This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new ff2a6467b refactor(bindings/go): Restructure FFI system with type-safe 
wrapper (#6268)
ff2a6467b is described below

commit ff2a6467b90a52e951ffdff74592d39d5f685020
Author: Hanchin Hsieh <[email protected]>
AuthorDate: Tue Jun 10 22:36:26 2025 +0800

    refactor(bindings/go): Restructure FFI system with type-safe wrapper (#6268)
---
 bindings/go/Makefile         | 118 +++++++++++++++++++++++++++++++
 bindings/go/README.md        |  67 ++----------------
 bindings/go/delete.go        |  13 ++--
 bindings/go/error.go         |  13 ++--
 bindings/go/ffi.go           | 160 +++++++++++++++----------------------------
 bindings/go/lister.go        |  79 +++++++--------------
 bindings/go/metadata.go      |  65 ++++++------------
 bindings/go/opendal.go       |  14 ++--
 bindings/go/operator.go      |  88 ++++++++----------------
 bindings/go/operator_info.go |  92 ++++++++-----------------
 bindings/go/reader.go        |  67 ++++++------------
 bindings/go/stat.go          |  26 +++----
 bindings/go/write.go         |  87 ++++++++---------------
 13 files changed, 349 insertions(+), 540 deletions(-)

diff --git a/bindings/go/Makefile b/bindings/go/Makefile
new file mode 100644
index 000000000..861b42661
--- /dev/null
+++ b/bindings/go/Makefile
@@ -0,0 +1,118 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+.PHONY: tests clean help check-env generate-services run-tests
+
+# Detect current OS and architecture
+OS := $(shell uname -s | tr '[:upper:]' '[:lower:]')
+ARCH := $(shell uname -m)
+
+# Normalize architecture names
+ifeq ($(ARCH),x86_64)
+    ARCH := amd64
+endif
+ifeq ($(ARCH),aarch64)
+    ARCH := arm64
+endif
+
+# Define file extensions for different OS
+ifeq ($(OS),linux)
+    LIB_EXT := so
+endif
+ifeq ($(OS),darwin)
+    LIB_EXT := dylib
+endif
+ifeq ($(findstring mingw,$(OS)),mingw)
+    OS := windows
+    LIB_EXT := dll
+endif
+ifeq ($(findstring msys,$(OS)),msys)
+    OS := windows
+    LIB_EXT := dll
+endif
+
+# Check if OPENDAL_TEST environment variable is set
+check-env:
+       @if [ -z "$(OPENDAL_TEST)" ]; then \
+               echo "Error: OPENDAL_TEST environment variable is not set"; \
+               echo "Please set OPENDAL_TEST to the service you want to test 
(e.g., fs, s3, etc.)"; \
+               exit 1; \
+       fi
+
+# Other variables
+SERVICE := $(OPENDAL_TEST)
+GITHUB_WORKSPACE := $(PWD)/opendal-go-services
+VERSION := latest
+MATRIX := '{"build": [{"target":"$(OS)", "goos":"$(OS)", "goarch": 
"$(ARCH)"}], "service": ["$(SERVICE)"]}'
+
+# Define library file paths
+C_LIB_FILE := opendal/bindings/c/target/debug/libopendal_c.$(LIB_EXT)
+SERVICE_PKG := $(shell echo $(SERVICE) | tr '-' '_')
+FINAL_LIB_FILE := 
$(GITHUB_WORKSPACE)/$(SERVICE_PKG)/libopendal_c.$(OS).$(ARCH).$(LIB_EXT).zst
+
+# Build C library only when source files change
+$(C_LIB_FILE): opendal/bindings/c/src/*.rs opendal/bindings/c/Cargo.toml
+       @echo "Building C library for service: $(SERVICE)"
+       cd opendal/bindings/c && cargo build --features 
"opendal/services-$(SERVICE)"
+
+# Create compressed library and generate services in one step
+# This target depends on the final library file location, not intermediate
+$(FINAL_LIB_FILE): $(C_LIB_FILE)
+       @echo "Compressing library and generating services for $(SERVICE) on 
$(OS)..."
+       @mkdir -p $(GITHUB_WORKSPACE)/libopendal_c_$(VERSION)_$(SERVICE)_$(OS)
+       zstd -19 $< -o 
$(GITHUB_WORKSPACE)/libopendal_c_$(VERSION)_$(SERVICE)_$(OS)/libopendal_c.$(OS).$(LIB_EXT).zst
+       @echo "Generating Go services..."
+       cd opendal-go-services/internal/generate && \
+       GITHUB_WORKSPACE="$(GITHUB_WORKSPACE)" \
+       VERSION="$(VERSION)" \
+       MATRIX=$(MATRIX) \
+       go run generate.go
+       @echo "Cleaning up intermediate directory..."
+       @rm -rf $(GITHUB_WORKSPACE)/libopendal_c_$(VERSION)_$(SERVICE)_$(OS)
+
+# Generate services target now just depends on the final file
+generate-services: $(FINAL_LIB_FILE)
+
+# Run tests
+run-tests: generate-services
+       @echo "Running behavior tests..."
+       go test ./opendal/bindings/go/tests/behavior_tests -v -run TestBehavior
+
+# Main target: run all tests
+tests: check-env run-tests
+       @echo "All tests completed successfully!"
+
+# Clean all generated files
+clean:
+       @echo "Cleaning all generated files..."
+       rm -rf opendal/bindings/c/target
+       rm -rf opendal-go-services/*/libopendal_c.*
+       rm -rf opendal-go-services/libopendal_c_*
+
+# Help target
+help:
+       @echo "Available targets:"
+       @echo "  tests        - Run all tests (requires OPENDAL_TEST 
environment variable)"
+       @echo "  clean        - Clean all generated files"
+       @echo "  help         - Show this help message"
+       @echo ""
+       @echo "Environment variables:"
+       @echo "  OPENDAL_TEST - Required. The service to test (e.g., fs, s3, 
gcs, etc.)"
+       @echo ""
+       @echo "Example usage:"
+       @echo "  OPENDAL_TEST=fs make tests"
+
diff --git a/bindings/go/README.md b/bindings/go/README.md
index bc00d4246..265c9b170 100644
--- a/bindings/go/README.md
+++ b/bindings/go/README.md
@@ -222,7 +222,7 @@ We use `go workspace` to manage and build the dependencies. 
To set up the worksp
 
 <details>
   <summary>
-  For Linux
+  For Linux and macOS
   </summary>
 
 <br/>
@@ -240,65 +240,7 @@ go work use ./opendal/bindings/go/tests/behavior_tests
 go work use ./opendal-go-services/fs
 go work use ./opendal-go-services/memory
 
