diff --git a/docs/api-status.json b/docs/api-status.json index 8fee5c283..219f60f1e 100644 --- a/docs/api-status.json +++ b/docs/api-status.json @@ -932,6 +932,12 @@ "comment": "CmpExt ensures that given object range (extent) satisfies comparison.\n PREVIEW\n\nImplements:\n void rados_write_op_cmpext(rados_write_op_t write_op,\n const char * cmp_buf,\n size_t cmp_len,\n uint64_t off,\n int * prval);\n", "added_in_version": "v0.12.0", "expected_stable_version": "v0.14.0" + }, + { + "name": "ReadOp.Read", + "comment": "Read bytes from offset into buffer.\nlen(buffer) is the maximum number of bytes read from the object.\nbuffer[:ReadOpReadStep.BytesRead] then contains object data.\n PREVIEW\n\nImplements:\n void rados_read_op_read(rados_read_op_t read_op,\n uint64_t offset,\n size_t len,\n char * buffer,\n size_t * bytes_read,\n int * prval)\n", + "added_in_version": "v0.14.0", + "expected_stable_version": "v0.16.0" } ] }, diff --git a/docs/api-status.md b/docs/api-status.md index 2f921ea4a..17772a869 100644 --- a/docs/api-status.md +++ b/docs/api-status.md @@ -13,6 +13,7 @@ Name | Added in Version | Expected Stable Version | ---- | ---------------- | ----------------------- | WriteOp.CmpExt | v0.12.0 | v0.14.0 | +ReadOp.Read | v0.14.0 | v0.16.0 | ## Package: rbd diff --git a/rados/read_op_read.go b/rados/read_op_read.go new file mode 100644 index 000000000..0c4809360 --- /dev/null +++ b/rados/read_op_read.go @@ -0,0 +1,75 @@ +//go:build ceph_preview +// +build ceph_preview + +package rados + +// #cgo LDFLAGS: -lrados +// #include +// #include +// +import "C" + +import ( + "unsafe" +) + +// ReadOpReadStep holds the result of the Read read operation. +// Result is valid only after Operate() was called. +type ReadOpReadStep struct { + // C returned data: + bytesRead *C.size_t + prval *C.int + + BytesRead int64 // Bytes read by this action. + Result int // Result of this action. +} + +func (s *ReadOpReadStep) update() error { + s.BytesRead = (int64)(*s.bytesRead) + s.Result = (int)(*s.prval) + + return nil +} + +func (s *ReadOpReadStep) free() { + C.free(unsafe.Pointer(s.bytesRead)) + C.free(unsafe.Pointer(s.prval)) + + s.bytesRead = nil + s.prval = nil +} + +func newReadOpReadStep() *ReadOpReadStep { + return &ReadOpReadStep{ + bytesRead: (*C.size_t)(C.malloc(C.sizeof_size_t)), + prval: (*C.int)(C.malloc(C.sizeof_int)), + } +} + +// Read bytes from offset into buffer. +// len(buffer) is the maximum number of bytes read from the object. +// buffer[:ReadOpReadStep.BytesRead] then contains object data. +// PREVIEW +// +// Implements: +// void rados_read_op_read(rados_read_op_t read_op, +// uint64_t offset, +// size_t len, +// char * buffer, +// size_t * bytes_read, +// int * prval) +func (r *ReadOp) Read(offset uint64, buffer []byte) *ReadOpReadStep { + oe := newReadStep(buffer, offset) + readStep := newReadOpReadStep() + r.steps = append(r.steps, oe, readStep) + C.rados_read_op_read( + r.op, + oe.cOffset, + oe.cReadLen, + oe.cBuffer, + readStep.bytesRead, + readStep.prval, + ) + + return readStep +} diff --git a/rados/read_op_read_test.go b/rados/read_op_read_test.go new file mode 100644 index 000000000..1d15b31e3 --- /dev/null +++ b/rados/read_op_read_test.go @@ -0,0 +1,38 @@ +//go:build ceph_preview +// +build ceph_preview + +package rados + +import ( + "github.com/stretchr/testify/assert" +) + +func (suite *RadosTestSuite) TestReadOpRead() { + suite.SetupConnection() + ta := assert.New(suite.T()) + + var ( + oid = "TestReadOpRead" + data = []byte("data to read") + err error + ) + + // Create an object and populate it with data. + op1 := CreateWriteOp() + defer op1.Release() + op1.Create(CreateIdempotent) + op1.WriteFull(data) + err = op1.Operate(suite.ioctx, oid, OperationNoFlag) + ta.NoError(err) + + // Read the object's contents and compare them with expected data. + readBuf := make([]byte, 64) + op2 := CreateReadOp() + defer op2.Release() + readOpRes := op2.Read(0, readBuf) + err = op2.Operate(suite.ioctx, oid, OperationNoFlag) + ta.NoError(err) + ta.Equal(int(0), readOpRes.Result) + ta.Equal(int64(len(data)), readOpRes.BytesRead) + ta.Equal(data, readBuf[:readOpRes.BytesRead]) +} diff --git a/rados/read_step.go b/rados/read_step.go new file mode 100644 index 000000000..732f37b00 --- /dev/null +++ b/rados/read_step.go @@ -0,0 +1,31 @@ +package rados + +// #include +import "C" + +import ( + "unsafe" +) + +type readStep struct { + withoutUpdate + withoutFree + // the c pointer utilizes the Go byteslice data and no free is needed + + // inputs: + b []byte + + // arguments: + cBuffer *C.char + cReadLen C.size_t + cOffset C.uint64_t +} + +func newReadStep(b []byte, offset uint64) *readStep { + return &readStep{ + b: b, + cBuffer: (*C.char)(unsafe.Pointer(&b[0])), // TODO: must be pinned + cReadLen: C.size_t(len(b)), + cOffset: C.uint64_t(offset), + } +}