This commit is contained in:
Joe Thornber 2013-03-14 13:13:18 +00:00
parent add23c1709
commit 12dff31358
6 changed files with 477 additions and 16 deletions

View File

@ -56,11 +56,13 @@ namespace persistent_data {
struct array_dim {
array_dim(unsigned nr_entries, unsigned entries_per_block)
: nr_full_blocks(nr_entries / entries_per_block),
nr_entries_in_last_block(nr_entries % entries_per_block) {
nr_entries_in_last_block(nr_entries % entries_per_block),
nr_total_blocks(nr_full_blocks + (nr_entries_in_last_block ? 1 : 0)) {
}
unsigned nr_full_blocks;
unsigned nr_entries_in_last_block;
unsigned nr_total_blocks;
};
unsigned calc_max_entries(size_t value_size, size_t block_size)
@ -143,8 +145,8 @@ namespace persistent_data {
array(tm_ptr tm,
typename ValueTraits::ref_counter rc,
unsigned nr_entries,
block_address root)
block_address root,
unsigned nr_entries)
: tm_(tm),
entries_per_block_(rblock::calc_max_entries()),
nr_entries_(nr_entries),
@ -207,8 +209,10 @@ namespace persistent_data {
else if (old_dim_.nr_entries_in_last_block > 0)
grow_extend_tail_block(new_dim_.nr_entries_in_last_block);
else
else if (new_dim_.nr_entries_in_last_block)
grow_add_tail_block();
a_.nr_entries_ = new_nr_entries;
}
private:
@ -230,17 +234,16 @@ namespace persistent_data {
if (old_dim_.nr_entries_in_last_block > 0)
grow_extend_tail_block(entries_per_block_);
insert_full_ablocks(old_dim_.nr_full_blocks + (old_dim_.nr_entries_in_last_block ? 1 : 0),
new_dim_.nr_full_blocks);
insert_full_ablocks(old_dim_.nr_total_blocks, new_dim_.nr_full_blocks);
if (new_dim_.nr_entries_in_last_block > 0)
grow_add_tail_block();
}
void grow_extend_tail_block(unsigned new_nr_entries) {
uint64_t last_block = div_up<uint64_t>(a_.nr_entries_, entries_per_block_);
uint64_t last_block = a_.nr_entries_ / entries_per_block_;
wblock b = a_.shadow_ablock(last_block);
b.grow(new_nr_entries % entries_per_block_, v_);
b.grow(new_nr_entries, v_);
}
array<ValueTraits> &a_;

View File

@ -0,0 +1,167 @@
// Copyright (C) 2013 Red Hat, Inc. All rights reserved.
//
// This file is part of the thin-provisioning-tools source.
//
// thin-provisioning-tools is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// thin-provisioning-tools is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with thin-provisioning-tools. If not, see
// <http://www.gnu.org/licenses/>.
#ifndef BITSET_H
#define BITSET_H
#include "persistent-data/math_utils.h"
#include "persistent-data/data-structures/array.h"
//----------------------------------------------------------------
namespace persistent_data {
namespace bitset_detail {
struct bitset_traits {
typedef base::__le64 disk_type;
typedef uint64_t value_type;
typedef no_op_ref_counter<uint64_t> ref_counter;
static void unpack(disk_type const &disk, value_type &value) {
value = base::to_cpu<uint64_t>(disk);
}
static void pack(value_type const &value, disk_type &disk) {
disk = base::to_disk<base::__le64>(value);
}
};
}
class bitset {
public:
typedef boost::shared_ptr<bitset> ptr;
typedef typename persistent_data::transaction_manager::ptr tm_ptr;
bitset(tm_ptr tm)
: nr_bits_(0),
array_(tm, rc_) {
}
bitset(tm_ptr tm, block_address root, unsigned nr_bits)
: nr_bits_(nr_bits),
array_(tm, rc_, root, nr_bits) {
}
block_address get_root() const {
return array_.get_root();
}
void grow(unsigned new_nr_bits, bool default_value) {
pad_last_block(default_value);
resize_array(new_nr_bits, default_value);
nr_bits_ = new_nr_bits;
}
void destroy();
// May trigger a flush, so cannot be const
bool get(unsigned n) {
check_bounds(n);
return get_bit(array_.get(word(n)), bit(n));
}
void set(unsigned n, bool value) {
check_bounds(n);
unsigned w_index = word(n);
uint64_t w = array_.get(w_index);
if (value)
w = set_bit(w, bit(n));
else
w = clear_bit(w, bit(n));
array_.set(w_index, w);
}
void flush() {
}
private:
void pad_last_block(bool default_value) {
// Set defaults in the final word
if (bit(nr_bits_)) {
unsigned w_index = word(nr_bits_);
uint64_t w = array_.get(w_index);
for (unsigned b = bit(nr_bits_); b < 64; b++)
if (default_value)
w = set_bit(w, b);
else
w = clear_bit(w, b);
array_.set(w_index, w);
}
}
void resize_array(unsigned new_nr_bits, bool default_value) {
unsigned old_nr_words = words_needed(nr_bits_);
unsigned new_nr_words = words_needed(new_nr_bits);
if (new_nr_words < old_nr_words)
throw runtime_error("bitset grow actually asked to shrink");
if (new_nr_words > old_nr_words)
array_.grow(new_nr_words, default_value ? ~0 : 0);
}
unsigned words_needed(unsigned nr_bits) const {
return base::div_up<unsigned>(nr_bits, 64u);
}
unsigned word(unsigned bit) const {
return bit / 64;
}
uint64_t mask(unsigned bit) const {
return 1ull << bit;
}
bool get_bit(uint64_t w, unsigned bit) const {
return w & mask(bit);
}
uint64_t set_bit(uint64_t w, unsigned bit) const {
return w | mask(bit);
}
uint64_t clear_bit(uint64_t w, unsigned bit) const {
return w & (~mask(bit));
}
unsigned bit(unsigned bit) const {
return bit % 64;
}
// The last word may be only partially full, so we have to
// do our own bounds checking rather than relying on array
// to do it.
void check_bounds(unsigned n) const {
if (n >= nr_bits_) {
std::ostringstream str;
str << "bitset index out of bounds ("
<< n << " >= " << nr_bits_ << endl;
throw runtime_error(str.str());
}
}
unsigned nr_bits_;
no_op_ref_counter<uint64_t> rc_;
array<bitset_detail::bitset_traits> array_;
};
}
//----------------------------------------------------------------
#endif

View File

@ -19,6 +19,7 @@
TEST_SOURCE=\
unit-tests/array_block_t.cc \
unit-tests/array_t.cc \
unit-tests/bitset_t.cc \
unit-tests/buffer_t.cc \
unit-tests/cache_t.cc \
unit-tests/block_t.cc \
@ -40,6 +41,9 @@ unit-tests/array_t: unit-tests/array_t.o $(OBJECTS)
unit-tests/array_block_t: unit-tests/array_block_t.o $(OBJECTS)
g++ $(CXXFLAGS) $(INCLUDES) -o $@ $+ $(LIBS) $(LIBEXPAT)
unit-tests/bitset_t: unit-tests/bitset_t.o $(OBJECTS)
g++ $(CXXFLAGS) $(INCLUDES) -o $@ $+ $(LIBS) $(LIBEXPAT)
unit-tests/buffer_t: unit-tests/buffer_t.o $(OBJECTS)
g++ $(CXXFLAGS) $(INCLUDES) -o $@ $+ $(LIBS) $(LIBEXPAT)

View File

@ -153,6 +153,8 @@ BOOST_AUTO_TEST_CASE(can_create_an_empty_array)
BOOST_AUTO_TEST_CASE(read_only_array_blocks_are_possible)
{
unsigned const COUNT = 10;
block_address loc;
transaction_manager::ptr tm = create_tm();
@ -167,22 +169,76 @@ BOOST_AUTO_TEST_CASE(read_only_array_blocks_are_possible)
BOOST_CHECK_THROW(b.get(0), runtime_error);
BOOST_CHECK_THROW(b.set(0, 12345LL), runtime_error);
b.grow(COUNT, 0);
for (unsigned i = 0; i < COUNT; i++)
b.set(i, i);
}
{
ablock64_r b = read_array_block(tm, loc);
BOOST_CHECK_EQUAL(b.nr_entries(), COUNT);
BOOST_CHECK_EQUAL(b.value_size(), sizeof(uint64_t));
BOOST_CHECK_EQUAL(b.max_entries(), (4096 - 24) / 8);
for (unsigned i = 0; i < COUNT; i++)
BOOST_CHECK_EQUAL(b.get(i), i);
}
}
BOOST_AUTO_TEST_CASE(updating_reopened_array_block)
{
unsigned const COUNT = 10;
block_address loc;
transaction_manager::ptr tm = create_tm();
{
pair<ablock64, block_address> p = new_array_block(tm);
ablock64 &b = p.first;
loc = p.second;
BOOST_CHECK_EQUAL(b.nr_entries(), 0);
BOOST_CHECK_EQUAL(b.value_size(), sizeof(uint64_t));
BOOST_CHECK_EQUAL(b.max_entries(), (4096 - 24) / 8);
BOOST_CHECK_THROW(b.get(0), runtime_error);
BOOST_CHECK_THROW(b.set(0, 12345LL), runtime_error);
// Compile time error as expected
// BOOST_CHECK_THROW(b.set(0, 12345LL), runtime_error);
b.grow(COUNT, 0);
for (unsigned i = 0; i < COUNT; i++)
b.set(i, i);
}
{
ablock64 b = open_array_block(tm, loc);
BOOST_CHECK_EQUAL(b.nr_entries(), COUNT);
BOOST_CHECK_EQUAL(b.value_size(), sizeof(uint64_t));
BOOST_CHECK_EQUAL(b.max_entries(), (4096 - 24) / 8);
for (unsigned i = 0; i < COUNT; i++)
BOOST_CHECK_EQUAL(b.get(i), i);
for (unsigned i = 0; i < COUNT; i++)
b.set(i, i + 37);
}
{
ablock64_r b = read_array_block(tm, loc);
BOOST_CHECK_EQUAL(b.nr_entries(), COUNT);
BOOST_CHECK_EQUAL(b.value_size(), sizeof(uint64_t));
BOOST_CHECK_EQUAL(b.max_entries(), (4096 - 24) / 8);
for (unsigned i = 0; i < COUNT; i++)
BOOST_CHECK_EQUAL(b.get(i), i + 37);
}
}
BOOST_AUTO_TEST_CASE(growing)
{
uint64_t default_value = 123, new_value = 234;
@ -203,6 +259,18 @@ BOOST_AUTO_TEST_CASE(growing)
}
}
BOOST_AUTO_TEST_CASE(grow_does_not_touch_existing_values)
{
transaction_manager::ptr tm = create_tm();
pair<ablock64, block_address> p = new_array_block(tm);
ablock64 &b = p.first;
b.grow(10, 123);
BOOST_CHECK_EQUAL(b.get(9), 123);
b.grow(20, 234);
BOOST_CHECK_EQUAL(b.get(9), 123);
}
BOOST_AUTO_TEST_CASE(shrinking)
{
uint64_t default_value = 123;

View File

@ -20,6 +20,8 @@
#include "persistent-data/space-maps/core.h"
#include "persistent-data/data-structures/array.h"
#include <vector>
#define BOOST_TEST_MODULE ArrayTests
#include <boost/test/included/unit_test.hpp>
@ -52,6 +54,14 @@ namespace {
return a;
}
typename array64::ptr
open_array(block_address root, unsigned nr_entries) {
uint64_traits::ref_counter rc;
typename array64::ptr a(new array64(create_tm(), rc, root, nr_entries));
return a;
}
}
//----------------------------------------------------------------
@ -96,6 +106,7 @@ unsigned array_size(T (&)[size]) {
BOOST_AUTO_TEST_CASE(grow)
{
unsigned const COUNT = 10000;
unsigned const STEPS[] = {
17, 71, 137, 277, 439, 683, 967
};
@ -103,20 +114,56 @@ BOOST_AUTO_TEST_CASE(grow)
for (unsigned s = 0; s < array_size(STEPS); s++) {
unsigned step = STEPS[s];
cerr << "testing grow with step size " << step << endl;
unsigned const COUNT = 10000;
vector<unsigned> chunks;
for (unsigned c = 0; c < COUNT; c += step)
chunks.push_back(c);
chunks.push_back(COUNT);
array<uint64_traits>::ptr a = create_array(0, 123);
for (unsigned i = 0; i < COUNT; i = min(i + step, COUNT)) {
a->grow(i + step, i);
for (unsigned i = 1; i < chunks.size(); i++) {
if (i > 1)
BOOST_CHECK_EQUAL(a->get(chunks[i - 1] - 1), i - 1);
for (unsigned j = i; j < i + step; j++)
a->grow(chunks[i], i);
if (i > 1)
BOOST_CHECK_EQUAL(a->get(chunks[i - 1] - 1), i - 1);
for (unsigned j = chunks[i - 1]; j < chunks[i]; j++)
BOOST_CHECK_EQUAL(a->get(j), i);
BOOST_CHECK_THROW(a->get(i + step), runtime_error);
BOOST_CHECK_THROW(a->get(chunks[i] + 1), runtime_error);
}
}
}
BOOST_AUTO_TEST_CASE(reopen_array)
{
unsigned const COUNT = 10000;
block_address root;
{
typename array64::ptr a = create_array(COUNT, 123);
for (unsigned i = 0; i < COUNT; i += 7)
a->set(i, 234);
root = a->get_root();
}
{
typename array64::ptr a = open_array(root, COUNT);
for (unsigned i = 0; i < COUNT; i++)
BOOST_CHECK_EQUAL(a->get(i), i % 7 ? 123: 234);
}
}
BOOST_AUTO_TEST_CASE(destroy)
{
// FIXME: pending
}
//----------------------------------------------------------------

172
unit-tests/bitset_t.cc Normal file
View File

@ -0,0 +1,172 @@
// Copyright (C) 2013 Red Hat, Inc. All rights reserved.
//
// This file is part of the thin-provisioning-tools source.
//
// thin-provisioning-tools is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// thin-provisioning-tools is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with thin-provisioning-tools. If not, see
// <http://www.gnu.org/licenses/>.
#include "persistent-data/transaction_manager.h"
#include "persistent-data/space-maps/core.h"
#include "persistent-data/data-structures/bitset.h"
#include <vector>
#define BOOST_TEST_MODULE ArrayTests
#include <boost/test/included/unit_test.hpp>
using namespace std;
using namespace boost;
using namespace persistent_data;
//----------------------------------------------------------------
namespace {
block_address const NR_BLOCKS = 102400;
transaction_manager::ptr
create_tm() {
block_manager<>::ptr bm(new block_manager<>("./test.data", NR_BLOCKS, 4, block_io<>::READ_WRITE));
space_map::ptr sm(new core_map(NR_BLOCKS));
transaction_manager::ptr tm(new transaction_manager(bm, sm));
return tm;
}
bitset::ptr
create_bitset() {
return bitset::ptr(new bitset(create_tm()));
}
bitset::ptr
open_bitset(block_address root, unsigned count) {
return bitset::ptr(new bitset(create_tm(), root, count));
}
}
//----------------------------------------------------------------
BOOST_AUTO_TEST_CASE(create_empty_bitset)
{
bitset::ptr bs = create_bitset();
BOOST_CHECK_THROW(bs->get(0), runtime_error);
}
BOOST_AUTO_TEST_CASE(grow_default_false)
{
unsigned const COUNT = 100000;
bitset::ptr bs = create_bitset();
bs->grow(COUNT, false);
for (unsigned i = 0; i < COUNT; i++)
BOOST_CHECK_EQUAL(bs->get(i), false);
}
BOOST_AUTO_TEST_CASE(grow_default_true)
{
unsigned const COUNT = 100000;
bitset::ptr bs = create_bitset();
bs->grow(COUNT, true);
for (unsigned i = 0; i < COUNT; i++)
BOOST_CHECK_EQUAL(bs->get(i), true);
}
BOOST_AUTO_TEST_CASE(grow_throws_if_actualy_asked_to_shrink)
{
unsigned const COUNT = 100000;
bitset::ptr bs = create_bitset();
bs->grow(COUNT, false);
BOOST_CHECK_THROW(bs->grow(COUNT / 2, false), runtime_error);
}
BOOST_AUTO_TEST_CASE(multiple_grow_calls)
{
unsigned const COUNT = 100000;
unsigned const STEP = 37;
bitset::ptr bs = create_bitset();
vector<unsigned> chunks;
unsigned c;
for (c = 0; c < COUNT; c += STEP)
chunks.push_back(c);
chunks.push_back(c);
bool default_value = true;
for (unsigned i = 1; i < chunks.size(); i++) {
bs->grow(chunks[i], default_value);
for (unsigned j = chunks[i - 1]; j < chunks[i]; j++)
BOOST_CHECK_EQUAL(bs->get(j), default_value);
default_value = !default_value;
}
default_value = true;
for (unsigned i = 1; i < chunks.size(); i++) {
for (unsigned j = chunks[i - 1]; j < chunks[i]; j++)
BOOST_CHECK_EQUAL(bs->get(j), default_value);
default_value = !default_value;
}
}
BOOST_AUTO_TEST_CASE(set_out_of_bounds_throws)
{
unsigned const COUNT = 100000;
bitset::ptr bs = create_bitset();
BOOST_CHECK_THROW(bs->set(0, true), runtime_error);
bs->grow(COUNT, true);
BOOST_CHECK_THROW(bs->set(COUNT, true), runtime_error);
}
BOOST_AUTO_TEST_CASE(set_works)
{
unsigned const COUNT = 100000;
bitset::ptr bs = create_bitset();
bs->grow(COUNT, true);
for (unsigned i = 0; i < COUNT; i += 7)
bs->set(i, false);
for (unsigned i = 0; i < COUNT; i++) {
BOOST_CHECK_EQUAL(bs->get(i), i % 7 ? true : false);
}
}
BOOST_AUTO_TEST_CASE(reopen_works)
{
unsigned const COUNT = 100000;
block_address root;
{
bitset::ptr bs = create_bitset();
bs->grow(COUNT, true);
for (unsigned i = 0; i < COUNT; i += 7)
bs->set(i, false);
root = bs->get_root();
}
{
bitset::ptr bs = open_bitset(root, COUNT);
for (unsigned i = 0; i < COUNT; i++)
BOOST_CHECK_EQUAL(bs->get(i), i % 7 ? true : false);
}
}
//----------------------------------------------------------------