diff --git a/rados/ioctx.go b/rados/ioctx.go index b428c6f..27b687d 100644 --- a/rados/ioctx.go +++ b/rados/ioctx.go @@ -78,6 +78,19 @@ func (ioctx *IOContext) WriteFull(oid string, data []byte) error { return GetRadosError(ret) } +// Append appends len(data) bytes to the object with key oid. +// The object is appended with the provided data. If the object exists, +// it is atomically appended to. It returns an error, if any. +func (ioctx *IOContext) Append(oid string, data []byte) error { + c_oid := C.CString(oid) + defer C.free(unsafe.Pointer(c_oid)) + + ret := C.rados_append(ioctx.ioctx, c_oid, + (*C.char)(unsafe.Pointer(&data[0])), + (C.size_t)(len(data))) + return GetRadosError(ret) +} + // Read reads up to len(data) bytes from the object with key oid starting at byte // offset offset. It returns the number of bytes read and an error, if any. func (ioctx *IOContext) Read(oid string, data []byte, offset uint64) (int, error) { diff --git a/rados/rados_test.go b/rados/rados_test.go index 051694f..b2b7c70 100644 --- a/rados/rados_test.go +++ b/rados/rados_test.go @@ -321,6 +321,38 @@ func TestReadWrite(t *testing.T) { conn.Shutdown() } +func TestAppend(t *testing.T) { + conn, _ := rados.NewConn() + conn.ReadDefaultConfigFile() + conn.Connect() + + // make pool + pool_name := GetUUID() + err := conn.MakePool(pool_name) + assert.NoError(t, err) + + pool, err := conn.OpenIOContext(pool_name) + assert.NoError(t, err) + + bytes_accum := []byte{} + for _, str_in := range []string{"input", " ", "another", " ", "data"} { + bytes_in := []byte(str_in) + err = pool.Append("obj", bytes_in) + assert.NoError(t, err) + + bytes_accum = append(bytes_accum, bytes_in...) + bytes_out := make([]byte, len(bytes_accum)) + n_out, err := pool.Read("obj", bytes_out, 0) + + assert.NoError(t, err) + assert.Equal(t, n_out, len(bytes_accum)) + assert.Equal(t, bytes_accum, bytes_out) + } + + pool.Destroy() + conn.Shutdown() +} + func TestNotFound(t *testing.T) { conn, _ := rados.NewConn() conn.ReadDefaultConfigFile()