1- //go:build cgo
2- // +build cgo
3-
41package native
52
6- /*
7- #include <stdlib.h>
8- #include <stdint.h>
9- typedef struct kclvm_service kclvm_service;
10- */
11- import "C"
123import (
134 "bytes"
145 "errors"
156 "runtime"
167 "strings"
8+ "sync"
179 "unsafe"
1810
11+ "github.com/ebitengine/purego"
1912 "google.golang.org/protobuf/proto"
2013 "google.golang.org/protobuf/reflect/protoreflect"
2114 "kcl-lang.io/lib/go/api"
2215 "kcl-lang.io/lib/go/plugin"
2316)
2417
18+ var libInit sync.Once
19+
20+ var (
21+ lib uintptr
22+ serviceNew func (uint64 ) uintptr
23+ serviceDelete func (uintptr )
24+ serviceCall func (uintptr , string , string , uint , * uint ) uintptr
25+ freeString func (uintptr )
26+ )
27+
2528type validator interface {
2629 Validate () error
2730}
2831
2932type NativeServiceClient struct {
30- client * C.kclvm_service
33+ svc uintptr
34+ }
35+
36+ func initLib () {
37+ libInit .Do (func () {
38+ lib , _ = loadServiceNativeLib ()
39+ purego .RegisterLibFunc (& serviceNew , lib , "kclvm_service_new" )
40+ purego .RegisterLibFunc (& serviceDelete , lib , "kclvm_service_delete" )
41+ purego .RegisterLibFunc (& serviceCall , lib , "kclvm_service_call_with_length" )
42+ purego .RegisterLibFunc (& freeString , lib , "kclvm_service_free_string" )
43+ })
3144}
3245
3346func NewNativeServiceClient () api.ServiceClient {
3447 return NewNativeServiceClientWithPluginAgent (plugin .GetInvokeJsonProxyPtr ())
3548}
3649
3750func NewNativeServiceClientWithPluginAgent (pluginAgent uint64 ) * NativeServiceClient {
51+ initLib ()
3852 c := new (NativeServiceClient )
39- c .client = NewKclvmService ( C . uint64_t ( pluginAgent ) )
53+ c .svc = serviceNew ( pluginAgent )
4054 runtime .SetFinalizer (c , func (x * NativeServiceClient ) {
41- DeleteKclvmService (x .client )
42- x . client = nil
55+ serviceDelete (x .svc )
56+ closeLibrary ( lib )
4357 })
4458 return c
4559}
@@ -68,20 +82,10 @@ func cApiCall[I interface {
6882 if err != nil {
6983 return nil , err
7084 }
85+ var cOutSize uint
86+ cOut := serviceCall (c .svc , callName , string (inBytes ), uint (len (inBytes )), & cOutSize )
7187
72- cCallName := C .CString (callName )
73-
74- defer C .free (unsafe .Pointer (cCallName ))
75-
76- cIn := C .CString (string (inBytes ))
77-
78- defer C .free (unsafe .Pointer (cIn ))
79-
80- cOut , cOutSize := KclvmServiceCall (c .client , cCallName , cIn , C .size_t (len (inBytes )))
81-
82- defer KclvmServiceFreeString (cOut )
83-
84- msg := C .GoBytes (unsafe .Pointer (cOut ), C .int (cOutSize ))
88+ msg := GoByte (cOut , cOutSize )
8589
8690 if bytes .HasPrefix (msg , []byte ("ERROR:" )) {
8791 return nil , errors .New (strings .TrimPrefix (string (msg ), "ERROR:" ))
@@ -96,6 +100,16 @@ func cApiCall[I interface {
96100 return out , nil
97101}
98102
103+ // GoByte copies a null-terminated char* to a Go string.
104+ func GoByte (c uintptr , length uint ) []byte {
105+ // We take the address and then dereference it to trick go vet from creating a possible misuse of unsafe.Pointer
106+ ptr := * (* unsafe .Pointer )(unsafe .Pointer (& c ))
107+ if ptr == nil {
108+ return []byte {}
109+ }
110+ return unsafe .Slice ((* byte )(ptr ), length )
111+ }
112+
99113func (c * NativeServiceClient ) Ping (in * api.Ping_Args ) (* api.Ping_Result , error ) {
100114 return cApiCall [* api.Ping_Args , * api.Ping_Result ](c , "KclvmService.Ping" , in )
101115}
0 commit comments