From: Jason Dillaman Date: Thu, 9 Oct 2014 06:51:44 +0000 (-0400) Subject: cls_rbd: Add methods for manipulating an image object map X-Git-Tag: v0.93~143^2~11 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=13fd6d1dd0d16c60e0be0cdc316cb429b694fb52;p=ceph.git cls_rbd: Add methods for manipulating an image object map The object map will track the current state of each object within an RBD image. Signed-off-by: Jason Dillaman --- diff --git a/src/cls/rbd/cls_rbd.cc b/src/cls/rbd/cls_rbd.cc index 866823f024ba..50323e273f4c 100644 --- a/src/cls/rbd/cls_rbd.cc +++ b/src/cls/rbd/cls_rbd.cc @@ -37,6 +37,7 @@ #include #include +#include "common/bit_vector.hpp" #include "common/errno.h" #include "objclass/objclass.h" #include "include/rbd_types.h" @@ -90,6 +91,9 @@ cls_method_handle_t h_dir_list; cls_method_handle_t h_dir_add_image; cls_method_handle_t h_dir_remove_image; cls_method_handle_t h_dir_rename_image; +cls_method_handle_t h_object_map_load; +cls_method_handle_t h_object_map_resize; +cls_method_handle_t h_object_map_update; cls_method_handle_t h_old_snapshots_list; cls_method_handle_t h_old_snapshot_add; cls_method_handle_t h_old_snapshot_remove; @@ -1795,6 +1799,186 @@ int dir_remove_image(cls_method_context_t hctx, bufferlist *in, bufferlist *out) return dir_remove_image_helper(hctx, name, id); } +int object_map_read(cls_method_context_t hctx, BitVector<2> &object_map) +{ + uint64_t size; + int r = cls_cxx_stat(hctx, &size, NULL); + if (r < 0) { + return r; + } + + bufferlist bl; + r = cls_cxx_read(hctx, 0, size, &bl); + if (r < 0) { + return r; + } + + try { + bufferlist::iterator iter = bl.begin(); + ::decode(object_map, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + return 0; +} + +/** + * Load an rbd image's object map + * + * Input: + * none + * + * Output: + * @param object map bit vector + * @returns 0 on success, negative error code on failure + */ +int object_map_load(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + BitVector<2> object_map; + int r = object_map_read(hctx, object_map); + if (r < 0) { + return r; + } + + ::encode(object_map, *out); + return 0; +} + +/** + * Resize an rbd image's object map + * + * Input: + * @param object_count the max number of objects in the image + * @param default_state the default state of newly created objects + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int object_map_resize(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t object_count; + uint8_t default_state; + try { + bufferlist::iterator iter = in->begin(); + ::decode(object_count, iter); + ::decode(default_state, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + BitVector<2> object_map; + int r = object_map_read(hctx, object_map); + if ((r < 0) && (r != -ENOENT)) { + return r; + } + + size_t orig_object_map_size = object_map.size(); + if (orig_object_map_size != object_count) { + object_map.resize(object_count); + for (uint64_t i = orig_object_map_size; i < object_count; ++i) { + object_map[i] = default_state; + } + } + + bufferlist map; + ::encode(object_map, map); + CLS_LOG(20, "object_map_resize: object size=%llu, byte size=%llu", + static_cast(object_count), + static_cast(map.length())); + return cls_cxx_write_full(hctx, &map); +} + +/** + * Update an rbd image's object map + * + * Input: + * @param start_object_no the start object iterator + * @param end_object_no the end object iterator + * @param new_object_state the new object state + * @param current_object_state optional current object state filter + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int object_map_update(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t start_object_no; + uint64_t end_object_no; + uint8_t new_object_state; + boost::optional current_object_state; + try { + bufferlist::iterator iter = in->begin(); + ::decode(start_object_no, iter); + ::decode(end_object_no, iter); + ::decode(new_object_state, iter); + ::decode(current_object_state, iter); + } catch (const buffer::error &err) { + return -EINVAL; + } + + BitVector<2> object_map; + bufferlist header_bl; + int r = cls_cxx_read(hctx, 0, object_map.get_header_length(), &header_bl); + if (r < 0) { + return r; + } + + try { + bufferlist::iterator it = header_bl.begin(); + object_map.decode_header(it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + if (start_object_no >= end_object_no || end_object_no > object_map.size()) { + return -ERANGE; + } + + uint64_t byte_offset; + uint64_t byte_length; + object_map.get_data_extents(start_object_no, + end_object_no - start_object_no, + &byte_offset, &byte_length); + + bufferlist data_bl; + r = cls_cxx_read(hctx, object_map.get_header_length() + byte_offset, + byte_length, &data_bl); + if (r < 0) { + return r; + } + + try { + bufferlist::iterator it = data_bl.begin(); + object_map.decode_data(it, byte_offset); + } catch (const buffer::error &err) { + return -EINVAL; + } + + bool updated = false; + for (uint64_t object_no = start_object_no; object_no < end_object_no; + ++object_no) { + if ((!current_object_state || object_map[object_no] == *current_object_state) && + object_map[object_no] != new_object_state) { + object_map[object_no] = new_object_state; + updated = true; + } + } + + if (updated) { + CLS_LOG(20, "object_map_update: %llu~%llu -> %llu", + static_cast(byte_offset), + static_cast(byte_length), + static_cast(object_map.get_header_length() + + byte_offset)); + + bufferlist update; + object_map.encode_data(update, byte_offset, byte_length); + r = cls_cxx_write(hctx, object_map.get_header_length() + byte_offset, + update.length(), &update); + } + return r; +} + /****************************** Old format *******************************/ int old_snapshots_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out) @@ -2090,6 +2274,17 @@ void __cls_init() CLS_METHOD_RD | CLS_METHOD_WR, dir_rename_image, &h_dir_rename_image); + /* methods for the rbd_object_map.$image_id object */ + cls_register_cxx_method(h_class, "object_map_load", + CLS_METHOD_RD, + object_map_load, &h_object_map_load); + cls_register_cxx_method(h_class, "object_map_resize", + CLS_METHOD_RD | CLS_METHOD_WR, + object_map_resize, &h_object_map_resize); + cls_register_cxx_method(h_class, "object_map_update", + CLS_METHOD_RD | CLS_METHOD_WR, + object_map_update, &h_object_map_update); + /* methods for the old format */ cls_register_cxx_method(h_class, "snap_list", CLS_METHOD_RD, diff --git a/src/cls/rbd/cls_rbd_client.cc b/src/cls/rbd/cls_rbd_client.cc index 66dc03b3fdfe..aa91178fbf47 100644 --- a/src/cls/rbd/cls_rbd_client.cc +++ b/src/cls/rbd/cls_rbd_client.cc @@ -655,5 +655,47 @@ namespace librbd { ::encode(id, in); return ioctx->exec(oid, "rbd", "dir_rename_image", in, out); } + + int object_map_load(librados::IoCtx *ioctx, const std::string &oid, + ceph::BitVector<2> *object_map) + { + bufferlist in; + bufferlist out; + int r = ioctx->exec(oid, "rbd", "object_map_load", in, out); + if (r < 0) { + return r; + } + + try { + bufferlist::iterator iter = out.begin(); + ::decode(*object_map, iter); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; + } + + void object_map_resize(librados::ObjectWriteOperation *rados_op, + uint64_t object_count, uint8_t default_state) + { + bufferlist in; + ::encode(object_count, in); + ::encode(default_state, in); + rados_op->exec("rbd", "object_map_resize", in); + } + + void object_map_update(librados::ObjectWriteOperation *rados_op, + uint64_t start_object_no, uint64_t end_object_no, + uint8_t new_object_state, + const boost::optional ¤t_object_state) + { + bufferlist in; + ::encode(start_object_no, in); + ::encode(end_object_no, in); + ::encode(new_object_state, in); + ::encode(current_object_state, in); + rados_op->exec("rbd", "object_map_update", in); + } + } // namespace cls_client } // namespace librbd diff --git a/src/cls/rbd/cls_rbd_client.h b/src/cls/rbd/cls_rbd_client.h index 19f0b2dabce2..131b03099469 100644 --- a/src/cls/rbd/cls_rbd_client.h +++ b/src/cls/rbd/cls_rbd_client.h @@ -5,6 +5,7 @@ #define CEPH_LIBRBD_CLS_RBD_CLIENT_H #include "cls/lock/cls_lock_types.h" +#include "common/bit_vector.hpp" #include "common/snap_types.h" #include "include/rados/librados.hpp" #include "include/types.h" @@ -101,6 +102,16 @@ namespace librbd { const std::string &src, const std::string &dest, const std::string &id); + // operations on the rbd_object_map.$image_id object + int object_map_load(librados::IoCtx *ioctx, const std::string &oid, + ceph::BitVector<2> *object_map); + void object_map_resize(librados::ObjectWriteOperation *rados_op, + uint64_t object_count, uint8_t default_state); + void object_map_update(librados::ObjectWriteOperation *rados_op, + uint64_t start_object_no, uint64_t end_object_no, + uint8_t new_object_state, + const boost::optional ¤t_object_state); + // class operations on the old format, kept for // backwards compatability int old_snapshot_add(librados::IoCtx *ioctx, const std::string &oid, diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 07a7f1c77ee9..621a10a4f625 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -227,7 +227,8 @@ noinst_HEADERS += \ common/Readahead.h \ common/Cycles.h \ common/Initialize.h \ - common/ContextCompletion.h + common/ContextCompletion.h \ + common/bit_vector.hpp if ENABLE_XIO noinst_HEADERS += \ diff --git a/src/common/bit_vector.hpp b/src/common/bit_vector.hpp new file mode 100644 index 000000000000..5c8e20f99c20 --- /dev/null +++ b/src/common/bit_vector.hpp @@ -0,0 +1,311 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2014 Red Hat + * + * LGPL2.1 (see COPYING-LGPL2.1) or later + */ + +#ifndef BIT_VECTOR_HPP +#define BIT_VECTOR_HPP + +#include "common/Formatter.h" +#include "include/assert.h" +#include "include/buffer.h" +#include "include/encoding.h" +#include +#include +#include +#include + +namespace ceph { + +template +class BitVector +{ +private: + static const uint8_t BITS_PER_BYTE = 8; + static const uint32_t ELEMENTS_PER_BLOCK = BITS_PER_BYTE / _bit_count; + static const uint8_t MASK = static_cast((1 << _bit_count) - 1); + + // must be power of 2 + BOOST_STATIC_ASSERT((_bit_count != 0) && !(_bit_count & (_bit_count - 1))); + BOOST_STATIC_ASSERT(_bit_count <= BITS_PER_BYTE); +public: + + class ConstReference { + public: + operator uint8_t() const; + private: + friend class BitVector; + const BitVector &m_bit_vector; + uint64_t m_offset; + + ConstReference(const BitVector &bit_vector, uint64_t offset); + }; + + class Reference { + public: + operator uint8_t() const; + Reference& operator=(uint8_t v); + private: + friend class BitVector; + BitVector &m_bit_vector; + uint64_t m_offset; + + Reference(BitVector &bit_vector, uint64_t offset); + }; + + static const uint8_t BIT_COUNT = _bit_count; + + BitVector(); + + void clear(); + + void resize(uint64_t elements); + uint64_t size() const; + + const bufferlist& get_data() const; + + Reference operator[](uint64_t offset); + ConstReference operator[](uint64_t offset) const; + + void encode_header(bufferlist& bl) const; + void decode_header(bufferlist::iterator& it); + uint64_t get_header_length() const; + + void encode_data(bufferlist& bl, uint64_t byte_offset, + uint64_t byte_length) const; + void decode_data(bufferlist::iterator& it, uint64_t byte_offset); + void get_data_extents(uint64_t offset, uint64_t length, + uint64_t *byte_offset, uint64_t *byte_length) const; + + void encode(bufferlist& bl) const; + void decode(bufferlist::iterator& it); + void dump(Formatter *f) const; + + bool operator==(const BitVector &b) const; + + static void generate_test_instances(std::list &o); +private: + + bufferlist m_data; + uint64_t m_size; + + static void compute_index(uint64_t offset, uint64_t *index, uint64_t *shift); + +}; + +template +BitVector<_b>::BitVector() : m_size(0) +{ +} + +template +void BitVector<_b>::clear() { + m_data.clear(); + m_size = 0; +} + +template +void BitVector<_b>::resize(uint64_t size) { + uint64_t buffer_size = static_cast(std::ceil(static_cast(size) / + ELEMENTS_PER_BLOCK)); + if (buffer_size > m_data.length()) { + m_data.append_zero(buffer_size - m_data.length()); + } else if (buffer_size < m_data.length()) { + bufferlist bl; + bl.substr_of(m_data, 0, buffer_size); + bl.swap(m_data); + } + m_size = size; +} + +template +uint64_t BitVector<_b>::size() const { + return m_size; +} + +template +const bufferlist& BitVector<_b>::get_data() const { + return m_data; +} + +template +void BitVector<_b>::compute_index(uint64_t offset, uint64_t *index, uint64_t *shift) { + *index = offset / ELEMENTS_PER_BLOCK; + *shift = ((ELEMENTS_PER_BLOCK - 1) - (offset % ELEMENTS_PER_BLOCK)) * _b; +} + +template +void BitVector<_b>::encode_header(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(m_size, bl); + ENCODE_FINISH(bl); +} + +template +void BitVector<_b>::decode_header(bufferlist::iterator& it) { + uint64_t size; + DECODE_START(1, it); + ::decode(size, it); + DECODE_FINISH(it); + + resize(size); +} + +template +uint64_t BitVector<_b>::get_header_length() const { + // 6 byte encoding header, 8 byte size + return 14; +} + +template +void BitVector<_b>::encode_data(bufferlist& bl, uint64_t byte_offset, + uint64_t byte_length) const { + bufferlist bit; + bit.substr_of(m_data, byte_offset, byte_length); + bl.append(bit); +} + +template +void BitVector<_b>::decode_data(bufferlist::iterator& it, uint64_t byte_offset) { + if (byte_offset + it.get_remaining() > m_data.length()) { + throw buffer::malformed_input("attempting to decode past end of buffer"); + } + + char* packed_data = m_data.c_str(); + for (; !it.end(); ++it) { + packed_data[byte_offset++] = *it; + } +} + +template +void BitVector<_b>::get_data_extents(uint64_t offset, uint64_t length, + uint64_t *byte_offset, + uint64_t *byte_length) const { + assert(length > 0); + uint64_t shift; + compute_index(offset, byte_offset, &shift); + + uint64_t end_offset; + compute_index(offset + length - 1, &end_offset, &shift); + assert(*byte_offset <= end_offset); + + *byte_length = end_offset - *byte_offset + 1; +} + +template +void BitVector<_b>::encode(bufferlist& bl) const { + encode_header(bl); + if (size() > 0) { + encode_data(bl, 0, m_data.length()); + } +} + +template +void BitVector<_b>::decode(bufferlist::iterator& it) { + decode_header(it); + decode_data(it, 0); +} + +template +void BitVector<_b>::dump(Formatter *f) const { + f->dump_unsigned("size", m_size); + f->open_array_section("bit_table"); + for (unsigned i = 0; i < m_data.length(); ++i) { + f->dump_format("byte", "0x%02hhX", m_data[i]); + } + f->close_section(); +} + +template +bool BitVector<_b>::operator==(const BitVector &b) const { + return (this->m_size == b.m_size && this->m_data == b.m_data); +} + +template +typename BitVector<_b>::Reference BitVector<_b>::operator[](uint64_t offset) { + return Reference(*this, offset); +} + +template +typename BitVector<_b>::ConstReference BitVector<_b>::operator[](uint64_t offset) const { + return ConstReference(*this, offset); +} + +template +BitVector<_b>::ConstReference::ConstReference(const BitVector<_b> &bit_vector, + uint64_t offset) + : m_bit_vector(bit_vector), m_offset(offset) +{ +} + +template +BitVector<_b>::ConstReference::operator uint8_t() const { + uint64_t index; + uint64_t shift; + this->m_bit_vector.compute_index(this->m_offset, &index, &shift); + + return (this->m_bit_vector.m_data[index] >> shift) & MASK; +} + +template +BitVector<_b>::Reference::Reference(BitVector<_b> &bit_vector, uint64_t offset) + : m_bit_vector(bit_vector), m_offset(offset) +{ +} + +template +BitVector<_b>::Reference::operator uint8_t() const { + uint64_t index; + uint64_t shift; + this->m_bit_vector.compute_index(this->m_offset, &index, &shift); + + return (this->m_bit_vector.m_data[index] >> shift) & MASK; +} + +template +typename BitVector<_b>::Reference& BitVector<_b>::Reference::operator=(uint8_t v) { + uint64_t index; + uint64_t shift; + this->m_bit_vector.compute_index(this->m_offset, &index, &shift); + + // TODO: find out why bufferlist doesn't support char& operator[]() + uint8_t mask = MASK << shift; + char* packed_data = this->m_bit_vector.m_data.c_str(); + uint8_t packed_value = (packed_data[index] & ~mask) | ((v << shift) & mask); + packed_data[index] = packed_value; + return *this; +} + +template +void BitVector<_b>::generate_test_instances(std::list &o) { + o.push_back(new BitVector()); + + BitVector *b = new BitVector(); + const uint64_t radix = 1 << b->BIT_COUNT; + const uint64_t size = 1024; + + b->resize(size); + for (uint64_t i = 0; i < size; ++i) { + (*b)[i] = rand() % radix; + } + o.push_back(b); +} + +} + +WRITE_CLASS_ENCODER(ceph::BitVector<2>) + +template +inline std::ostream& operator<<(std::ostream& out, const ceph::BitVector<_b> &b) +{ + out << "ceph::BitVector<" << _b << ">(size=" << b.size() << ", data=" + << b.get_data() << ")"; + return out; +} + +#endif // BIT_VECTOR_HPP diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 78aca98e1cb3..3da028fd29e6 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -669,6 +669,11 @@ unittest_tableformatter_CXXFLAGS = $(UNITTEST_CXXFLAGS) unittest_tableformatter_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL) check_PROGRAMS += unittest_tableformatter +unittest_bit_vector_SOURCES = test/common/test_bit_vector.cc +unittest_bit_vector_CXXFLAGS = $(UNITTEST_CXXFLAGS) +unittest_bit_vector_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL) +check_PROGRAMS += unittest_bit_vector + check_SCRIPTS += test/pybind/test_ceph_argparse.py if WITH_RADOSGW diff --git a/src/test/cls_rbd/test_cls_rbd.cc b/src/test/cls_rbd/test_cls_rbd.cc index bef5fe141b54..663c9332bc2f 100644 --- a/src/test/cls_rbd/test_cls_rbd.cc +++ b/src/test/cls_rbd/test_cls_rbd.cc @@ -49,6 +49,9 @@ using ::librbd::cls_client::get_stripe_unit_count; using ::librbd::cls_client::set_stripe_unit_count; using ::librbd::cls_client::old_snapshot_add; using ::librbd::cls_client::get_mutable_metadata; +using ::librbd::cls_client::object_map_load; +using ::librbd::cls_client::object_map_resize; +using ::librbd::cls_client::object_map_update; static char *random_buf(size_t len) { @@ -923,3 +926,98 @@ TEST_F(TestClsRbd, get_mutable_metadata_features) ioctx.close(); } + +TEST_F(TestClsRbd, object_map_resize) +{ + librados::IoCtx ioctx; + ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx)); + + string oid = get_temp_image_name(); + BitVector<2> ref_bit_vector; + ref_bit_vector.resize(32); + for (uint64_t i = 0; i < ref_bit_vector.size(); ++i) { + ref_bit_vector[i] = 1; + } + + librados::ObjectWriteOperation op1; + object_map_resize(&op1, ref_bit_vector.size(), 1); + ASSERT_EQ(0, ioctx.operate(oid, &op1)); + + BitVector<2> osd_bit_vector; + ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector)); + ASSERT_EQ(ref_bit_vector, osd_bit_vector); + + ref_bit_vector.resize(64); + for (uint64_t i = 32; i < ref_bit_vector.size(); ++i) { + ref_bit_vector[i] = 2; + } + + librados::ObjectWriteOperation op2; + object_map_resize(&op2, ref_bit_vector.size(), 2); + ASSERT_EQ(0, ioctx.operate(oid, &op2)); + ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector)); + ASSERT_EQ(ref_bit_vector, osd_bit_vector); + + ref_bit_vector.resize(16); + + librados::ObjectWriteOperation op3; + object_map_resize(&op3, ref_bit_vector.size(), 1); + ASSERT_EQ(0, ioctx.operate(oid, &op3)); + ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector)); + ASSERT_EQ(ref_bit_vector, osd_bit_vector); + + ioctx.close(); +} + +TEST_F(TestClsRbd, object_map_update) +{ + librados::IoCtx ioctx; + ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx)); + + string oid = get_temp_image_name(); + BitVector<2> ref_bit_vector; + ref_bit_vector.resize(16); + for (uint64_t i = 0; i < ref_bit_vector.size(); ++i) { + ref_bit_vector[i] = 2; + } + + BitVector<2> osd_bit_vector; + + librados::ObjectWriteOperation op1; + object_map_resize(&op1, ref_bit_vector.size(), 2); + ASSERT_EQ(0, ioctx.operate(oid, &op1)); + ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector)); + ASSERT_EQ(ref_bit_vector, osd_bit_vector); + + ref_bit_vector[7] = 1; + ref_bit_vector[8] = 1; + + librados::ObjectWriteOperation op2; + object_map_update(&op2, 7, 9, 1, boost::optional()); + ASSERT_EQ(0, ioctx.operate(oid, &op2)); + ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector)); + ASSERT_EQ(ref_bit_vector, osd_bit_vector); + + ref_bit_vector[7] = 3; + ref_bit_vector[8] = 3; + + librados::ObjectWriteOperation op3; + object_map_update(&op3, 6, 10, 3, 1); + ASSERT_EQ(0, ioctx.operate(oid, &op3)); + ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector)); + ASSERT_EQ(ref_bit_vector, osd_bit_vector); + + ioctx.close(); +} + +TEST_F(TestClsRbd, object_map_load_enoent) +{ + librados::IoCtx ioctx; + ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx)); + + string oid = get_temp_image_name(); + BitVector<2> osd_bit_vector; + ASSERT_EQ(-ENOENT, object_map_load(&ioctx, oid, &osd_bit_vector)); + + ioctx.close(); +} diff --git a/src/test/common/test_bit_vector.cc b/src/test/common/test_bit_vector.cc new file mode 100644 index 000000000000..ad6c243849e9 --- /dev/null +++ b/src/test/common/test_bit_vector.cc @@ -0,0 +1,110 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2014 Red Hat + * + * LGPL2.1 (see COPYING-LGPL2.1) or later + */ + +#include +#include +#include "common/bit_vector.hpp" + +using namespace ceph; + +template +class TestParams { +public: + static const uint8_t BIT_COUNT = _bit_count; +}; + +template +class BitVectorTest : public ::testing::Test { +public: + typedef BitVector bit_vector_t; +}; + +typedef ::testing::Types > BitVectorTypes; +TYPED_TEST_CASE(BitVectorTest, BitVectorTypes); + +TYPED_TEST(BitVectorTest, resize) { + typename TestFixture::bit_vector_t bit_vector; + + size_t size = 2357; + + double elements_per_byte = 8 / bit_vector.BIT_COUNT; + + bit_vector.resize(size); + ASSERT_EQ(bit_vector.size(), size); + ASSERT_EQ(bit_vector.get_data().length(), static_cast(std::ceil( + size / elements_per_byte))); +} + +TYPED_TEST(BitVectorTest, clear) { + typename TestFixture::bit_vector_t bit_vector; + + bit_vector.resize(123); + bit_vector.clear(); + ASSERT_EQ(0ull, bit_vector.size()); + ASSERT_EQ(0ull, bit_vector.get_data().length()); +} + +TYPED_TEST(BitVectorTest, bit_order) { + typename TestFixture::bit_vector_t bit_vector; + bit_vector.resize(1); + + uint8_t value = 1; + bit_vector[0] = value; + + value <<= (8 - bit_vector.BIT_COUNT); + ASSERT_EQ(value, bit_vector.get_data()[0]); +} + +TYPED_TEST(BitVectorTest, get_set) { + typename TestFixture::bit_vector_t bit_vector; + std::vector ref; + + uint64_t radix = 1 << bit_vector.BIT_COUNT; + + size_t size = 1024; + bit_vector.resize(size); + ref.resize(size); + for (size_t i = 0; i < size; ++i) { + uint64_t v = rand() % radix; + ref[i] = v; + bit_vector[i] = v; + } + + const typename TestFixture::bit_vector_t &const_bit_vector(bit_vector); + for (size_t i = 0; i < size; ++i) { + ASSERT_EQ(ref[i], bit_vector[i]); + ASSERT_EQ(ref[i], const_bit_vector[i]); + } +} + +TYPED_TEST(BitVectorTest, get_buffer_extents) { + typename TestFixture::bit_vector_t bit_vector; + + uint64_t offset = 5381; + uint64_t length = 4111; + uint64_t byte_offset; + uint64_t byte_length; + bit_vector.get_data_extents(offset, length, &byte_offset, &byte_length); + + uint64_t elements_per_byte = 8 / bit_vector.BIT_COUNT; + uint64_t start_byte = offset / elements_per_byte; + ASSERT_EQ(start_byte, byte_offset); + + uint64_t end_byte = (offset + length - 1) / elements_per_byte; + ASSERT_EQ(end_byte - start_byte + 1, byte_length); +} + +TYPED_TEST(BitVectorTest, get_header_length) { + typename TestFixture::bit_vector_t bit_vector; + + bufferlist bl; + bit_vector.encode_header(bl); + ASSERT_EQ(bl.length(), bit_vector.get_header_length()); +} diff --git a/src/test/encoding/types.h b/src/test/encoding/types.h index 684364e1841a..bc4fe33e4424 100644 --- a/src/test/encoding/types.h +++ b/src/test/encoding/types.h @@ -4,6 +4,9 @@ TYPE(CompatSet) #include "include/filepath.h" TYPE(filepath) +#include "common/bit_vector.hpp" +TYPE(BitVector<2>) + #include "common/bloom_filter.hpp" TYPE(bloom_filter) TYPE(compressible_bloom_filter)