-cat <<EOF > ./make_test.sh
-#!/bin/bash
-
-# Check if OPENDAL_TEST is set
-if [ -z "\$OPENDAL_TEST" ]; then
-    echo "Error: OPENDAL_TEST environment variable is not set"
-    echo "Please set OPENDAL_TEST to specify which backend to test (e.g., fs 
or memory)"
-    exit 1
-fi
-
-# Specify the backend to test
-export SERVICE="\$OPENDAL_TEST"
-
-# Get architecture
-architecture=\$(uname -m)
-if [ "\$architecture" = "x86_64" ]; then
-    ARCH="amd64"
-    GOARCH="amd64"
-elif [ "\$architecture" = "aarch64" ] || [ "\$architecture" = "arm64" ]; then
-    ARCH="arm64"
-    GOARCH="arm64"
-else
-    ARCH="unknown"
-fi
-
-# Build opendal
-cd opendal/bindings/c
-cargo build
-cd -
-
-# Set environment variables
-export GITHUB_WORKSPACE="\$PWD/opendal-go-services"
-export VERSION="latest"
-export TARGET="linux"
-export DIR="\$GITHUB_WORKSPACE/libopendal_c_\${VERSION}_\${SERVICE}_\${TARGET}"
-
-# Create directory if not exists
-mkdir -p "\$DIR"
-
-export OUTPUT="\$DIR/libopendal_c.\$TARGET.so.zst"
-# Compress with zstd
-zstd -19 opendal/bindings/c/target/debug/libopendal_c.so -o \$OUTPUT
-
-# Set environment variables for test
-export MATRIX='{"build": [{"target":"linux", "goos":"linux", "goarch": 
"'\$GOARCH'"}], "service": ["fs"]}'
-
-# Generate code
-cd opendal-go-services/internal/generate
-go run generate.go
-cd -
-
-# Delete unnecessary files
-rm -rf \$DIR
-
-# Run tests
-go test ./opendal/bindings/go/tests/behavior_tests -v -run TestBehavior
-EOF
-
-chmod +x ./make_test.sh
+cp opendal/bindings/go/Makefile .
 
 cd -
 ```
@@ -312,8 +254,7 @@ cd opendal_workspace
 export OPENDAL_TEST=fs
 export OPENDAL_FS_ROOT=/tmp/opendal
 
-# build the C binding and run the tests
-./make_test.sh
+make tests
 
 cd -
 ```
@@ -342,7 +283,7 @@ go work use ./opendal-go-services/fs
 go work use ./opendal-go-services/memory
 
 @'
