#include <sstream>
#include <vector>
+#include "common/bit_vector.hpp"
#include "common/errno.h"
#include "objclass/objclass.h"
#include "include/rbd_types.h"
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;
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<unsigned long long>(object_count),
+ static_cast<unsigned long long>(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<uint8_t> 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<unsigned long long>(byte_offset),
+ static_cast<unsigned long long>(byte_length),
+ static_cast<unsigned long long>(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)
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,
::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<uint8_t> ¤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
#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"
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<uint8_t> ¤t_object_state);
+
// class operations on the old format, kept for
// backwards compatability
int old_snapshot_add(librados::IoCtx *ioctx, const std::string &oid,
common/Readahead.h \
common/Cycles.h \
common/Initialize.h \
- common/ContextCompletion.h
+ common/ContextCompletion.h \
+ common/bit_vector.hpp
if ENABLE_XIO
noinst_HEADERS += \
--- /dev/null
+// -*- 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 <contact@redhat.com>
+ *
+ * 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 <stdint.h>
+#include <cmath>
+#include <list>
+#include <boost/static_assert.hpp>
+
+namespace ceph {
+
+template <uint8_t _bit_count>
+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<uint8_t>((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<BitVector *> &o);
+private:
+
+ bufferlist m_data;
+ uint64_t m_size;
+
+ static void compute_index(uint64_t offset, uint64_t *index, uint64_t *shift);
+
+};
+
+template <uint8_t _b>
+BitVector<_b>::BitVector() : m_size(0)
+{
+}
+
+template <uint8_t _b>
+void BitVector<_b>::clear() {
+ m_data.clear();
+ m_size = 0;
+}
+
+template <uint8_t _b>
+void BitVector<_b>::resize(uint64_t size) {
+ uint64_t buffer_size = static_cast<uint64_t>(std::ceil(static_cast<double>(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 <uint8_t _b>
+uint64_t BitVector<_b>::size() const {
+ return m_size;
+}
+
+template <uint8_t _b>
+const bufferlist& BitVector<_b>::get_data() const {
+ return m_data;
+}
+
+template <uint8_t _b>
+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 <uint8_t _b>
+void BitVector<_b>::encode_header(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(m_size, bl);
+ ENCODE_FINISH(bl);
+}
+
+template <uint8_t _b>
+void BitVector<_b>::decode_header(bufferlist::iterator& it) {
+ uint64_t size;
+ DECODE_START(1, it);
+ ::decode(size, it);
+ DECODE_FINISH(it);
+
+ resize(size);
+}
+
+template <uint8_t _b>
+uint64_t BitVector<_b>::get_header_length() const {
+ // 6 byte encoding header, 8 byte size
+ return 14;
+}
+
+template <uint8_t _b>
+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 <uint8_t _b>
+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 <uint8_t _b>
+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 <uint8_t _b>
+void BitVector<_b>::encode(bufferlist& bl) const {
+ encode_header(bl);
+ if (size() > 0) {
+ encode_data(bl, 0, m_data.length());
+ }
+}
+
+template <uint8_t _b>
+void BitVector<_b>::decode(bufferlist::iterator& it) {
+ decode_header(it);
+ decode_data(it, 0);
+}
+
+template <uint8_t _b>
+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 <uint8_t _b>
+bool BitVector<_b>::operator==(const BitVector &b) const {
+ return (this->m_size == b.m_size && this->m_data == b.m_data);
+}
+
+template <uint8_t _b>
+typename BitVector<_b>::Reference BitVector<_b>::operator[](uint64_t offset) {
+ return Reference(*this, offset);
+}
+
+template <uint8_t _b>
+typename BitVector<_b>::ConstReference BitVector<_b>::operator[](uint64_t offset) const {
+ return ConstReference(*this, offset);
+}
+
+template <uint8_t _b>
+BitVector<_b>::ConstReference::ConstReference(const BitVector<_b> &bit_vector,
+ uint64_t offset)
+ : m_bit_vector(bit_vector), m_offset(offset)
+{
+}
+
+template <uint8_t _b>
+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 <uint8_t _b>
+BitVector<_b>::Reference::Reference(BitVector<_b> &bit_vector, uint64_t offset)
+ : m_bit_vector(bit_vector), m_offset(offset)
+{
+}
+
+template <uint8_t _b>
+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 <uint8_t _b>
+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 <uint8_t _b>
+void BitVector<_b>::generate_test_instances(std::list<BitVector *> &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 <uint8_t _b>
+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
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
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)
{
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<uint8_t>());
+ 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();
+}
--- /dev/null
+// -*- 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 <contact@redhat.com>
+ *
+ * LGPL2.1 (see COPYING-LGPL2.1) or later
+ */
+
+#include <gtest/gtest.h>
+#include <cmath>
+#include "common/bit_vector.hpp"
+
+using namespace ceph;
+
+template <uint8_t _bit_count>
+class TestParams {
+public:
+ static const uint8_t BIT_COUNT = _bit_count;
+};
+
+template <typename T>
+class BitVectorTest : public ::testing::Test {
+public:
+ typedef BitVector<T::BIT_COUNT> bit_vector_t;
+};
+
+typedef ::testing::Types<TestParams<2> > 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<uint64_t>(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<uint64_t> 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());
+}
#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)