diff --git a/internal/cutil/aliases.go b/internal/cutil/aliases.go index 309c19dab..70a786ff3 100644 --- a/internal/cutil/aliases.go +++ b/internal/cutil/aliases.go @@ -21,8 +21,14 @@ const ( // SizeTSize is the size of C.size_t SizeTSize = C.sizeof_size_t + + // IntSize is the size of C.int + IntSize = C.sizeof_int ) +// Compile-time assertion ensuring that Go's `int` is at least as large as C's. +const _ = unsafe.Sizeof(int(0)) - unsafe.Sizeof(C.int(0)) + // SizeT wraps size_t from C. type SizeT C.size_t diff --git a/rados/write_op_preview.go b/rados/write_op_preview.go new file mode 100644 index 000000000..4c7bae3c2 --- /dev/null +++ b/rados/write_op_preview.go @@ -0,0 +1,65 @@ +//go:build ceph_preview +// +build ceph_preview + +package rados + +// #cgo LDFLAGS: -lrados +// #include +// #include +// +import "C" + +import ( + "unsafe" + + "github.com/ceph/go-ceph/internal/cutil" +) + +// WriteOpCmpExtStep holds result of the CmpExt write operation. +// Result is valid only after Operate() was called. +type WriteOpCmpExtStep struct { + // C returned data: + prval *C.int + + // Result of the CmpExt write operation. + Result int +} + +func (s *WriteOpCmpExtStep) update() error { + s.Result = (int)(*s.prval) + return nil +} + +func (s *WriteOpCmpExtStep) free() { + C.free(unsafe.Pointer(s.prval)) + s.prval = nil +} + +func newWriteOpCmpExtStep() *WriteOpCmpExtStep { + return &WriteOpCmpExtStep{ + prval: (*C.int)(C.malloc(cutil.IntSize)), + } +} + +// CmpExt ensures that given object range (extent) satisfies comparison. +// PREVIEW +// +// Implements: +// void rados_write_op_cmpext(rados_write_op_t write_op, +// const char * cmp_buf, +// size_t cmp_len, +// uint64_t off, +// int * prval); +func (w *WriteOp) CmpExt(b []byte, offset uint64) *WriteOpCmpExtStep { + oe := newWriteStep(b, 0, offset) + cmpExtStep := newWriteOpCmpExtStep() + w.steps = append(w.steps, oe, cmpExtStep) + C.rados_write_op_cmpext( + w.op, + oe.cBuffer, + oe.cDataLen, + oe.cOffset, + cmpExtStep.prval) + + return cmpExtStep +} diff --git a/rados/write_op_preview_test.go b/rados/write_op_preview_test.go new file mode 100644 index 000000000..b6319db2e --- /dev/null +++ b/rados/write_op_preview_test.go @@ -0,0 +1,40 @@ +//go:build ceph_preview +// +build ceph_preview + +package rados + +import ( + "github.com/stretchr/testify/assert" +) + +func (suite *RadosTestSuite) TestWriteOpCmpExt() { + suite.SetupConnection() + ta := assert.New(suite.T()) + + oid := "TestWriteOpCmpExt" + data := []byte("compare this") + + // Create an object and populate it with data. + op1 := CreateWriteOp() + defer op1.Release() + op1.Create(CreateIdempotent) + op1.WriteFull([]byte(data)) + err := op1.Operate(suite.ioctx, oid, OperationNoFlag) + ta.NoError(err) + + // Compare contents of the object. Should succeed. + op2 := CreateWriteOp() + defer op2.Release() + cmpExtRes1 := op2.CmpExt(data, 0) + err = op2.Operate(suite.ioctx, oid, OperationNoFlag) + ta.NoError(err) + ta.Equal(cmpExtRes1.Result, int(0)) + + // Compare contents of the object. Should fail. + op3 := CreateWriteOp() + defer op3.Release() + cmpExtRes2 := op3.CmpExt([]byte("xxx"), 0) + err = op3.Operate(suite.ioctx, oid, OperationNoFlag) + ta.Error(err) + ta.NotEqual(cmpExtRes2.Result, int(0)) +}