-# Check if OPENDAL_TEST is set\;if (-not $env:OPENDAL_TEST) {\;    Write-Error 
"OPENDAL_TEST environment variable is not set"\;    Write-Host "Please set 
OPENDAL_TEST to specify which backend to test (e.g., fs or memory)"\;    exit 
1\;}\;# Specify the backend to test\;Set-Item -Path Env:SERVICE -Value 
"$env:OPENDAL_TEST"\;# Get architecture\;$architecture = (Get-WmiObject 
Win32_OperatingSystem).OSArchitecture\;\;if ($architecture -like "*64*") {\;    
$ARCH = "x86_64"\;} else {\;    $ARCH [...]
+# Check if OPENDAL_TEST is set\;if (-not $env:OPENDAL_TEST) {\;    Write-Error 
"OPENDAL_TEST environment variable is not set"\;    Write-Host "Please set 
OPENDAL_TEST to specify which backend to test (e.g., fs or memory)"\;    exit 
1\;}\;# Specify the backend to test\;Set-Item -Path Env:SERVICE -Value 
"$env:OPENDAL_TEST"\;# Get architecture\;$architecture = (Get-WmiObject 
Win32_OperatingSystem).OSArchitecture\;\;if ($architecture -like "*64*") {\;    
$ARCH = "x86_64"\;} else {\;    $ARCH [...]
 '@ -replace "\\;","`n" | Out-File -FilePath "MakeTest.ps1" -Encoding UTF8
 
 Pop-Location
diff --git a/bindings/go/delete.go b/bindings/go/delete.go
index 31903383c..f7fd9b6f0 100644
--- a/bindings/go/delete.go
+++ b/bindings/go/delete.go
@@ -40,19 +40,14 @@ import (
 //
 // Use with caution as this operation is irreversible.
 func (op *Operator) Delete(path string) error {
-       delete := getFFI[operatorDelete](op.ctx, symOperatorDelete)
-       return delete(op.inner, path)
+       return ffiOperatorDelete.symbol(op.ctx)(op.inner, path)
 }
 
-type operatorDelete func(op *opendalOperator, path string) error
-
-const symOperatorDelete = "opendal_operator_delete"
-
-var withOperatorDelete = withFFI(ffiOpts{
-       sym:    symOperatorDelete,
+var ffiOperatorDelete = newFFI(ffiOpts{
+       sym:    "opendal_operator_delete",
        rType:  &ffi.TypePointer,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorDelete {
+}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator, path 
string) error {
        return func(op *opendalOperator, path string) error {
                bytePath, err := BytePtrFromString(path)
                if err != nil {
diff --git a/bindings/go/error.go b/bindings/go/error.go
index 9e7c2de9c..899f75661 100644
--- a/bindings/go/error.go
+++ b/bindings/go/error.go
@@ -73,8 +73,7 @@ func parseError(ctx context.Context, err *opendalError) error 
{
        if err == nil {
                return nil
        }
-       free := getFFI[errorFree](ctx, symErrorFree)
-       defer free(err)
+       defer ffiErrorFree.symbol(ctx)(err)
        return &Error{
                code:    ErrorCode(err.code),
                message: string(parseBytes(err.message)),
@@ -98,15 +97,11 @@ func (e *Error) Message() string {
        return e.message
 }
 
-type errorFree func(e *opendalError)
-
-const symErrorFree = "opendal_error_free"
-
-var withErrorFree = withFFI(ffiOpts{
-       sym:    symErrorFree,
+var ffiErrorFree = newFFI(ffiOpts{
+       sym:    "opendal_error_free",
        rType:  &ffi.TypeVoid,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(_ context.Context, ffiCall ffiCall) errorFree {
+}, func(_ context.Context, ffiCall ffiCall) func(e *opendalError) {
        return func(e *opendalError) {
                ffiCall(
                        nil,
diff --git a/bindings/go/ffi.go b/bindings/go/ffi.go
index df1e6e3f2..376663600 100644
--- a/bindings/go/ffi.go
+++ b/bindings/go/ffi.go
@@ -27,37 +27,6 @@ import (
        "github.com/jupiterrider/ffi"
 )
 
-func contextWithFFIs(path string) (ctx context.Context, cancel 
context.CancelFunc, err error) {
-       libopendal, err := LoadLibrary(path)
-       if err != nil {
-               return
-       }
-       ctx = context.Background()
-       for _, withFFI := range withFFIs {
-               ctx, err = withFFI(ctx, libopendal)
-               if err != nil {
-                       return
-               }
-       }
-       cancel = func() {
-               _ = FreeLibrary(libopendal)
-       }
-       return
-}
-
-type contextWithFFI func(ctx context.Context, libopendal uintptr) 
(context.Context, error)
-
-func getFFI[T any](ctx context.Context, key string) T {
-       ctxKey := contextKey(key)
-       return ctx.Value(ctxKey).(T)
-}
-
-type contextKey string
-
-func (k contextKey) String() string {
-       return string(k)
-}
-
 type ffiOpts struct {
        sym    contextKey
        rType  *ffi.Type
@@ -66,85 +35,70 @@ type ffiOpts struct {
 
 type ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)
 
-func withFFI[T any](
-       opts ffiOpts,
-       withFunc func(
-               ctx context.Context,
-               ffiCall ffiCall,
-       ) T,
-) func(ctx context.Context, libopendal uintptr) (context.Context, error) {
-       return func(ctx context.Context, libopendal uintptr) (context.Context, 
error) {
-               var cif ffi.Cif
-               if status := ffi.PrepCif(
-                       &cif,
-                       ffi.DefaultAbi,
-                       uint32(len(opts.aTypes)),
-                       opts.rType,
-                       opts.aTypes...,
-               ); status != ffi.OK {
-                       return nil, errors.New(status.String())
-               }
-               fn, err := GetProcAddress(libopendal, opts.sym.String())
-               if err != nil {
-                       return nil, err
-               }
-               return context.WithValue(ctx, opts.sym,
-                       withFunc(ctx, func(rValue unsafe.Pointer, aValues 
...unsafe.Pointer) {
-                               ffi.Call(&cif, fn, rValue, aValues...)
-                       }),
-               ), nil
-       }
-}
+type contextKey string
 
-var withFFIs = []contextWithFFI{
-       // free must be on top
-       withBytesFree,
-       withErrorFree,
+func (c contextKey) String() string {
+       return string(c)
+}
 
-       withOperatorOptionsNew,
-       withOperatorOptionsSet,
-       withOperatorOptionsFree,
+type contextWithFFI func(ctx context.Context, lib uintptr) (context.Context, 
error)
 
-       withOperatorNew,
-       withOperatorFree,
+type FFI[T any] struct {
+       opts     ffiOpts
+       withFunc func(ctx context.Context, ffiCall ffiCall) T
+}
 
-       withOperatorInfoNew,
-       withOperatorInfoGetFullCapability,
-       withOperatorInfoGetNativeCapability,
-       withOperatorInfoGetScheme,
-       withOperatorInfoGetRoot,
-       withOperatorInfoGetName,
-       withOperatorInfoFree,
+func newFFI[T any](opts ffiOpts, withFunc func(ctx context.Context, ffiCall 
ffiCall) T) *FFI[T] {
+       ffi := &FFI[T]{
+               opts:     opts,
+               withFunc: withFunc,
+       }
+       withFFIs = append(withFFIs, ffi.withFFI)
+       return ffi
+}
 
-       withOperatorCreateDir,
-       withOperatorRead,
-       withOperatorWrite,
-       withOperatorDelete,
-       withOperatorStat,
-       withOperatorIsExists,
-       withOperatorCopy,
-       withOperatorRename,
+func (f *FFI[T]) symbol(ctx context.Context) T {
+       return ctx.Value(f.opts.sym).(T)
+}
 
-       withMetaContentLength,
-       withMetaIsFile,
-       withMetaIsDir,
-       withMetaLastModified,
-       withMetaFree,
+func (f *FFI[T]) withFFI(ctx context.Context, lib uintptr) (context.Context, 
error) {
+       var cif ffi.Cif
+       if status := ffi.PrepCif(
+               &cif,
+               ffi.DefaultAbi,
+               uint32(len(f.opts.aTypes)),
+               f.opts.rType,
+               f.opts.aTypes...,
+       ); status != ffi.OK {
+               return nil, errors.New(status.String())
+       }
+       fn, err := GetProcAddress(lib, f.opts.sym.String())
+       if err != nil {
+               return nil, err
+       }
+       val := f.withFunc(ctx, func(rValue unsafe.Pointer, aValues 
...unsafe.Pointer) {
+               ffi.Call(&cif, fn, rValue, aValues...)
+       })
+       return context.WithValue(ctx, f.opts.sym, val), nil
+}
 
-       withOperatorList,
-       withListerNext,
-       withListerFree,
-       withEntryName,
-       withEntryPath,
-       withEntryFree,
+var withFFIs []contextWithFFI
 
-       withOperatorReader,
-       withReaderRead,
-       withReaderSeek,
-       withReaderFree,
+func newContext(path string) (ctx context.Context, cancel context.CancelFunc, 
err error) {
+       lib, err := LoadLibrary(path)
+       if err != nil {
+               return
+       }
+       ctx = context.Background()
+       for _, withFFI := range withFFIs {
+               ctx, err = withFFI(ctx, lib)
+               if err != nil {
+                       return
+               }
+       }
+       cancel = func() {
+               _ = FreeLibrary(lib)
+       }
 
-       withOperatorWriter,
-       withWriterWrite,
-       withWriterFree,
-       withWriterClose,
+       return
 }
diff --git a/bindings/go/lister.go b/bindings/go/lister.go
index 5ec7de0d5..0d24fbc77 100644
--- a/bindings/go/lister.go
+++ b/bindings/go/lister.go
@@ -121,8 +121,7 @@ func (op *Operator) Check() (err error) {
 // Note: Always check lister.Err() after the loop to catch any errors that
 // occurred during iteration.
 func (op *Operator) List(path string) (*Lister, error) {
-       list := getFFI[operatorList](op.ctx, symOperatorList)
-       inner, err := list(op.inner, path)
+       inner, err := ffiOperatorList.symbol(op.ctx)(op.inner, path)
        if err != nil {
                return nil, err
        }
@@ -175,8 +174,7 @@ type Lister struct {
 // This method implements the io.Closer interface. It should be called when
 // the Lister is no longer needed to ensure proper resource cleanup.
 func (l *Lister) Close() error {
-       free := getFFI[listerFree](l.ctx, symListerFree)
-       free(l.inner)
+       ffiListerFree.symbol(l.ctx)(l.inner)
 
        return nil
 }
@@ -222,8 +220,7 @@ func (l *Lister) Error() error {
 //             fmt.Println(entry.Name())
 //     }
 func (l *Lister) Next() bool {
-       next := getFFI[listerNext](l.ctx, symListerNext)
-       inner, err := next(l.inner)
+       inner, err := ffiListerNext.symbol(l.ctx)(l.inner)
        if inner == nil || err != nil {
                l.err = err
                l.entry = nil
@@ -284,15 +281,11 @@ type Entry struct {
 }
 
 func newEntry(ctx context.Context, inner *opendalEntry) *Entry {
-       name := getFFI[entryName](ctx, symEntryName)
-       path := getFFI[entryPath](ctx, symEntryPath)
-       free := getFFI[entryFree](ctx, symEntryFree)
-
-       defer free(inner)
+       defer ffiEntryFree.symbol(ctx)(inner)
 
        return &Entry{
-               name: name(inner),
-               path: path(inner),
+               name: ffiEntryName.symbol(ctx)(inner),
+               path: ffiEntryPath.symbol(ctx)(inner),
        }
 }
 
@@ -306,15 +299,11 @@ func (e *Entry) Path() string {
        return e.path
 }
 
-const symOperatorList = "opendal_operator_list"
-
-type operatorList func(op *opendalOperator, path string) (*opendalLister, 
error)
-
-var withOperatorList = withFFI(ffiOpts{
-       sym:    symOperatorList,
+var ffiOperatorList = newFFI(ffiOpts{
+       sym:    "opendal_operator_list",
        rType:  &typeResultList,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorList {
+}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator, path 
string) (*opendalLister, error) {
        return func(op *opendalOperator, path string) (*opendalLister, error) {
                bytePath, err := BytePtrFromString(path)
                if err != nil {
@@ -333,15 +322,11 @@ var withOperatorList = withFFI(ffiOpts{
        }
 })
 
-const symListerFree = "opendal_lister_free"
-
-type listerFree func(l *opendalLister)
-
-var withListerFree = withFFI(ffiOpts{
-       sym:    symListerFree,
+var ffiListerFree = newFFI(ffiOpts{
+       sym:    "opendal_lister_free",
        rType:  &ffi.TypeVoid,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) listerFree {
+}, func(ctx context.Context, ffiCall ffiCall) func(l *opendalLister) {
        return func(l *opendalLister) {
                ffiCall(
                        nil,
@@ -350,15 +335,11 @@ var withListerFree = withFFI(ffiOpts{
        }
 })
 
-const symListerNext = "opendal_lister_next"
-
-type listerNext func(l *opendalLister) (*opendalEntry, error)
-
-var withListerNext = withFFI(ffiOpts{
-       sym:    symListerNext,
+var ffiListerNext = newFFI(ffiOpts{
+       sym:    "opendal_lister_next",
        rType:  &typeResultListerNext,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) listerNext {
+}, func(ctx context.Context, ffiCall ffiCall) func(l *opendalLister) 
(*opendalEntry, error) {
        return func(l *opendalLister) (*opendalEntry, error) {
                var result opendalResultListerNext
                ffiCall(
@@ -372,15 +353,11 @@ var withListerNext = withFFI(ffiOpts{
        }
 })
 
-const symEntryFree = "opendal_entry_free"
-
-type entryFree func(e *opendalEntry)
-
-var withEntryFree = withFFI(ffiOpts{
-       sym:    symEntryFree,
+var ffiEntryFree = newFFI(ffiOpts{
+       sym:    "opendal_entry_free",
        rType:  &ffi.TypePointer,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) entryFree {
+}, func(ctx context.Context, ffiCall ffiCall) func(e *opendalEntry) {
        return func(e *opendalEntry) {
                ffiCall(
                        nil,
@@ -389,15 +366,11 @@ var withEntryFree = withFFI(ffiOpts{
        }
 })
 
-const symEntryName = "opendal_entry_name"
-
-type entryName func(e *opendalEntry) string
-
-var withEntryName = withFFI(ffiOpts{
-       sym:    symEntryName,
+var ffiEntryName = newFFI(ffiOpts{
+       sym:    "opendal_entry_name",
        rType:  &ffi.TypePointer,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) entryName {
+}, func(ctx context.Context, ffiCall ffiCall) func(e *opendalEntry) string {
        return func(e *opendalEntry) string {
                var bytePtr *byte
                ffiCall(
@@ -408,15 +381,11 @@ var withEntryName = withFFI(ffiOpts{
        }
 })
 
-const symEntryPath = "opendal_entry_path"
-
-type entryPath func(e *opendalEntry) string
-
-var withEntryPath = withFFI(ffiOpts{
-       sym:    symEntryPath,
+var ffiEntryPath = newFFI(ffiOpts{
+       sym:    "opendal_entry_path",
        rType:  &ffi.TypePointer,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) entryPath {
+}, func(ctx context.Context, ffiCall ffiCall) func(e *opendalEntry) string {
        return func(e *opendalEntry) string {
                var bytePtr *byte
                ffiCall(
diff --git a/bindings/go/metadata.go b/bindings/go/metadata.go
index ccdfebd0f..daff59d63 100644
--- a/bindings/go/metadata.go
+++ b/bindings/go/metadata.go
@@ -39,24 +39,19 @@ type Metadata struct {
 }
 
 func newMetadata(ctx context.Context, inner *opendalMetadata) *Metadata {
-       getLength := getFFI[metaContentLength](ctx, symMetadataContentLength)
-       isFile := getFFI[metaIsFile](ctx, symMetadataIsFile)
-       isDir := getFFI[metaIsDir](ctx, symMetadataIsDir)
-       getLastModified := getFFI[metaLastModified](ctx, 
symMetadataLastModified)
 
        var lastModified time.Time
-       ms := getLastModified(inner)
+       ms := ffiMetaLastModified.symbol(ctx)(inner)
        if ms != -1 {
                lastModified = time.UnixMilli(ms)
        }
 
-       free := getFFI[metaFree](ctx, symMetadataFree)
-       defer free(inner)
+       defer ffiMetadataFree.symbol(ctx)(inner)
 
        return &Metadata{
-               contentLength: getLength(inner),
-               isFile:        isFile(inner),
-               isDir:         isDir(inner),
+               contentLength: ffiMetaContentLength.symbol(ctx)(inner),
+               isFile:        ffiMetaIsFile.symbol(ctx)(inner),
+               isDir:         ffiMetaIsDir.symbol(ctx)(inner),
                lastModified:  lastModified,
        }
 }
@@ -85,15 +80,11 @@ func (m *Metadata) LastModified() time.Time {
        return m.lastModified
 }
 
-type metaContentLength func(m *opendalMetadata) uint64
-
-const symMetadataContentLength = "opendal_metadata_content_length"
-
-var withMetaContentLength = withFFI(ffiOpts{
-       sym:    symMetadataContentLength,
+var ffiMetaContentLength = newFFI(ffiOpts{
+       sym:    "opendal_metadata_content_length",
        rType:  &ffi.TypeUint64,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) metaContentLength {
+}, func(ctx context.Context, ffiCall ffiCall) func(m *opendalMetadata) uint64 {
        return func(m *opendalMetadata) uint64 {
                var length uint64
                ffiCall(
@@ -104,15 +95,11 @@ var withMetaContentLength = withFFI(ffiOpts{
        }
 })
 
-type metaIsFile func(m *opendalMetadata) bool
-
-const symMetadataIsFile = "opendal_metadata_is_file"
-
-var withMetaIsFile = withFFI(ffiOpts{
-       sym:    symMetadataIsFile,
+var ffiMetaIsFile = newFFI(ffiOpts{
+       sym:    "opendal_metadata_is_file",
        rType:  &ffi.TypeUint8,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) metaIsFile {
+}, func(ctx context.Context, ffiCall ffiCall) func(m *opendalMetadata) bool {
        return func(m *opendalMetadata) bool {
                var result uint8
                ffiCall(
@@ -123,15 +110,11 @@ var withMetaIsFile = withFFI(ffiOpts{
        }
 })
 
-type metaIsDir func(m *opendalMetadata) bool
-
-const symMetadataIsDir = "opendal_metadata_is_dir"
-
-var withMetaIsDir = withFFI(ffiOpts{
-       sym:    symMetadataIsDir,
+var ffiMetaIsDir = newFFI(ffiOpts{
+       sym:    "opendal_metadata_is_dir",
        rType:  &ffi.TypeUint8,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) metaIsDir {
+}, func(ctx context.Context, ffiCall ffiCall) func(m *opendalMetadata) bool {
        return func(m *opendalMetadata) bool {
                var result uint8
                ffiCall(
@@ -142,15 +125,11 @@ var withMetaIsDir = withFFI(ffiOpts{
        }
 })
 
-type metaLastModified func(m *opendalMetadata) int64
-
-const symMetadataLastModified = "opendal_metadata_last_modified_ms"
-
-var withMetaLastModified = withFFI(ffiOpts{
-       sym:    symMetadataLastModified,
+var ffiMetaLastModified = newFFI(ffiOpts{
+       sym:    "opendal_metadata_last_modified_ms",
        rType:  &ffi.TypeSint64,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) metaLastModified {
+}, func(ctx context.Context, ffiCall ffiCall) func(m *opendalMetadata) int64 {
        return func(m *opendalMetadata) int64 {
                var result int64
                ffiCall(
@@ -161,15 +140,11 @@ var withMetaLastModified = withFFI(ffiOpts{
        }
 })
 
-type metaFree func(m *opendalMetadata)
-
-const symMetadataFree = "opendal_metadata_free"
-
-var withMetaFree = withFFI(ffiOpts{
-       sym:    symMetadataFree,
+var ffiMetadataFree = newFFI(ffiOpts{
+       sym:    "opendal_metadata_free",
        rType:  &ffi.TypeVoid,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) metaFree {
+}, func(ctx context.Context, ffiCall ffiCall) func(m *opendalMetadata) {
        return func(m *opendalMetadata) {
                ffiCall(
                        nil,
diff --git a/bindings/go/opendal.go b/bindings/go/opendal.go
index 86b80b853..ab2777878 100644
--- a/bindings/go/opendal.go
+++ b/bindings/go/opendal.go
@@ -171,16 +171,15 @@ func NewOperator(scheme Scheme, opts OperatorOptions) (op 
*Operator, err error)
                return
        }
 
-       ctx, cancel, err := contextWithFFIs(scheme.Path())
+       ctx, cancel, err := newContext(scheme.Path())
        if err != nil {
                return
        }
 
-       options := getFFI[operatorOptionsNew](ctx, symOperatorOptionsNew)()
-       setOptions := getFFI[operatorOptionsSet](ctx, symOperatorOptionSet)
-       optionsFree := getFFI[operatorOptionsFree](ctx, symOperatorOptionsFree)
+       options := ffiOperatorOptionsNew.symbol(ctx)()
+       setOptions := ffiOperatorOptionsSet.symbol(ctx)
 
-       defer optionsFree(options)
+       defer ffiOperatorOptionsFree.symbol(ctx)(options)
 
        for key, value := range opts {
                err = setOptions(options, key, value)
@@ -189,7 +188,7 @@ func NewOperator(scheme Scheme, opts OperatorOptions) (op 
*Operator, err error)
                }
        }
 
-       inner, err := getFFI[operatorNew](ctx, symOperatorNew)(scheme, options)
+       inner, err := ffiOperatorNew.symbol(ctx)(scheme, options)
        if err != nil {
                cancel()
                return
@@ -211,7 +210,6 @@ func NewOperator(scheme Scheme, opts OperatorOptions) (op 
*Operator, err error)
 //
 // Note: It's recommended to use defer op.Close() immediately after creating 
an Operator.
 func (op *Operator) Close() {
-       free := getFFI[operatorFree]
-       free(op.ctx, symOperatorFree)(op.inner)
+       ffiOperatorFree.symbol(op.ctx)(op.inner)
        op.cancel()
 }
diff --git a/bindings/go/operator.go b/bindings/go/operator.go
index c3ba125f7..46a996033 100644
--- a/bindings/go/operator.go
+++ b/bindings/go/operator.go
@@ -59,8 +59,7 @@ import (
 //
 // Note: This example assumes proper error handling and import statements.
 func (op *Operator) Copy(src, dest string) error {
-       cp := getFFI[operatorCopy](op.ctx, symOperatorCopy)
-       return cp(op.inner, src, dest)
+       return ffiOperatorCopy.symbol(op.ctx)(op.inner, src, dest)
 }
 
 // Rename changes the name or location of a file from the source path to the 
destination path.
@@ -95,24 +94,19 @@ func (op *Operator) Copy(src, dest string) error {
 //
 // Note: This example assumes proper error handling and import statements.
 func (op *Operator) Rename(src, dest string) error {
-       rename := getFFI[operatorRename](op.ctx, symOperatorRename)
-       return rename(op.inner, src, dest)
+       return ffiOperatorRename.symbol(op.ctx)(op.inner, src, dest)
 }
 
-const symOperatorNew = "opendal_operator_new"
-
-type operatorNew func(scheme Scheme, opts *operatorOptions) (op 
*opendalOperator, err error)
-
-var withOperatorNew = withFFI(ffiOpts{
-       sym:    symOperatorNew,
+var ffiOperatorNew = newFFI(ffiOpts{
+       sym:    "opendal_operator_new",
        rType:  &typeResultOperatorNew,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorNew {
+}, func(ctx context.Context, ffiCall ffiCall) func(scheme Scheme, opts 
*operatorOptions) (op *opendalOperator, err error) {
        return func(scheme Scheme, opts *operatorOptions) (op *opendalOperator, 
err error) {
                var byteName *byte
                byteName, err = BytePtrFromString(scheme.Name())
                if err != nil {
-                       return
+                       return nil, err
                }
                var result resultOperatorNew
                ffiCall(
@@ -129,15 +123,11 @@ var withOperatorNew = withFFI(ffiOpts{
        }
 })
 
-const symOperatorFree = "opendal_operator_free"
-
-type operatorFree func(op *opendalOperator)
-
-var withOperatorFree = withFFI(ffiOpts{
-       sym:    symOperatorFree,
+var ffiOperatorFree = newFFI(ffiOpts{
+       sym:    "opendal_operator_free",
        rType:  &ffi.TypeVoid,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(_ context.Context, ffiCall ffiCall) operatorFree {
+}, func(_ context.Context, ffiCall ffiCall) func(op *opendalOperator) {
        return func(op *opendalOperator) {
                ffiCall(
                        nil,
@@ -148,29 +138,21 @@ var withOperatorFree = withFFI(ffiOpts{
 
 type operatorOptions struct{}
 
-const symOperatorOptionsNew = "opendal_operator_options_new"
-
-type operatorOptionsNew func() (opts *operatorOptions)
-
-var withOperatorOptionsNew = withFFI(ffiOpts{
-       sym:   symOperatorOptionsNew,
+var ffiOperatorOptionsNew = newFFI(ffiOpts{
+       sym:   "opendal_operator_options_new",
        rType: &ffi.TypePointer,
-}, func(_ context.Context, ffiCall ffiCall) operatorOptionsNew {
+}, func(_ context.Context, ffiCall ffiCall) func() (opts *operatorOptions) {
        return func() (opts *operatorOptions) {
                ffiCall(unsafe.Pointer(&opts))
                return
        }
 })
 
-const symOperatorOptionSet = "opendal_operator_options_set"
-
-type operatorOptionsSet func(opts *operatorOptions, key, value string) error
-
-var withOperatorOptionsSet = withFFI(ffiOpts{
-       sym:    symOperatorOptionSet,
+var ffiOperatorOptionsSet = newFFI(ffiOpts{
+       sym:    "opendal_operator_options_set",
        rType:  &ffi.TypeVoid,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer, 
&ffi.TypePointer},
-}, func(_ context.Context, ffiCall ffiCall) operatorOptionsSet {
+}, func(_ context.Context, ffiCall ffiCall) func(opts *operatorOptions, key, 
value string) (err error) {
        return func(opts *operatorOptions, key, value string) (err error) {
                var (
                        byteKey   *byte
@@ -194,15 +176,11 @@ var withOperatorOptionsSet = withFFI(ffiOpts{
        }
 })
 
-const symOperatorOptionsFree = "opendal_operator_options_free"
-
-type operatorOptionsFree func(opts *operatorOptions)
-
-var withOperatorOptionsFree = withFFI(ffiOpts{
-       sym:    symOperatorOptionsFree,
+var ffiOperatorOptionsFree = newFFI(ffiOpts{
+       sym:    "opendal_operator_options_free",
        rType:  &ffi.TypeVoid,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(_ context.Context, ffiCall ffiCall) operatorOptionsFree {
+}, func(_ context.Context, ffiCall ffiCall) func(opts *operatorOptions) {
        return func(opts *operatorOptions) {
                ffiCall(
                        nil,
@@ -211,15 +189,11 @@ var withOperatorOptionsFree = withFFI(ffiOpts{
        }
 })
 
-const symOperatorCopy = "opendal_operator_copy"
-
-type operatorCopy func(op *opendalOperator, src, dest string) (err error)
-
-var withOperatorCopy = withFFI(ffiOpts{
-       sym:    symOperatorCopy,
+var ffiOperatorCopy = newFFI(ffiOpts{
+       sym:    "opendal_operator_copy",
        rType:  &ffi.TypePointer,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer, 
&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorCopy {
+}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator, src, 
dest string) (err error) {
        return func(op *opendalOperator, src, dest string) (err error) {
                var (
                        byteSrc  *byte
@@ -244,15 +218,11 @@ var withOperatorCopy = withFFI(ffiOpts{
        }
 })
 
-const symOperatorRename = "opendal_operator_rename"
-
-type operatorRename func(op *opendalOperator, src, dest string) (err error)
-
-var withOperatorRename = withFFI(ffiOpts{
-       sym:    symOperatorRename,
+var ffiOperatorRename = newFFI(ffiOpts{
+       sym:    "opendal_operator_rename",
        rType:  &ffi.TypePointer,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer, 
&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorRename {
+}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator, src, 
dest string) (err error) {
        return func(op *opendalOperator, src, dest string) (err error) {
                var (
                        byteSrc  *byte
@@ -277,15 +247,11 @@ var withOperatorRename = withFFI(ffiOpts{
        }
 })
 
-const symBytesFree = "opendal_bytes_free"
-
-type bytesFree func(b *opendalBytes)
-
-var withBytesFree = withFFI(ffiOpts{
-       sym:    symBytesFree,
+var ffiBytesFree = newFFI(ffiOpts{
+       sym:    "opendal_bytes_free",
        rType:  &ffi.TypeVoid,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(_ context.Context, ffiCall ffiCall) bytesFree {
+}, func(ctx context.Context, ffiCall ffiCall) func(b *opendalBytes) {
        return func(b *opendalBytes) {
                ffiCall(
                        nil,
diff --git a/bindings/go/operator_info.go b/bindings/go/operator_info.go
index 89ff932ca..ca02ed2dd 100644
--- a/bindings/go/operator_info.go
+++ b/bindings/go/operator_info.go
@@ -34,23 +34,15 @@ import (
 // Returns:
 //   - *OperatorInfo: A pointer to an OperatorInfo struct containing the 
Operator's metadata.
 func (op *Operator) Info() *OperatorInfo {
-       newInfo := getFFI[operatorInfoNew](op.ctx, symOperatorInfoNew)
-       inner := newInfo(op.inner)
-       getFullCap := getFFI[operatorInfoGetFullCapability](op.ctx, 
symOperatorInfoGetFullCapability)
-       getNativeCap := getFFI[operatorInfoGetNativeCapability](op.ctx, 
symOperatorInfoGetNativeCapability)
-       getScheme := getFFI[operatorInfoGetScheme](op.ctx, 
symOperatorInfoGetScheme)
-       getRoot := getFFI[operatorInfoGetRoot](op.ctx, symOperatorInfoGetRoot)
-       getName := getFFI[operatorInfoGetName](op.ctx, symOperatorInfoGetName)
-
-       free := getFFI[operatorInfoFree](op.ctx, symOperatorInfoFree)
-       defer free(inner)
+       inner := ffiOperatorInfoNew.symbol(op.ctx)(op.inner)
+       defer ffiOperatorInfoFree.symbol(op.ctx)(inner)
 
        return &OperatorInfo{
-               scheme:    getScheme(inner),
-               root:      getRoot(inner),
-               name:      getName(inner),
-               fullCap:   &Capability{inner: getFullCap(inner)},
-               nativeCap: &Capability{inner: getNativeCap(inner)},
+               scheme:    ffiOperatorInfoGetScheme.symbol(op.ctx)(inner),
+               root:      ffiOperatorInfoGetRoot.symbol(op.ctx)(inner),
+               name:      ffiOperatorInfoGetName.symbol(op.ctx)(inner),
+               fullCap:   &Capability{inner: 
ffiOperatorInfoGetFullCapability.symbol(op.ctx)(inner)},
+               nativeCap: &Capability{inner: 
ffiOperatorInfoGetNativeCapability.symbol(op.ctx)(inner)},
        }
 }
 
@@ -235,15 +227,11 @@ func (c *Capability) Blocking() bool {
        return c.inner.blocking == 1
 }
 
-const symOperatorInfoNew = "opendal_operator_info_new"
-
-type operatorInfoNew func(op *opendalOperator) *opendalOperatorInfo
-
-var withOperatorInfoNew = withFFI(ffiOpts{
-       sym:    symOperatorInfoNew,
+var ffiOperatorInfoNew = newFFI(ffiOpts{
+       sym:    "opendal_operator_info_new",
        rType:  &ffi.TypePointer,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorInfoNew {
+}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator) 
*opendalOperatorInfo {
        return func(op *opendalOperator) *opendalOperatorInfo {
                var result *opendalOperatorInfo
                ffiCall(
@@ -254,15 +242,11 @@ var withOperatorInfoNew = withFFI(ffiOpts{
        }
 })
 
-const symOperatorInfoFree = "opendal_operator_info_free"
-
-type operatorInfoFree func(info *opendalOperatorInfo)
-
-var withOperatorInfoFree = withFFI(ffiOpts{
-       sym:    symOperatorInfoFree,
+var ffiOperatorInfoFree = newFFI(ffiOpts{
+       sym:    "opendal_operator_info_free",
        rType:  &ffi.TypeVoid,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorInfoFree {
+}, func(_ context.Context, ffiCall ffiCall) func(info *opendalOperatorInfo) {
        return func(info *opendalOperatorInfo) {
                ffiCall(
                        nil,
@@ -271,15 +255,11 @@ var withOperatorInfoFree = withFFI(ffiOpts{
        }
 })
 
-const symOperatorInfoGetFullCapability = 
"opendal_operator_info_get_full_capability"
-
-type operatorInfoGetFullCapability func(info *opendalOperatorInfo) 
*opendalCapability
-
-var withOperatorInfoGetFullCapability = withFFI(ffiOpts{
-       sym:    symOperatorInfoGetFullCapability,
+var ffiOperatorInfoGetFullCapability = newFFI(ffiOpts{
+       sym:    "opendal_operator_info_get_full_capability",
        rType:  &typeCapability,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorInfoGetFullCapability {
+}, func(ctx context.Context, ffiCall ffiCall) func(info *opendalOperatorInfo) 
*opendalCapability {
        return func(info *opendalOperatorInfo) *opendalCapability {
                var cap opendalCapability
                ffiCall(
@@ -290,15 +270,11 @@ var withOperatorInfoGetFullCapability = withFFI(ffiOpts{
        }
 })
 
-const symOperatorInfoGetNativeCapability = 
"opendal_operator_info_get_native_capability"
-
-type operatorInfoGetNativeCapability func(info *opendalOperatorInfo) 
*opendalCapability
-
-var withOperatorInfoGetNativeCapability = withFFI(ffiOpts{
-       sym:    symOperatorInfoGetNativeCapability,
+var ffiOperatorInfoGetNativeCapability = newFFI(ffiOpts{
+       sym:    "opendal_operator_info_get_native_capability",
        rType:  &typeCapability,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorInfoGetNativeCapability {
+}, func(ctx context.Context, ffiCall ffiCall) func(info *opendalOperatorInfo) 
*opendalCapability {
        return func(info *opendalOperatorInfo) *opendalCapability {
                var cap opendalCapability
                ffiCall(
@@ -309,15 +285,11 @@ var withOperatorInfoGetNativeCapability = withFFI(ffiOpts{
        }
 })
 
-const symOperatorInfoGetScheme = "opendal_operator_info_get_scheme"
-
-type operatorInfoGetScheme func(info *opendalOperatorInfo) string
-
-var withOperatorInfoGetScheme = withFFI(ffiOpts{
-       sym:    symOperatorInfoGetScheme,
+var ffiOperatorInfoGetScheme = newFFI(ffiOpts{
+       sym:    "opendal_operator_info_get_scheme",
        rType:  &ffi.TypePointer,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorInfoGetScheme {
+}, func(ctx context.Context, ffiCall ffiCall) func(info *opendalOperatorInfo) 
string {
        return func(info *opendalOperatorInfo) string {
                var bytePtr *byte
                ffiCall(
@@ -328,15 +300,11 @@ var withOperatorInfoGetScheme = withFFI(ffiOpts{
        }
 })
 
-const symOperatorInfoGetRoot = "opendal_operator_info_get_root"
-
-type operatorInfoGetRoot func(info *opendalOperatorInfo) string
-
-var withOperatorInfoGetRoot = withFFI(ffiOpts{
-       sym:    symOperatorInfoGetRoot,
+var ffiOperatorInfoGetRoot = newFFI(ffiOpts{
+       sym:    "opendal_operator_info_get_root",
        rType:  &ffi.TypePointer,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorInfoGetRoot {
+}, func(ctx context.Context, ffiCall ffiCall) func(info *opendalOperatorInfo) 
string {
        return func(info *opendalOperatorInfo) string {
                var bytePtr *byte
                ffiCall(
@@ -347,15 +315,11 @@ var withOperatorInfoGetRoot = withFFI(ffiOpts{
        }
 })
 
-const symOperatorInfoGetName = "opendal_operator_info_get_name"
-
-type operatorInfoGetName func(info *opendalOperatorInfo) string
-
-var withOperatorInfoGetName = withFFI(ffiOpts{
-       sym:    symOperatorInfoGetName,
+var ffiOperatorInfoGetName = newFFI(ffiOpts{
+       sym:    "opendal_operator_info_get_name",
        rType:  &ffi.TypePointer,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorInfoGetName {
+}, func(ctx context.Context, ffiCall ffiCall) func(info *opendalOperatorInfo) 
string {
        return func(info *opendalOperatorInfo) string {
                var bytePtr *byte
                ffiCall(
diff --git a/bindings/go/reader.go b/bindings/go/reader.go
index 3dde9ce68..346bce199 100644
--- a/bindings/go/reader.go
+++ b/bindings/go/reader.go
@@ -58,16 +58,14 @@ import (
 //
 // Note: This example assumes proper error handling and import statements.
 func (op *Operator) Read(path string) ([]byte, error) {
-       read := getFFI[operatorRead](op.ctx, symOperatorRead)
-       bytes, err := read(op.inner, path)
+       bytes, err := ffiOperatorRead.symbol(op.ctx)(op.inner, path)
        if err != nil {
                return nil, err
        }
 
        data := parseBytes(bytes)
        if len(data) > 0 {
-               free := getFFI[bytesFree](op.ctx, symBytesFree)
-               free(&bytes)
+               ffiBytesFree.symbol(op.ctx)(&bytes)
        }
        return data, nil
 }
@@ -113,8 +111,7 @@ func (op *Operator) Read(path string) ([]byte, error) {
 //
 // Note: This example assumes proper error handling and import statements.
 func (op *Operator) Reader(path string) (*Reader, error) {
-       getReader := getFFI[operatorReader](op.ctx, symOperatorReader)
-       inner, err := getReader(op.inner, path)
+       inner, err := ffiOperatorReader.symbol(op.ctx)(op.inner, path)
        if err != nil {
                return nil, err
        }
@@ -177,7 +174,7 @@ func (r *Reader) Read(buf []byte) (int, error) {
        if length == 0 {
                return 0, nil
        }
-       read := getFFI[readerRead](r.ctx, symReaderRead)
+       read := ffiReaderRead.symbol(r.ctx)
        var (
                totalSize uint
                size      uint
@@ -238,26 +235,20 @@ func (r *Reader) Read(buf []byte) (int, error) {
 // Note: The actual new position may differ from the requested position
 // if the underlying storage system has restrictions on seeking.
 func (r *Reader) Seek(offset int64, whence int) (int64, error) {
-       seek := getFFI[readerSeek](r.ctx, symReaderSeek)
-       return seek(r.inner, offset, whence)
+       return ffiReaderSeek.symbol(r.ctx)(r.inner, offset, whence)
 }
 
 // Close releases resources associated with the OperatorReader.
 func (r *Reader) Close() error {
-       free := getFFI[readerFree](r.ctx, symReaderFree)
-       free(r.inner)
+       ffiReaderFree.symbol(r.ctx)(r.inner)
        return nil
 }
 
-const symOperatorRead = "opendal_operator_read"
-
-type operatorRead func(op *opendalOperator, path string) (opendalBytes, error)
-
-var withOperatorRead = withFFI(ffiOpts{
-       sym:    symOperatorRead,
+var ffiOperatorRead = newFFI(ffiOpts{
+       sym:    "opendal_operator_read",
        rType:  &typeResultRead,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorRead {
+}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator, path 
string) (opendalBytes, error) {
        return func(op *opendalOperator, path string) (opendalBytes, error) {
                bytePath, err := BytePtrFromString(path)
                if err != nil {
@@ -273,15 +264,11 @@ var withOperatorRead = withFFI(ffiOpts{
        }
 })
 
-const symOperatorReader = "opendal_operator_reader"
-
-type operatorReader func(op *opendalOperator, path string) (*opendalReader, 
error)
-
-var withOperatorReader = withFFI(ffiOpts{
-       sym:    symOperatorReader,
+var ffiOperatorReader = newFFI(ffiOpts{
+       sym:    "opendal_operator_reader",
        rType:  &typeResultOperatorReader,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorReader {
+}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator, path 
string) (*opendalReader, error) {
        return func(op *opendalOperator, path string) (*opendalReader, error) {
                bytePath, err := BytePtrFromString(path)
                if err != nil {
@@ -300,15 +287,11 @@ var withOperatorReader = withFFI(ffiOpts{
        }
 })
 
-const symReaderFree = "opendal_reader_free"
-
-type readerFree func(r *opendalReader)
-
-var withReaderFree = withFFI(ffiOpts{
-       sym:    symReaderFree,
+var ffiReaderFree = newFFI(ffiOpts{
+       sym:    "opendal_reader_free",
        rType:  &ffi.TypeVoid,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) readerFree {
+}, func(ctx context.Context, ffiCall ffiCall) func(r *opendalReader) {
        return func(r *opendalReader) {
                ffiCall(
                        nil,
@@ -317,15 +300,11 @@ var withReaderFree = withFFI(ffiOpts{
        }
 })
 
-const symReaderRead = "opendal_reader_read"
-
-type readerRead func(r *opendalReader, buf []byte) (size uint, err error)
-
-var withReaderRead = withFFI(ffiOpts{
-       sym:    symReaderRead,
+var ffiReaderRead = newFFI(ffiOpts{
+       sym:    "opendal_reader_read",
        rType:  &typeResultReaderRead,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer, 
&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) readerRead {
+}, func(ctx context.Context, ffiCall ffiCall) func(r *opendalReader, buf 
[]byte) (size uint, err error) {
        return func(r *opendalReader, buf []byte) (size uint, err error) {
                var length = len(buf)
                if length == 0 {
@@ -346,15 +325,11 @@ var withReaderRead = withFFI(ffiOpts{
        }
 })
 
-const symReaderSeek = "opendal_reader_seek"
-
-type readerSeek func(r *opendalReader, offset int64, whence int) (int64, error)
-
-var withReaderSeek = withFFI(ffiOpts{
-       sym:    symReaderSeek,
+var ffiReaderSeek = newFFI(ffiOpts{
+       sym:    "opendal_reader_seek",
        rType:  &typeResultReaderSeek,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer, 
&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) readerSeek {
+}, func(ctx context.Context, ffiCall ffiCall) func(r *opendalReader, offset 
int64, whence int) (int64, error) {
        return func(r *opendalReader, offset int64, whence int) (int64, error) {
                var result resultReaderSeek
                ffiCall(
diff --git a/bindings/go/stat.go b/bindings/go/stat.go
index f4a8a6881..a754dbb4c 100644
--- a/bindings/go/stat.go
+++ b/bindings/go/stat.go
@@ -61,8 +61,7 @@ import (
 //
 // Note: This example assumes proper error handling and import statements.
 func (op *Operator) Stat(path string) (*Metadata, error) {
-       stat := getFFI[operatorStat](op.ctx, symOperatorStat)
-       meta, err := stat(op.inner, path)
+       meta, err := ffiOperatorStat.symbol(op.ctx)(op.inner, path)
        if err != nil {
                return nil, err
        }
@@ -96,19 +95,14 @@ func (op *Operator) Stat(path string) (*Metadata, error) {
 //             fmt.Println("The file does not exist")
 //     }
 func (op *Operator) IsExist(path string) (bool, error) {
-       isExist := getFFI[operatorIsExist](op.ctx, symOperatorIsExist)
-       return isExist(op.inner, path)
+       return ffiOperatorIsExist.symbol(op.ctx)(op.inner, path)
 }
 
-const symOperatorStat = "opendal_operator_stat"
-
-type operatorStat func(op *opendalOperator, path string) (*opendalMetadata, 
error)
-
-var withOperatorStat = withFFI(ffiOpts{
-       sym:    symOperatorStat,
+var ffiOperatorStat = newFFI(ffiOpts{
+       sym:    "opendal_operator_stat",
        rType:  &typeResultStat,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorStat {
+}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator, path 
string) (*opendalMetadata, error) {
        return func(op *opendalOperator, path string) (*opendalMetadata, error) 
{
                bytePath, err := BytePtrFromString(path)
                if err != nil {
@@ -127,15 +121,11 @@ var withOperatorStat = withFFI(ffiOpts{
        }
 })
 
-const symOperatorIsExist = "opendal_operator_is_exist"
-
-type operatorIsExist func(op *opendalOperator, path string) (bool, error)
-
-var withOperatorIsExists = withFFI(ffiOpts{
-       sym:    symOperatorIsExist,
+var ffiOperatorIsExist = newFFI(ffiOpts{
+       sym:    "opendal_operator_is_exist",
        rType:  &typeResultIsExist,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorIsExist {
+}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator, path 
string) (bool, error) {
        return func(op *opendalOperator, path string) (bool, error) {
                bytePath, err := BytePtrFromString(path)
                if err != nil {
diff --git a/bindings/go/write.go b/bindings/go/write.go
index 520dcff43..c2d0e5088 100644
--- a/bindings/go/write.go
+++ b/bindings/go/write.go
@@ -54,8 +54,7 @@ import (
 //
 // Note: This example assumes proper error handling and import statements.
 func (op *Operator) Write(path string, data []byte) error {
-       write := getFFI[operatorWrite](op.ctx, symOperatorWrite)
-       return write(op.inner, path, data)
+       return ffiOperatorWrite.symbol(op.ctx)(op.inner, path, data)
 }
 
 // CreateDir creates a directory at the specified path.
@@ -94,8 +93,7 @@ func (op *Operator) Write(path string, data []byte) error {
 // Note: This example assumes proper error handling and import statements.
 // The trailing slash in "test/" is important to indicate it's a directory.
 func (op *Operator) CreateDir(path string) error {
-       createDir := getFFI[operatorCreateDir](op.ctx, symOperatorCreateDir)
-       return createDir(op.inner, path)
+       return ffiOperatorCreateDir.symbol(op.ctx)(op.inner, path)
 }
 
 // Writer returns a new Writer for the specified path.
@@ -127,9 +125,7 @@ func (op *Operator) CreateDir(path string) error {
 //
 // Note: This example assumes proper error handling and import statements.
 func (op *Operator) Writer(path string) (*Writer, error) {
-       getWriter := getFFI[operatorWriter](op.ctx,
-               symOperatorWriter)
-       inner, err := getWriter(op.inner, path)
+       inner, err := ffiOperatorWriter.symbol(op.ctx)(op.inner, path)
        if err != nil {
                return nil, err
        }
@@ -173,31 +169,24 @@ type Writer struct {
 //
 // Note: This example assumes proper error handling and import statements.
 func (w *Writer) Write(p []byte) (n int, err error) {
-       write := getFFI[writerWrite](w.ctx, symWriterWrite)
-       return write(w.inner, p)
+       return ffiWriterWrite.symbol(w.ctx)(w.inner, p)
 }
 
 // Close finishes the write and releases the resources associated with the 
Writer.
 // It is important to call Close after writing all the data to ensure that the 
data is
 // properly flushed and written to the storage. Otherwise, the data may be 
lost.
 func (w *Writer) Close() error {
-       free := getFFI[writerFree](w.ctx, symWriterFree)
-       defer free(w.inner)
-       close := getFFI[writerClose](w.ctx, symWriterClose)
-       return close(w.inner)
+       defer ffiWriterFree.symbol(w.ctx)(w.inner)
+       return ffiWriterClose.symbol(w.ctx)(w.inner)
 }
 
 var _ io.WriteCloser = (*Writer)(nil)
 
-const symOperatorWrite = "opendal_operator_write"
-
-type operatorWrite func(op *opendalOperator, path string, data []byte) error
-
-var withOperatorWrite = withFFI(ffiOpts{
-       sym:    symOperatorWrite,
+var ffiOperatorWrite = newFFI(ffiOpts{
+       sym:    "opendal_operator_write",
        rType:  &ffi.TypePointer,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer, 
&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorWrite {
+}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator, path 
string, data []byte) error {
        return func(op *opendalOperator, path string, data []byte) error {
                bytePath, err := BytePtrFromString(path)
                if err != nil {
@@ -215,15 +204,11 @@ var withOperatorWrite = withFFI(ffiOpts{
        }
 })
 
-const symOperatorCreateDir = "opendal_operator_create_dir"
-
-type operatorCreateDir func(op *opendalOperator, path string) error
-
-var withOperatorCreateDir = withFFI(ffiOpts{
-       sym:    symOperatorCreateDir,
+var ffiOperatorCreateDir = newFFI(ffiOpts{
+       sym:    "opendal_operator_create_dir",
        rType:  &ffi.TypePointer,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorCreateDir {
+}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator, path 
string) error {
        return func(op *opendalOperator, path string) error {
                bytePath, err := BytePtrFromString(path)
                if err != nil {
@@ -239,15 +224,11 @@ var withOperatorCreateDir = withFFI(ffiOpts{
        }
 })
 
-const symOperatorWriter = "opendal_operator_writer"
-
-type operatorWriter func(op *opendalOperator, path string) (*opendalWriter, 
error)
-
-var withOperatorWriter = withFFI(ffiOpts{
-       sym:    symOperatorWriter,
+var ffiOperatorWriter = newFFI(ffiOpts{
+       sym:    "opendal_operator_writer",
        rType:  &typeResultOperatorWriter,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) operatorWriter {
+}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator, path 
string) (*opendalWriter, error) {
        return func(op *opendalOperator, path string) (*opendalWriter, error) {
                bytePath, err := BytePtrFromString(path)
                if err != nil {
@@ -266,34 +247,26 @@ var withOperatorWriter = withFFI(ffiOpts{
        }
 })
 
-const symWriterFree = "opendal_writer_free"
-
-type writerFree func(w *opendalWriter)
-
-var withWriterFree = withFFI(ffiOpts{
-       sym:    symWriterFree,
+var ffiWriterFree = newFFI(ffiOpts{
+       sym:    "opendal_writer_free",
        rType:  &ffi.TypeVoid,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) writerFree {
-       return func(r *opendalWriter) {
+}, func(ctx context.Context, ffiCall ffiCall) func(w *opendalWriter) {
+       return func(w *opendalWriter) {
                ffiCall(
                        nil,
-                       unsafe.Pointer(&r),
+                       unsafe.Pointer(&w),
                )
        }
 })
 
-const symWriterWrite = "opendal_writer_write"
-
-type writerWrite func(r *opendalWriter, buf []byte) (size int, err error)
-
-var withWriterWrite = withFFI(ffiOpts{
-       sym:    symWriterWrite,
+var ffiWriterWrite = newFFI(ffiOpts{
+       sym:    "opendal_writer_write",
        rType:  &typeResultWriterWrite,
        aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) writerWrite {
-       return func(r *opendalWriter, data []byte) (size int, err error) {
-               bytes := toOpendalBytes(data)
+}, func(ctx context.Context, ffiCall ffiCall) func(r *opendalWriter, buf 
[]byte) (size int, err error) {
+       return func(r *opendalWriter, buf []byte) (size int, err error) {
+               bytes := toOpendalBytes(buf)
                var result resultWriterWrite
                ffiCall(
                        unsafe.Pointer(&result),
@@ -307,15 +280,11 @@ var withWriterWrite = withFFI(ffiOpts{
        }
 })
 
-const symWriterClose = "opendal_writer_close"
-
-type writerClose func(r *opendalWriter) error
-
-var withWriterClose = withFFI(ffiOpts{
-       sym:    symWriterClose,
+var ffiWriterClose = newFFI(ffiOpts{
+       sym:    "opendal_writer_close",
        rType:  &ffi.TypePointer,
        aTypes: []*ffi.Type{&ffi.TypePointer},
-}, func(ctx context.Context, ffiCall ffiCall) writerClose {
+}, func(ctx context.Context, ffiCall ffiCall) func(r *opendalWriter) error {
        return func(r *opendalWriter) error {
                var e *opendalError
                ffiCall(

Reply via email to