diff --git a/bindings/go/types.go b/bindings/go/types.go index 49c0daf5d3f..4ee2de18841 100644 --- a/bindings/go/types.go +++ b/bindings/go/types.go @@ -89,6 +89,15 @@ var ( }[0], } + typeResultWriterWrite = ffi.Type{ + Type: ffi.Struct, + Elements: &[]*ffi.Type{ + &ffi.TypePointer, + &ffi.TypePointer, + nil, + }[0], + } + typeResultReaderRead = ffi.Type{ Type: ffi.Struct, Elements: &[]*ffi.Type{ @@ -209,6 +218,18 @@ type resultOperatorReader struct { error *opendalError } +type opendalWriter struct{} + +type resultOperatorWriter struct { + writer *opendalWriter + error *opendalError +} + +type resultWriterWrite struct { + size uint + error *opendalError +} + type resultReaderRead struct { size uint error *opendalError diff --git a/bindings/go/write.go b/bindings/go/write.go index e1d18a13223..4b191dd8f31 100644 --- a/bindings/go/write.go +++ b/bindings/go/write.go @@ -21,6 +21,7 @@ package opendal import ( "context" + "io" "unsafe" "github.com/jupiterrider/ffi" @@ -98,6 +99,94 @@ func (op *Operator) CreateDir(path string) error { return createDir(op.inner, path) } +// Writer returns a new Writer for the specified path. +// +// Writer is a wrapper around the C-binding function `opendal_operator_writer`. +// It provides a way to obtain a writer for writing data to the storage system. +// +// # Parameters +// +// - path: The destination path where data will be written. +// +// # Returns +// +// - *Writer: A pointer to a Writer instance, or an error if the operation fails. +// +// # Example +// +// func exampleWriter(op *opendal.Operator) { +// writer, err := op.Writer("test/") +// if err != nil { +// log.Fatal(err) +// } +// defer writer.Close() +// _, err = writer.Write([]byte("Hello opendal writer!")) +// if err != nil { +// log.Fatal(err) +// } +// } +// +// 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) + if err != nil { + return nil, err + } + writer := &Writer{ + inner: inner, + ctx: op.ctx, + } + return writer, nil +} + +type Writer struct { + inner *opendalWriter + ctx context.Context +} + +// Write writes the given bytes to the specified path. +// +// Write is a wrapper around the C-binding function `opendal_operator_write`. It provides a simplified +// interface for writing data to the storage. Write can be called multiple times to write +// additional data to the same path. +// +// # Parameters +// +// - path: The destination path where the bytes will be written. +// - data: The byte slice containing the data to be written. +// +// # Returns +// +// - error: An error if the write operation fails, or nil if successful. +// +// # Example +// +// func exampleWrite(op *opendal.Operator) { +// err = op.Write("test", []byte("Hello opendal go binding!")) +// if err != nil { +// log.Fatal(err) +// } +// } +// +// 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) +} + +// 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. +func (w *Writer) Close() error { + free := getFFI[writerFree](w.ctx, symWriterFree) + free(w.inner) + return nil +} + +var _ io.WriteCloser = (*Writer)(nil) + const symOperatorWrite = "opendal_operator_write" type operatorWrite func(op *opendalOperator, path string, data []byte) error @@ -150,3 +239,76 @@ var withOperatorCreateDir = withFFI(ffiOpts{ return parseError(ctx, e) } }) + +const symOperatorWriter = "opendal_operator_writer" + +type operatorWriter func(op *opendalOperator, path string) (*opendalWriter, error) + +var withOperatorWriter = withFFI(ffiOpts{ + sym: symOperatorWriter, + rType: &ffi.TypePointer, + aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer}, +}, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) operatorWriter { + return func(op *opendalOperator, path string) (*opendalWriter, error) { + bytePath, err := unix.BytePtrFromString(path) + if err != nil { + return nil, err + } + var result resultOperatorWriter + ffiCall( + unsafe.Pointer(&result), + unsafe.Pointer(&op), + unsafe.Pointer(&bytePath), + ) + if result.error != nil { + return nil, parseError(ctx, result.error) + } + return result.writer, nil + } +}) + +const symWriterFree = "opendal_writer_free" + +type writerFree func(w *opendalWriter) + +var withWriterFree = withFFI(ffiOpts{ + sym: symWriterFree, + rType: &ffi.TypeVoid, + aTypes: []*ffi.Type{&ffi.TypePointer}, +}, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) writerFree { + return func(r *opendalWriter) { + ffiCall( + nil, + unsafe.Pointer(&r), + ) + } +}) + +const symWriterWrite = "opendal_writer_write" + +type writerWrite func(r *opendalWriter, buf []byte) (size int, err error) + +var withWriterWrite = withFFI(ffiOpts{ + sym: symWriterWrite, + rType: &typeResultWriterWrite, + aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer, &ffi.TypePointer}, +}, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) writerWrite { + return func(r *opendalWriter, buf []byte) (size int, err error) { + var length = len(buf) + if length == 0 { + return 0, nil + } + bytePtr := &buf[0] + var result resultWriterWrite + ffiCall( + unsafe.Pointer(&result), + unsafe.Pointer(&r), + unsafe.Pointer(&bytePtr), + unsafe.Pointer(&length), + ) + if result.error != nil { + return 0, parseError(ctx, result.error) + } + return int(result.size), nil + } +})