blob: b511b0157bdee941c17e59000318467ff72a8888 [file]
/*
* 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.
*/
package opendal
import (
"context"
"io"
"runtime"
"time"
"unsafe"
"github.com/jupiterrider/ffi"
)
// Read reads the entire contents of the file at the specified path into a byte slice.
//
// Read is a wrapper around the C-binding function `opendal_operator_read`.
// When options are provided, it uses `opendal_operator_read_with`.
//
// # Parameters
//
// - path: The path of the file to read.
// - opts: Optional read options.
//
// # Returns
//
// - []byte: The contents of the file as a byte slice.
// - error: An error if the read operation fails, or nil if successful.
//
// # Notes
//
// - Read allocates a new byte slice internally. For more precise memory control
// or lazy reading, consider using the Reader() method instead.
//
// # Example
//
// func exampleRead(op *opendal.Operator) {
// data, err := op.Read("test")
// if err != nil {
// log.Fatal(err)
// }
// fmt.Printf("Read: %s\n", data)
// }
//
// Note: This example assumes proper error handling and import statements.
func (op *Operator) Read(path string, opts ...WithReadFn) ([]byte, error) {
bytes, err := op.read(path, opts...)
if err != nil {
return nil, err
}
data := parseBytes(bytes)
if len(data) > 0 {
ffiBytesFree.symbol(op.ctx)(&bytes)
}
return data, nil
}
func (op *Operator) read(path string, opts ...WithReadFn) (opendalBytes, error) {
if len(opts) == 0 {
return ffiOperatorRead.symbol(op.ctx)(op.inner, path)
}
o := parseReadOptions(opts...)
cOpts, keepAlive, err := newOpendalReadOptions(op.ctx, o)
if err != nil {
return opendalBytes{}, err
}
defer ffiReadOptionsFree.symbol(op.ctx)(cOpts)
bytes, err := ffiOperatorReadWith.symbol(op.ctx)(op.inner, path, cOpts)
runtime.KeepAlive(keepAlive)
return bytes, err
}
// WithReadFn is a functional option for read operations.
type WithReadFn func(*readOptions)
// ReadWithRange sets the byte range to read, starting at offset and reading
// length bytes. To read a file with size n, offset must be in [0, n) and the
// effective range is [offset, offset+length).
func ReadWithRange(offset, length uint64) WithReadFn {
return func(o *readOptions) {
o.hasRange = true
o.hasRangeLength = true
o.rangeOffset = offset
o.rangeLength = length
}
}
// ReadWithRangeFrom sets the byte range to start at offset and read until the
// end of the file, i.e. the range [offset, n) for a file of size n.
func ReadWithRangeFrom(offset uint64) WithReadFn {
return func(o *readOptions) {
o.hasRange = true
o.hasRangeLength = false
o.rangeOffset = offset
o.rangeLength = 0
}
}
// ReadWithVersion sets the version of the object to read.
func ReadWithVersion(version string) WithReadFn {
return func(o *readOptions) {
o.version = version
}
}
// ReadWithIfMatch sets the If-Match condition for the read operation.
func ReadWithIfMatch(ifMatch string) WithReadFn {
return func(o *readOptions) {
o.ifMatch = ifMatch
}
}
// ReadWithIfNoneMatch sets the If-None-Match condition for the read operation.
func ReadWithIfNoneMatch(ifNoneMatch string) WithReadFn {
return func(o *readOptions) {
o.ifNoneMatch = ifNoneMatch
}
}
// ReadWithIfModifiedSince sets the If-Modified-Since condition for the read operation.
func ReadWithIfModifiedSince(t time.Time) WithReadFn {
return func(o *readOptions) {
millis := t.UnixMilli()
o.ifModifiedSince = &millis
}
}
// ReadWithIfUnmodifiedSince sets the If-Unmodified-Since condition for the read operation.
func ReadWithIfUnmodifiedSince(t time.Time) WithReadFn {
return func(o *readOptions) {
millis := t.UnixMilli()
o.ifUnmodifiedSince = &millis
}
}
// ReadWithConcurrent sets the number of concurrent read tasks.
func ReadWithConcurrent(concurrent uint) WithReadFn {
return func(o *readOptions) {
o.concurrent = concurrent
}
}
// ReadWithChunk sets the chunk size for each read request.
func ReadWithChunk(chunk uint) WithReadFn {
return func(o *readOptions) {
o.chunk = chunk
}
}
// ReadWithGap sets the gap size for merging nearby range reads.
func ReadWithGap(gap uint) WithReadFn {
return func(o *readOptions) {
o.gap = gap
}
}
// ReadWithContentLengthHint sets the known content length of the object.
//
// This is an execution hint that allows OpenDAL to avoid extra metadata
// requests while planning reads. It must not be used as an object identity
// or consistency condition.
func ReadWithContentLengthHint(length uint64) WithReadFn {
return func(o *readOptions) {
o.contentLengthHint = &length
}
}
// ReadWithOverrideContentType sets the Content-Type to send back (presign only).
func ReadWithOverrideContentType(contentType string) WithReadFn {
return func(o *readOptions) {
o.overrideContentType = contentType
}
}
// ReadWithOverrideCacheControl sets the Cache-Control to send back (presign only).
func ReadWithOverrideCacheControl(cacheControl string) WithReadFn {
return func(o *readOptions) {
o.overrideCacheControl = cacheControl
}
}
// ReadWithOverrideContentDisposition sets the Content-Disposition to send back (presign only).
func ReadWithOverrideContentDisposition(contentDisposition string) WithReadFn {
return func(o *readOptions) {
o.overrideContentDisposition = contentDisposition
}
}
type readOptions struct {
hasRange bool
hasRangeLength bool
rangeOffset uint64
rangeLength uint64
version string
ifMatch string
ifNoneMatch string
ifModifiedSince *int64
ifUnmodifiedSince *int64
concurrent uint
chunk uint
gap uint
contentLengthHint *uint64
overrideContentType string
overrideCacheControl string
overrideContentDisposition string
}
func parseReadOptions(opts ...WithReadFn) *readOptions {
o := &readOptions{}
for _, opt := range opts {
opt(o)
}
return o
}
type readOptionsKeepAlive struct {
strings [][]byte
}
func newOpendalReadOptions(ctx context.Context, o *readOptions) (*opendalReadOptions, readOptionsKeepAlive, error) {
cOpts := ffiReadOptionsNew.symbol(ctx)()
keepAlive := readOptionsKeepAlive{}
// fail frees the C-allocated options before returning
fail := func(err error) (*opendalReadOptions, readOptionsKeepAlive, error) {
ffiReadOptionsFree.symbol(ctx)(cOpts)
return nil, readOptionsKeepAlive{}, err
}
setString := func(value string, set func(*opendalReadOptions, string) ([]byte, error)) error {
if value == "" {
return nil
}
data, err := set(cOpts, value)
if err != nil {
return err
}
keepAlive.strings = append(keepAlive.strings, data)
return nil
}
if o.hasRange {
if o.hasRangeLength {
ffiReadOptionsSetRange.symbol(ctx)(cOpts, o.rangeOffset, o.rangeLength)
} else {
ffiReadOptionsSetRangeFrom.symbol(ctx)(cOpts, o.rangeOffset)
}
}
if err := setString(o.version, ffiReadOptionsSetVersion.symbol(ctx)); err != nil {
return fail(err)
}
if err := setString(o.ifMatch, ffiReadOptionsSetIfMatch.symbol(ctx)); err != nil {
return fail(err)
}
if err := setString(o.ifNoneMatch, ffiReadOptionsSetIfNoneMatch.symbol(ctx)); err != nil {
return fail(err)
}
if o.ifModifiedSince != nil {
ffiReadOptionsSetIfModifiedSince.symbol(ctx)(cOpts, *o.ifModifiedSince)
}
if o.ifUnmodifiedSince != nil {
ffiReadOptionsSetIfUnmodifiedSince.symbol(ctx)(cOpts, *o.ifUnmodifiedSince)
}
if o.concurrent != 0 {
ffiReadOptionsSetConcurrent.symbol(ctx)(cOpts, o.concurrent)
}
if o.chunk != 0 {
ffiReadOptionsSetChunk.symbol(ctx)(cOpts, o.chunk)
}
if o.gap != 0 {
ffiReadOptionsSetGap.symbol(ctx)(cOpts, o.gap)
}
if o.contentLengthHint != nil {
ffiReadOptionsSetContentLengthHint.symbol(ctx)(cOpts, *o.contentLengthHint)
}
if err := setString(o.overrideContentType, ffiReadOptionsSetOverrideContentType.symbol(ctx)); err != nil {
return fail(err)
}
if err := setString(o.overrideCacheControl, ffiReadOptionsSetOverrideCacheControl.symbol(ctx)); err != nil {
return fail(err)
}
if err := setString(o.overrideContentDisposition, ffiReadOptionsSetOverrideContentDisposition.symbol(ctx)); err != nil {
return fail(err)
}
return cOpts, keepAlive, nil
}
// WithReaderFn is a functional option for creating a Reader.
type WithReaderFn func(*readerOptions)
// ReaderWithVersion sets the version of the object to read.
func ReaderWithVersion(version string) WithReaderFn {
return func(o *readerOptions) {
o.version = version
}
}
// ReaderWithIfMatch sets the If-Match condition for the reader.
func ReaderWithIfMatch(ifMatch string) WithReaderFn {
return func(o *readerOptions) {
o.ifMatch = ifMatch
}
}
// ReaderWithIfNoneMatch sets the If-None-Match condition for the reader.
func ReaderWithIfNoneMatch(ifNoneMatch string) WithReaderFn {
return func(o *readerOptions) {
o.ifNoneMatch = ifNoneMatch
}
}
// ReaderWithIfModifiedSince sets the If-Modified-Since condition for the reader.
func ReaderWithIfModifiedSince(t time.Time) WithReaderFn {
return func(o *readerOptions) {
millis := t.UnixMilli()
o.ifModifiedSince = &millis
}
}
// ReaderWithIfUnmodifiedSince sets the If-Unmodified-Since condition for the reader.
func ReaderWithIfUnmodifiedSince(t time.Time) WithReaderFn {
return func(o *readerOptions) {
millis := t.UnixMilli()
o.ifUnmodifiedSince = &millis
}
}
// ReaderWithContentLengthHint sets the known content length of the object.
//
// This is an execution hint that allows OpenDAL to avoid extra metadata
// requests while planning reads. It must not be used as an object identity
// or consistency condition.
func ReaderWithContentLengthHint(length uint64) WithReaderFn {
return func(o *readerOptions) {
o.contentLengthHint = &length
}
}
// ReaderWithConcurrent sets the number of concurrent read tasks.
func ReaderWithConcurrent(concurrent uint) WithReaderFn {
return func(o *readerOptions) {
o.concurrent = concurrent
}
}
// ReaderWithChunk sets the chunk size for each read request.
func ReaderWithChunk(chunk uint) WithReaderFn {
return func(o *readerOptions) {
o.chunk = chunk
}
}
// ReaderWithGap sets the gap size for merging nearby range reads.
func ReaderWithGap(gap uint) WithReaderFn {
return func(o *readerOptions) {
o.gap = gap
}
}
// ReaderWithPrefetch sets the number of prefetched byte ranges buffered during concurrent reads.
func ReaderWithPrefetch(prefetch uint) WithReaderFn {
return func(o *readerOptions) {
o.prefetch = prefetch
}
}
type readerOptions struct {
version string
ifMatch string
ifNoneMatch string
ifModifiedSince *int64
ifUnmodifiedSince *int64
contentLengthHint *uint64
concurrent uint
chunk uint
gap uint
prefetch uint
}
func parseReaderOptions(opts ...WithReaderFn) *readerOptions {
o := &readerOptions{}
for _, opt := range opts {
opt(o)
}
return o
}
func newOpendalReaderOptions(ctx context.Context, o *readerOptions) (*opendalReaderOptions, readOptionsKeepAlive, error) {
cOpts := ffiReaderOptionsNew.symbol(ctx)()
keepAlive := readOptionsKeepAlive{}
// fail frees the C-allocated options before returning
fail := func(err error) (*opendalReaderOptions, readOptionsKeepAlive, error) {
ffiReaderOptionsFree.symbol(ctx)(cOpts)
return nil, readOptionsKeepAlive{}, err
}
setString := func(value string, set func(*opendalReaderOptions, string) ([]byte, error)) error {
if value == "" {
return nil
}
data, err := set(cOpts, value)
if err != nil {
return err
}
keepAlive.strings = append(keepAlive.strings, data)
return nil
}
if err := setString(o.version, ffiReaderOptionsSetVersion.symbol(ctx)); err != nil {
return fail(err)
}
if err := setString(o.ifMatch, ffiReaderOptionsSetIfMatch.symbol(ctx)); err != nil {
return fail(err)
}
if err := setString(o.ifNoneMatch, ffiReaderOptionsSetIfNoneMatch.symbol(ctx)); err != nil {
return fail(err)
}
if o.ifModifiedSince != nil {
ffiReaderOptionsSetIfModifiedSince.symbol(ctx)(cOpts, *o.ifModifiedSince)
}
if o.ifUnmodifiedSince != nil {
ffiReaderOptionsSetIfUnmodifiedSince.symbol(ctx)(cOpts, *o.ifUnmodifiedSince)
}
if o.contentLengthHint != nil {
ffiReaderOptionsSetContentLengthHint.symbol(ctx)(cOpts, *o.contentLengthHint)
}
if o.concurrent != 0 {
ffiReaderOptionsSetConcurrent.symbol(ctx)(cOpts, o.concurrent)
}
if o.chunk != 0 {
ffiReaderOptionsSetChunk.symbol(ctx)(cOpts, o.chunk)
}
if o.gap != 0 {
ffiReaderOptionsSetGap.symbol(ctx)(cOpts, o.gap)
}
if o.prefetch != 0 {
ffiReaderOptionsSetPrefetch.symbol(ctx)(cOpts, o.prefetch)
}
return cOpts, keepAlive, nil
}
// Reader creates a new Reader for reading the contents of a file at the specified path.
//
// Reader is a wrapper around the C-binding function `opendal_operator_reader`.
// When options are provided, it uses `opendal_operator_reader_with`.
//
// # Parameters
//
// - path: The path of the file to read.
// - opts: Optional reader options such as conditional headers, version or
// concurrency tuning.
//
// # Returns
//
// - *Reader: A reader for accessing the file's contents. It implements `io.ReadCloser`.
// - error: An error if the reader creation fails, or nil if successful.
//
// # Notes
//
// - The returned reader allows for more controlled and efficient reading of large files.
//
// # Example
//
// func exampleReader(op *opendal.Operator) {
// r, err := op.Reader("path/to/file")
// if err != nil {
// log.Fatal(err)
// }
// defer r.Close()
//
// size := 1024 // Read 1KB at a time
// buffer := make([]byte, size)
//
// for {
// n, err := r.Read(buffer)
// if err != nil {
// log.Fatal(err)
// }
// fmt.Printf("Read %d bytes: %s\n", n, buffer[:n])
// }
// }
//
// Note: This example assumes proper error handling and import statements.
func (op *Operator) Reader(path string, opts ...WithReaderFn) (*Reader, error) {
if len(opts) == 0 {
inner, err := ffiOperatorReader.symbol(op.ctx)(op.inner, path)
if err != nil {
return nil, err
}
return &Reader{inner: inner, ctx: op.ctx}, nil
}
o := parseReaderOptions(opts...)
cOpts, keepAlive, err := newOpendalReaderOptions(op.ctx, o)
if err != nil {
return nil, err
}
defer ffiReaderOptionsFree.symbol(op.ctx)(cOpts)
inner, err := ffiOperatorReaderWith.symbol(op.ctx)(op.inner, path, cOpts)
runtime.KeepAlive(keepAlive)
if err != nil {
return nil, err
}
return &Reader{inner: inner, ctx: op.ctx}, nil
}
type Reader struct {
inner *opendalReader
ctx context.Context
}
var _ io.ReadSeekCloser = (*Reader)(nil)
// Read reads data from the underlying storage into the provided buffer.
//
// This method implements the io.Reader interface for OperatorReader.
//
// # Parameters
//
// - buf: A pre-allocated byte slice where the read data will be stored.
// The length of buf determines the maximum number of bytes to read.
//
// # Returns
//
// - int: The number of bytes read. Returns 0 if no data is available or the end of the file is reached.
// - error: An error if the read operation fails, or nil if successful.
// Note that this method does not return io.EOF; it returns nil at the end of the file.
//
// # Notes
//
// - The caller is responsible for pre-allocating the buffer and determining its size.
//
// # Example
//
// reader, err := op.Reader("path/to/file")
// if err != nil {
// log.Fatal(err)
// }
// defer reader.Close()
//
// buf := make([]byte, 1024)
// for {
// n, err := reader.Read(buf)
// if err != nil {
// log.Fatal(err)
// }
// if n == 0 {
// break // End of file
// }
// // Process buf[:n]
// }
//
// Note: Always check the number of bytes read (n) as it may be less than len(buf).
func (r *Reader) Read(buf []byte) (int, error) {
length := uint(len(buf))
if length == 0 {
return 0, nil
}
read := ffiReaderRead.symbol(r.ctx)
var (
totalSize uint
size uint
err error
)
for {
size, err = read(r.inner, buf[totalSize:])
totalSize += size
if size == 0 || err != nil || totalSize >= length {
break
}
}
if totalSize == 0 && err == nil {
err = io.EOF
}
return int(totalSize), err
}
// Seek sets the offset for the next Read operation on the reader.
//
// This method implements the io.Seeker interface for Reader.
//
// # Parameters
//
// - offset: The offset from the origin (specified by whence).
// - whence: The reference point for offset. Can be:
// - io.SeekStart (0): Relative to the start of the file
// - io.SeekCurrent (1): Relative to the current position
// - io.SeekEnd (2): Relative to the end of the file
//
// # Returns
//
// - int64: The new absolute position in the file after the seek operation.
// - error: An error if the seek operation fails, or nil if successful.
//
// # Example
//
// reader, err := op.Reader("path/to/file")
// if err != nil {
// log.Fatal(err)
// }
// defer reader.Close()
//
// // Seek to the middle of the file
// pos, err := reader.Seek(1000, io.SeekStart)
// if err != nil {
// log.Fatal(err)
// }
// fmt.Printf("New position: %d\n", pos)
//
// // Seek relative to current position
// pos, err = reader.Seek(100, io.SeekCurrent)
// if err != nil {
// log.Fatal(err)
// }
// fmt.Printf("New position: %d\n", pos)
//
// 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) {
return ffiReaderSeek.symbol(r.ctx)(r.inner, offset, whence)
}
// Close releases resources associated with the OperatorReader.
func (r *Reader) Close() error {
ffiReaderFree.symbol(r.ctx)(r.inner)
return nil
}
var ffiOperatorRead = newFFI(ffiOpts{
sym: "opendal_operator_read",
rType: &typeResultRead,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
}, 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 {
return opendalBytes{}, err
}
var result resultRead
ffiCall(
unsafe.Pointer(&result),
unsafe.Pointer(&op),
unsafe.Pointer(&bytePath),
)
return result.data, parseError(ctx, result.error)
}
})
var ffiOperatorReadWith = newFFI(ffiOpts{
sym: "opendal_operator_read_with",
rType: &typeResultRead,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer, &ffi.TypePointer},
}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator, path string, opts *opendalReadOptions) (opendalBytes, error) {
return func(op *opendalOperator, path string, opts *opendalReadOptions) (opendalBytes, error) {
bytePath, err := BytePtrFromString(path)
if err != nil {
return opendalBytes{}, err
}
var result resultRead
ffiCall(
unsafe.Pointer(&result),
unsafe.Pointer(&op),
unsafe.Pointer(&bytePath),
unsafe.Pointer(&opts),
)
return result.data, parseError(ctx, result.error)
}
})
var ffiReadOptionsNew = newFFI(ffiOpts{
sym: "opendal_read_options_new",
rType: &ffi.TypePointer,
}, func(_ context.Context, ffiCall ffiCall) func() *opendalReadOptions {
return func() *opendalReadOptions {
var opts *opendalReadOptions
ffiCall(unsafe.Pointer(&opts))
return opts
}
})
var ffiReadOptionsFree = newFFI(ffiOpts{
sym: "opendal_read_options_free",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReadOptions) {
return func(opts *opendalReadOptions) {
ffiCall(nil, unsafe.Pointer(&opts))
}
})
var ffiReadOptionsSetRange = newFFI(ffiOpts{
sym: "opendal_read_options_set_range",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypeUint64, &ffi.TypeUint64},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReadOptions, offset, length uint64) {
return func(opts *opendalReadOptions, offset, length uint64) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&offset), unsafe.Pointer(&length))
}
})
var ffiReadOptionsSetRangeFrom = newFFI(ffiOpts{
sym: "opendal_read_options_set_range_from",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypeUint64},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReadOptions, offset uint64) {
return func(opts *opendalReadOptions, offset uint64) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&offset))
}
})
func newReadOptionsSetStringFFI(sym string) *FFI[func(*opendalReadOptions, string) ([]byte, error)] {
return newFFI(ffiOpts{
sym: contextKey(sym),
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
}, func(_ context.Context, ffiCall ffiCall) func(*opendalReadOptions, string) ([]byte, error) {
return func(opts *opendalReadOptions, value string) ([]byte, error) {
data, err := byteSliceFromString(value)
if err != nil {
return nil, err
}
byteValue := &data[0]
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&byteValue))
return data, nil
}
})
}
var ffiReadOptionsSetVersion = newReadOptionsSetStringFFI("opendal_read_options_set_version")
var ffiReadOptionsSetIfMatch = newReadOptionsSetStringFFI("opendal_read_options_set_if_match")
var ffiReadOptionsSetIfNoneMatch = newReadOptionsSetStringFFI("opendal_read_options_set_if_none_match")
var ffiReadOptionsSetOverrideContentType = newReadOptionsSetStringFFI("opendal_read_options_set_override_content_type")
var ffiReadOptionsSetOverrideCacheControl = newReadOptionsSetStringFFI("opendal_read_options_set_override_cache_control")
var ffiReadOptionsSetOverrideContentDisposition = newReadOptionsSetStringFFI("opendal_read_options_set_override_content_disposition")
var ffiReadOptionsSetIfModifiedSince = newFFI(ffiOpts{
sym: "opendal_read_options_set_if_modified_since",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypeSint64},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReadOptions, millis int64) {
return func(opts *opendalReadOptions, millis int64) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&millis))
}
})
var ffiReadOptionsSetIfUnmodifiedSince = newFFI(ffiOpts{
sym: "opendal_read_options_set_if_unmodified_since",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypeSint64},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReadOptions, millis int64) {
return func(opts *opendalReadOptions, millis int64) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&millis))
}
})
var ffiReadOptionsSetConcurrent = newFFI(ffiOpts{
sym: "opendal_read_options_set_concurrent",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReadOptions, concurrent uint) {
return func(opts *opendalReadOptions, concurrent uint) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&concurrent))
}
})
var ffiReadOptionsSetChunk = newFFI(ffiOpts{
sym: "opendal_read_options_set_chunk",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReadOptions, chunk uint) {
return func(opts *opendalReadOptions, chunk uint) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&chunk))
}
})
var ffiReadOptionsSetGap = newFFI(ffiOpts{
sym: "opendal_read_options_set_gap",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReadOptions, gap uint) {
return func(opts *opendalReadOptions, gap uint) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&gap))
}
})
var ffiReadOptionsSetContentLengthHint = newFFI(ffiOpts{
sym: "opendal_read_options_set_content_length_hint",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypeUint64},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReadOptions, length uint64) {
return func(opts *opendalReadOptions, length uint64) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&length))
}
})
var ffiOperatorReader = newFFI(ffiOpts{
sym: "opendal_operator_reader",
rType: &typeResultOperatorReader,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
}, 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 {
return nil, err
}
var result resultOperatorReader
ffiCall(
unsafe.Pointer(&result),
unsafe.Pointer(&op),
unsafe.Pointer(&bytePath),
)
if result.error != nil {
return nil, parseError(ctx, result.error)
}
return result.reader, nil
}
})
var ffiOperatorReaderWith = newFFI(ffiOpts{
sym: "opendal_operator_reader_with",
rType: &typeResultOperatorReader,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer, &ffi.TypePointer},
}, func(ctx context.Context, ffiCall ffiCall) func(op *opendalOperator, path string, opts *opendalReaderOptions) (*opendalReader, error) {
return func(op *opendalOperator, path string, opts *opendalReaderOptions) (*opendalReader, error) {
bytePath, err := BytePtrFromString(path)
if err != nil {
return nil, err
}
var result resultOperatorReader
ffiCall(
unsafe.Pointer(&result),
unsafe.Pointer(&op),
unsafe.Pointer(&bytePath),
unsafe.Pointer(&opts),
)
if result.error != nil {
return nil, parseError(ctx, result.error)
}
return result.reader, nil
}
})
var ffiReaderOptionsNew = newFFI(ffiOpts{
sym: "opendal_reader_options_new",
rType: &ffi.TypePointer,
}, func(_ context.Context, ffiCall ffiCall) func() *opendalReaderOptions {
return func() *opendalReaderOptions {
var opts *opendalReaderOptions
ffiCall(unsafe.Pointer(&opts))
return opts
}
})
var ffiReaderOptionsFree = newFFI(ffiOpts{
sym: "opendal_reader_options_free",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReaderOptions) {
return func(opts *opendalReaderOptions) {
ffiCall(nil, unsafe.Pointer(&opts))
}
})
func newReaderOptionsSetStringFFI(sym string) *FFI[func(*opendalReaderOptions, string) ([]byte, error)] {
return newFFI(ffiOpts{
sym: contextKey(sym),
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
}, func(_ context.Context, ffiCall ffiCall) func(*opendalReaderOptions, string) ([]byte, error) {
return func(opts *opendalReaderOptions, value string) ([]byte, error) {
data, err := byteSliceFromString(value)
if err != nil {
return nil, err
}
byteValue := &data[0]
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&byteValue))
return data, nil
}
})
}
var ffiReaderOptionsSetVersion = newReaderOptionsSetStringFFI("opendal_reader_options_set_version")
var ffiReaderOptionsSetIfMatch = newReaderOptionsSetStringFFI("opendal_reader_options_set_if_match")
var ffiReaderOptionsSetIfNoneMatch = newReaderOptionsSetStringFFI("opendal_reader_options_set_if_none_match")
var ffiReaderOptionsSetIfModifiedSince = newFFI(ffiOpts{
sym: "opendal_reader_options_set_if_modified_since",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypeSint64},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReaderOptions, millis int64) {
return func(opts *opendalReaderOptions, millis int64) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&millis))
}
})
var ffiReaderOptionsSetIfUnmodifiedSince = newFFI(ffiOpts{
sym: "opendal_reader_options_set_if_unmodified_since",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypeSint64},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReaderOptions, millis int64) {
return func(opts *opendalReaderOptions, millis int64) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&millis))
}
})
var ffiReaderOptionsSetContentLengthHint = newFFI(ffiOpts{
sym: "opendal_reader_options_set_content_length_hint",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypeUint64},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReaderOptions, length uint64) {
return func(opts *opendalReaderOptions, length uint64) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&length))
}
})
var ffiReaderOptionsSetConcurrent = newFFI(ffiOpts{
sym: "opendal_reader_options_set_concurrent",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReaderOptions, concurrent uint) {
return func(opts *opendalReaderOptions, concurrent uint) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&concurrent))
}
})
var ffiReaderOptionsSetChunk = newFFI(ffiOpts{
sym: "opendal_reader_options_set_chunk",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReaderOptions, chunk uint) {
return func(opts *opendalReaderOptions, chunk uint) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&chunk))
}
})
var ffiReaderOptionsSetGap = newFFI(ffiOpts{
sym: "opendal_reader_options_set_gap",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReaderOptions, gap uint) {
return func(opts *opendalReaderOptions, gap uint) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&gap))
}
})
var ffiReaderOptionsSetPrefetch = newFFI(ffiOpts{
sym: "opendal_reader_options_set_prefetch",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
}, func(_ context.Context, ffiCall ffiCall) func(opts *opendalReaderOptions, prefetch uint) {
return func(opts *opendalReaderOptions, prefetch uint) {
ffiCall(nil, unsafe.Pointer(&opts), unsafe.Pointer(&prefetch))
}
})
var ffiReaderFree = newFFI(ffiOpts{
sym: "opendal_reader_free",
rType: &ffi.TypeVoid,
aTypes: []*ffi.Type{&ffi.TypePointer},
}, func(ctx context.Context, ffiCall ffiCall) func(r *opendalReader) {
return func(r *opendalReader) {
ffiCall(
nil,
unsafe.Pointer(&r),
)
}
})
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) 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 {
return 0, nil
}
bytePtr := &buf[0]
var result resultReaderRead
ffiCall(
unsafe.Pointer(&result),
unsafe.Pointer(&r),
unsafe.Pointer(&bytePtr),
unsafe.Pointer(&length),
)
if result.error != nil {
return 0, parseError(ctx, result.error)
}
return result.size, nil
}
})
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) func(r *opendalReader, offset int64, whence int) (int64, error) {
return func(r *opendalReader, offset int64, whence int) (int64, error) {
var result resultReaderSeek
ffiCall(
unsafe.Pointer(&result),
unsafe.Pointer(&r),
unsafe.Pointer(&offset),
unsafe.Pointer(&whence),
)
if result.error != nil {
return 0, parseError(ctx, result.error)
}
return int64(result.pos), nil
}
})