From be5e4cf5912ca38518e7e78c84aa4784064c5e34 Mon Sep 17 00:00:00 2001 From: Robert Vasek Date: Fri, 3 Dec 2021 10:14:58 +0100 Subject: [PATCH] rados: implement binding for rados_read_op_read This commit implements binding for rados_read_op_read RADOS Read operation. Includes a unit test. Signed-off-by: Robert Vasek --- docs/api-status.json | 6 +++ docs/api-status.md | 1 + rados/read_op_read.go | 75 ++++++++++++++++++++++++++++++++++++++ rados/read_op_read_test.go | 38 +++++++++++++++++++ rados/read_step.go | 31 ++++++++++++++++ 5 files changed, 151 insertions(+) create mode 100644 rados/read_op_read.go create mode 100644 rados/read_op_read_test.go create mode 100644 rados/read_step.go diff --git a/docs/api-status.json b/docs/api-status.json index 8fee5c283b..57238853eb 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.12.0", + "expected_stable_version": "v0.14.0" } ] }, diff --git a/docs/api-status.md b/docs/api-status.md index 2f921ea4a9..63731dee20 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.12.0 | v0.14.0 | ## Package: rbd diff --git a/rados/read_op_read.go b/rados/read_op_read.go new file mode 100644 index 0000000000..0c4809360b --- /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 0000000000..1d15b31e39 --- /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 0000000000..732f37b00f --- /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), + } +}