write some code

This commit is contained in:
Joe Thornber 2011-06-27 10:45:30 +01:00
parent 2e39670ff9
commit c090bb88b5
9 changed files with 366 additions and 99 deletions

View File

@ -1,16 +1,18 @@
SOURCE=\
block.cc \
main.cc \
metadata.cc \
transaction_manager.cc
metadata.cc
OBJECTS=$(subst .cc,.o,$(SOURCE))
CPPFLAGS=-Wall -std=c++0x
INCLUDES=
LIBS=-lstdc++
.SUFFIXES: .cc .o .d
.SUFFIXES: .cc .o
%.d: %.cc
$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
.cc.o:
g++ -c $(CPPFLAGS) $(INCLUDES) -o $@ $<
@ -18,7 +20,4 @@ LIBS=-lstdc++
multisnap_display: $(OBJECTS)
g++ -o $@ $+ $(LIBS)
main.o: block.h
block.o: block.h
transaction_manager.o: transaction_manager.h block.h
metadata.o: block.h transaction_manager.h btree.h metadata.h
include $(subst .cc,.d,$(SOURCE))

22
block.h
View File

@ -30,6 +30,8 @@ namespace persistent_data {
class block_validator {
public:
typedef boost::shared_ptr<block_validator> ptr;
virtual ~block_validator() {}
virtual void check(block const &b) const = 0;
@ -37,7 +39,7 @@ namespace persistent_data {
};
struct block {
typedef boost::optional<block_validator> maybe_validator;
typedef boost::optional<typename block_validator::ptr> maybe_validator;
block(block_address location,
maybe_validator v = maybe_validator())
@ -47,7 +49,7 @@ namespace persistent_data {
block_address location_;
buffer data_;
boost::optional<block_validator> validator_;
maybe_validator validator_;
};
typedef boost::shared_ptr<block> block_ptr;
@ -67,6 +69,8 @@ namespace persistent_data {
// locked.
class write_ref : public read_ref {
public:
write_ref(block_ptr b);
using read_ref::data;
buffer &data();
};
@ -86,16 +90,20 @@ namespace persistent_data {
// Validator variants
read_ref
read_lock(block_address location, block_validator const &v);
read_lock(block_address location,
typename block_validator::ptr const &v);
boost::optional<read_ref>
read_try_lock(block_address location, block_validator const &v);
read_try_lock(block_address location,
typename block_validator::ptr const &v);
write_ref
write_lock(block_address location, block_validator const &v);
write_lock(block_address location,
typename block_validator::ptr const &v);
write_ref
write_lock_zero(block_address location, block_validator const &v);
write_lock_zero(block_address location,
typename block_validator::ptr const &v);
// Use this to commit changes
void flush(write_ref super_block);
@ -111,6 +119,8 @@ namespace persistent_data {
};
}
#include "block.tcc"
//----------------------------------------------------------------
#endif

View File

@ -5,11 +5,13 @@
#include <sys/types.h>
#include <unistd.h>
#include <boost/bind.hpp>
#include <iostream>
#include <stdexcept>
using namespace boost;
using namespace persistent_data;
using namespace std;
//----------------------------------------------------------------
@ -33,6 +35,12 @@ block_manager<BlockSize>::read_ref::data() const
return block_->data_;
}
template <uint32_t BlockSize>
block_manager<BlockSize>::write_ref::write_ref(block_manager::block_ptr b)
: read_ref(b)
{
}
template <uint32_t BlockSize>
typename block_manager<BlockSize>::buffer &
block_manager<BlockSize>::write_ref::data()
@ -61,7 +69,7 @@ typename block_manager<BlockSize>::read_ref
block_manager<BlockSize>::read_lock(block_address location)
{
block_ptr b(new block(location));
read_block(b);
read_block(*b);
return read_ref(b);
}
@ -76,8 +84,8 @@ template <uint32_t BlockSize>
typename block_manager<BlockSize>::write_ref
block_manager<BlockSize>::write_lock(block_address location)
{
block_ptr b(new block(location), write_and_release);
read_block(b);
block_ptr b(new block(location), bind(&block_manager::write_and_release, this, _1));
read_block(*b);
return write_ref(b);
}
@ -85,25 +93,25 @@ template <uint32_t BlockSize>
typename block_manager<BlockSize>::write_ref
block_manager<BlockSize>::write_lock_zero(block_address location)
{
block_ptr b(new block(location), write_and_release);
zero_block(b);
block_ptr b(new block(location), bind(&block_manager<BlockSize>::write_and_release, this, _1));
zero_block(*b);
return write_ref(b);
}
template <uint32_t BlockSize>
typename block_manager<BlockSize>::read_ref
block_manager<BlockSize>::read_lock(block_address location,
block_manager<BlockSize>::block_validator const &v)
typename block_manager<BlockSize>::block_validator::ptr const &v)
{
block_ptr b(new block(location, v));
read_block(b);
read_block(*b);
return read_ref(b);
}
template <uint32_t BlockSize>
optional<typename block_manager<BlockSize>::read_ref>
block_manager<BlockSize>::read_try_lock(block_address location,
block_manager<BlockSize>::block_validator const &v)
typename block_manager<BlockSize>::block_validator::ptr const &v)
{
return read_lock(location, v);
}
@ -111,20 +119,20 @@ block_manager<BlockSize>::read_try_lock(block_address location,
template <uint32_t BlockSize>
typename block_manager<BlockSize>::write_ref
block_manager<BlockSize>::write_lock(block_address location,
block_manager<BlockSize>::block_validator const &v)
typename block_manager<BlockSize>::block_validator::ptr const &v)
{
block_ptr b(new block(location, v), write_and_release);
read_block(b);
read_block(*b);
return write_ref(b);
}
template <uint32_t BlockSize>
typename block_manager<BlockSize>::write_ref
block_manager<BlockSize>::write_lock_zero(block_address location,
block_manager<BlockSize>::block_validator const &v)
typename block_manager<BlockSize>::block_validator::ptr const &v)
{
block_ptr b(new block(location, v), write_and_release);
zero_block(b);
zero_block(*b);
return write_ref(b);
}
@ -140,16 +148,16 @@ template <uint32_t BlockSize>
void
block_manager<BlockSize>::read_block(block &b)
{
std::cerr << "reading block: " << b->location << std::endl;
std::cerr << "reading block: " << b.location_ << std::endl;
off_t r;
r = ::lseek(fd_, BlockSize * b->location_, SEEK_SET);
r = ::lseek(fd_, BlockSize * b.location_, SEEK_SET);
if (r == (off_t) -1)
throw std::runtime_error("lseek failed");
ssize_t n;
size_t remaining = BlockSize;
unsigned char *buf = b->data_;
unsigned char *buf = b.data_;
do {
n = ::read(fd_, buf, remaining);
if (n > 0) {
@ -167,18 +175,18 @@ void
block_manager<BlockSize>::write_block(block const &b)
{
std::cerr << "writing block: " << b->location << std::endl;
std::cerr << "writing block: " << b.location_ << std::endl;
off_t r;
r = ::lseek(fd_, BlockSize * b->location_, SEEK_SET);
r = ::lseek(fd_, BlockSize * b.location_, SEEK_SET);
if (r == (off_t) -1)
throw std::runtime_error("lseek failed");
ssize_t n;
size_t remaining = BlockSize;
unsigned char *buf = b->data_;
unsigned char const *buf = b.data_;
do {
n = ::read(fd_, buf, remaining);
n = ::write(fd_, buf, remaining);
if (n > 0) {
remaining -= n;
buf += n;
@ -193,14 +201,14 @@ template <uint32_t BlockSize>
void
block_manager<BlockSize>::zero_block(block &b)
{
memset(b->data_, 0, BlockSize);
memset(b.data_, 0, BlockSize);
}
template <uint32_t BlockSize>
void
block_manager<BlockSize>::write_and_release(block *b)
{
write_block(b);
write_block(*b);
delete b;
}

20
btree.h
View File

@ -1,6 +1,7 @@
#ifndef BTREE_H
#define BTREE_H
#include "endian.h"
#include "transaction_manager.h"
//----------------------------------------------------------------
@ -17,8 +18,8 @@ namespace persistent_data {
typedef typename block_manager<BlockSize>::read_ref read_ref;
typedef typename block_manager<BlockSize>::write_ref write_ref;
btree(boost::shared_ptr<transaction_manager<BlockSize> > tm);
btree(boost::shared_ptr<transaction_manager<BlockSize> > tm,
btree(typename persistent_data::transaction_manager<BlockSize>::ptr tm);
btree(typename transaction_manager<BlockSize>::ptr tm,
block_address root);
~btree();
@ -42,8 +43,23 @@ namespace persistent_data {
bool destroy_;
block_address root_;
};
struct uint64_traits {
typedef base::__le64 disk_type;
typedef uint64_t value_type;
static value_type construct(void *data) {
// extra memcpy because I'm paranoid about alignment issues
base::__le64 disk;
::memcpy(&disk, data, sizeof(disk));
return base::to_cpu<uint64_t>(disk);
}
};
};
#include "btree.tcc"
//----------------------------------------------------------------
#endif

192
btree.tcc
View File

@ -1,14 +1,20 @@
#include "btree.h"
#include "endian.h"
#include "transaction_manager.h"
#include <list>
#include <boost/noncopyable.hpp>
#include <boost/optional.hpp>
// FIXME: can't have using clauses in a header
using namespace base;
using namespace boost;
using namespace persistent_data;
//----------------------------------------------------------------
namespace {
namespace btree_detail {
//------------------------------------------------
// On disk data layout for btree nodes
enum node_flags {
@ -25,7 +31,7 @@ namespace {
__le32 max_entries;
} __attribute__((packed));
struct node {
struct disk_node {
struct node_header header;
__le64 keys[0];
} __attribute__((packed));
@ -34,34 +40,76 @@ namespace {
//------------------------------------------------
// Class that acts as an interface over the raw little endian btree
// node data.
class node {
class node_ref {
public:
enum type {
INTERNAL,
LEAF
};
type get_type() const;
void set_type(type t);
node_ref(disk_node *raw)
: raw_(raw) {
}
unsigned get_nr_entries() const;
void set_nr_entries(unsigned n);
type get_type() const {
uint32_t flags = to_cpu<uint32_t>(raw_->header.flags);
if (flags & INTERNAL_NODE)
return INTERNAL;
else if (flags & LEAF_NODE)
return LEAF;
else
throw runtime_error("unknow node type");
}
unsigned get_max_entries() const;
void set_max_entries(unsigned n);
void set_type(type t){
uint32_t flags = to_cpu<uint32_t>(raw_->header.flags);
switch (t) {
case INTERNAL:
flags |= INTERNAL_NODE;
break;
uint64_t key_at(unsigned i) const;
case LEAF:
flags |= LEAF_NODE;
break;
}
raw_->header.flags = to_disk<__le32>(flags);
}
unsigned get_nr_entries() const {
return to_cpu<uint32_t>(raw_->header.nr_entries);
}
void set_nr_entries(unsigned n) {
raw_->header.nr_entries = to_disk<__le32>(n);
}
unsigned get_max_entries() const {
return to_cpu<uint32_t>(raw_->header.max_entries);
}
void set_max_entries(unsigned n) {
raw_->header.max_entries = to_disk<__le32>(n);
}
uint64_t key_at(unsigned i) const {
return to_cpu<uint64_t>(raw_->keys[i]);
}
template <typename ValueTraits>
typename ValueTraits::value_type value_at(unsigned i) const;
typename ValueTraits::value_type value_at(unsigned i) const {
void *value_base = &raw_->keys[to_cpu<uint32_t>(raw_->header.max_entries)];
void *value_ptr = static_cast<unsigned char *>(value_base) +
sizeof(typename ValueTraits::disk_type) * i;
return ValueTraits::construct(value_ptr);
}
private:
struct node *raw_;
disk_node *raw_;
};
//------------------------------------------------
// Various searches
int bsearch(node const &n, uint64_t key, int want_hi)
int bsearch(node_ref const &n, uint64_t key, int want_hi)
{
int lo = -1, hi = n.get_nr_entries();
@ -81,7 +129,7 @@ namespace {
return want_hi ? hi : lo;
}
optional<unsigned> exact_search(node const &n, uint64_t key) {
optional<unsigned> exact_search(node_ref const &n, uint64_t key) {
int i = bsearch(n, key, 0);
if (i < 0 || static_cast<unsigned>(i) >= n.get_nr_entries())
return optional<unsigned>();
@ -92,7 +140,19 @@ namespace {
//------------------------------------------------
//
template <uint32_t BlockSize>
node &to_node(typename block_manager<BlockSize>::write_ref b);
node_ref to_node(typename block_manager<BlockSize>::read_ref &b)
{
// FIXME: this should return a const read_ref somehow.
return node_ref(
reinterpret_cast<disk_node *>(const_cast<unsigned char *>(b.data())));
}
template <uint32_t BlockSize>
node_ref to_node(typename block_manager<BlockSize>::write_ref &b)
{
return node_ref(
reinterpret_cast<disk_node *>(const_cast<unsigned char *>(b.data())));
}
unsigned
calc_max_entries(uint32_t bs);
@ -101,62 +161,103 @@ namespace {
template <uint32_t BlockSize>
class ro_spine : private noncopyable {
public:
void step(block_address b);
node get_node() const;
ro_spine(typename transaction_manager<BlockSize>::ptr tm)
: tm_(tm) {
}
void step(block_address b) {
spine_.push_back(tm_->read_lock(b));
if (spine_.size() > 2)
spine_.pop_front();
}
node_ref get_node() {
return to_node<BlockSize>(spine_.back());
}
private:
typename transaction_manager<BlockSize>::ptr tm_;
std::list<typename block_manager<BlockSize>::read_ref> spine_;
};
class internal_traits {
template <uint32_t BlockSize>
class shadow_spine : private noncopyable {
public:
typedef uint64_t value_type;
shadow_spine(typename transaction_manager<BlockSize>::ptr tm)
: tm_(tm) {
}
void step(block_address b) {
spine_.push_back(tm_->shadow(b));
if (spine_.size() == 1)
root_ = spine_.front().get_location();
else if (spine_.size() > 2)
spine_.pop_front();
}
node_ref get_node() {
return to_node<BlockSize>(spine_.back());
}
node_ref get_parent() {
if (spine_.size() < 2)
throw std::runtime_error("no parent");
return to_node<BlockSize>(spine_.front());
}
node_ref get_root() {
return root_;
}
private:
typename transaction_manager<BlockSize>::ptr tm_;
std::list<typename block_manager<BlockSize>::write_ref> spine_;
block_address root_;
};
template <typename ValueTraits, uint32_t BlockSize, typename Search>
template <typename ValueTraits, uint32_t BlockSize> //, typename Search>
optional<typename ValueTraits::value_type>
lookup_raw(ro_spine<BlockSize> &spine, block_address block, uint64_t key) {
using namespace boost;
typedef typename ValueTraits::value_type leaf_type;
typedef typename internal_traits::value_type internal_type;
Search find;
for (;;) {
spine.step(block);
node &n = spine.node();
node_ref const &n = spine.get_node();
auto mi = find(n, key);
auto mi = exact_search(n, key);
if (!mi)
return optional<leaf_type>();
if (n.get_type() == node::LEAF)
return optional<leaf_type>(n.value_at(*mi));
if (n.get_type() == node_ref::LEAF)
return optional<leaf_type>(n.value_at<ValueTraits>(*mi));
block = n.value_at<internal_type>(*mi);
block = n.value_at<uint64_traits>(*mi);
}
}
}
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
btree<Levels, ValueTraits, BlockSize>::btree(shared_ptr<transaction_manager<BlockSize> > tm)
btree<Levels, ValueTraits, BlockSize>::btree(typename transaction_manager<BlockSize>::ptr tm)
: tm_(tm),
destroy_(false)
{
write_ref root = tm_.new_block();
using namespace btree_detail;
node &n = to_node(root);
n.set_type(node::LEAF);
write_ref root = tm_->new_block();
node_ref n = to_node<BlockSize>(root);
n.set_type(node_ref::LEAF);
n.set_nr_entries(0);
n.set_max_entries(calc_max_entries(BlockSize));
root_ = root.location();
root_ = root.get_location();
}
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
btree<Levels, ValueTraits, BlockSize>::btree(shared_ptr<transaction_manager<BlockSize> > tm,
btree<Levels, ValueTraits, BlockSize>::btree(typename transaction_manager<BlockSize>::ptr tm,
block_address root)
: tm_(tm),
destroy_(false),
@ -174,24 +275,29 @@ template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
typename btree<Levels, ValueTraits, BlockSize>::maybe_value
btree<Levels, ValueTraits, BlockSize>::lookup(key const &key) const
{
ro_spine<BlockSize> spine;
using namespace btree_detail;
ro_spine<BlockSize> spine(tm_);
block_address root = root_;
for (unsigned level = 0; level < Levels - 1; ++level) {
auto mroot = lookup_raw<internal_traits, BlockSize, exact_search>(spine, root, key[level]);
optional<block_address> mroot =
lookup_raw<uint64_traits, BlockSize>(spine, root, key[level]);
if (!mroot)
return maybe_value();
root = *mroot;
}
return lookup_raw<ValueTraits, BlockSize, exact_search>(spine, root, key[Levels - 1]);
return lookup_raw<ValueTraits, BlockSize>(spine, root, key[Levels - 1]);
}
template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
typename btree<Levels, ValueTraits, BlockSize>::maybe_pair
btree<Levels, ValueTraits, BlockSize>::lookup_le(key const &key) const
{
using namespace btree_detail;
return maybe_pair();
}
@ -199,6 +305,8 @@ template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
typename btree<Levels, ValueTraits, BlockSize>::maybe_pair
btree<Levels, ValueTraits, BlockSize>::lookup_ge(key const &key) const
{
using namespace btree_detail;
return maybe_pair();
}
@ -207,6 +315,7 @@ template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
void
btree<Levels, ValueTraits, BlockSize>::insert(key const &key, typename ValueTraits::value_type const &value)
{
using namespace btree_detail;
}
@ -214,6 +323,7 @@ template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
void
btree<Levels, ValueTraits, BlockSize>::remove(key const &key)
{
using namespace btree_detail;
}
@ -228,6 +338,7 @@ template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
void
btree<Levels, ValueTraits, BlockSize>::set_root(block_address root)
{
using namespace btree_detail;
}
@ -235,6 +346,7 @@ template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
block_address
btree<Levels, ValueTraits, BlockSize>::get_root() const
{
using namespace btree_detail;
}
@ -242,6 +354,7 @@ template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
ptr
btree<Levels, ValueTraits, BlockSize>::clone() const
{
using namespace btree_detail;
}
@ -249,6 +362,7 @@ template <unsigned Levels, typename ValueTraits, uint32_t BlockSize>
void
btree<Levels, ValueTraits, BlockSize>::destroy()
{
using namespace btree_detail;
}
#endif

83
endian.h Normal file
View File

@ -0,0 +1,83 @@
#ifndef ENDIAN_H
#define ENDIAN_H
#include <boost/static_assert.hpp>
//----------------------------------------------------------------
namespace base {
// These are just little wrapper types to make the compiler
// understand that the le types are not assignable to the
// corresponding cpu type.
struct __le16 {
explicit __le16(uint16_t v = 0.0)
: v_(v) {
}
uint16_t v_;
} __attribute__((packed));
struct __le32 {
explicit __le32(uint32_t v = 0.0)
: v_(v) {
}
uint32_t v_;
} __attribute__((packed));
struct __le64 {
explicit __le64(uint64_t v = 0.0)
: v_(v) {
}
uint64_t v_;
} __attribute__((packed));
//--------------------------------
template <typename CPUType, typename DiskType>
CPUType to_cpu(DiskType const &d) {
BOOST_STATIC_ASSERT(sizeof(d) == 0);
}
template <typename DiskType, typename CPUType>
DiskType to_disk(CPUType const &v) {
BOOST_STATIC_ASSERT(sizeof(v) == 0);
}
template <>
uint16_t to_cpu<uint16_t, __le16>(__le16 const &d) {
return d.v_;
}
template <>
__le16 to_disk<__le16, uint16_t>(uint16_t const &v) {
return __le16(v);
}
template <>
uint32_t to_cpu<uint32_t, __le32>(__le32 const &d) {
return d.v_;
}
template <>
__le32 to_disk<__le32, uint32_t>(uint32_t const &v) {
return __le32(v);
}
template <>
uint64_t to_cpu<uint64_t, __le64>(__le64 const &d) {
return d.v_;
}
template <>
__le64 to_disk<__le64, uint64_t>(uint64_t const &v) {
return __le64(v);
}
}
//----------------------------------------------------------------
#endif

View File

@ -115,7 +115,7 @@ metadata::create_thin(dev_t dev)
throw std::runtime_error("Device already exists");
single_mapping_tree::ptr new_tree(new single_mapping_tree(tm_));
mappings_top_level_.insert(key, *new_tree);
mappings_top_level_.insert(key, new_tree->get_root());
mappings_.set_root(mappings_top_level_.get_root()); // FIXME: ugly
}
@ -125,12 +125,14 @@ metadata::create_snap(dev_t dev, dev_t origin)
uint64_t snap_key[1] = {dev};
uint64_t origin_key[1] = {origin};
auto mtree = mappings_top_level_.lookup(origin_key);
if (!mtree)
auto mtree_root = mappings_top_level_.lookup(origin_key);
if (!mtree_root)
throw std::runtime_error("unknown origin");
single_mapping_tree::ptr clone(mtree->clone());
mappings_top_level_.insert(snap_key, *clone);
single_mapping_tree otree(tm_, *mtree_root);
single_mapping_tree::ptr clone(otree.clone());
mappings_top_level_.insert(snap_key, clone->get_root());
mappings_.set_root(mappings_top_level_.get_root()); // FIXME: ugly
time_++;

View File

@ -17,6 +17,54 @@
namespace multisnap {
typedef uint64_t sector_t;
struct device_details_disk {
__le64 dev_size;
__le64 mapped_blocks;
__le64 transaction_id; /* when created */
__le32 creation_time;
__le32 snapshotted_time;
} __attribute__ ((packed));
struct device_details {
uint64_t dev_size;
uint64_t mapped_blocks;
uint64_t transaction_id; /* when created */
uint32_t creation_time;
uint32_t snapshotted_time;
};
struct detail_traits {
typedef device_details_disk disk_type;
typedef device_details value_type;
static value_type construct(void *data) {
struct device_details_disk disk;
struct device_details cpu;
::memcpy(&disk, data, sizeof(disk));
cpu.dev_size = to_cpu<uint64_t>(disk.dev_size);
cpu.mapped_blocks = to_cpu<uint64_t>(disk.mapped_blocks);
cpu.transaction_id = to_cpu<uint64_t>(disk.transaction_id);
cpu.creation_time = to_cpu<uint32_t>(disk.creation_time);
cpu.snapshotted_time = to_cpu<uint32_t>(disk.snapshotted_time);
return cpu;
}
};
#if 0
class dev_traits {
public:
typedef base::__le64 disk_type;
typedef persistent_data::btree<1, uint64_traits, BLOCK_SIZE> value_type;
static value_type construct(void *data) {
uint64_t root = uint64_traits::construct(data);
return value_type
}
};
#endif
class metadata {
public:
typedef boost::shared_ptr<metadata> ptr;
@ -75,29 +123,14 @@ namespace multisnap {
bool device_exists(dev_t dev) const;
class detail_traits {
public:
typedef uint64_t value_type;
};
class map_traits {
public:
typedef block_address value_type;
};
class dev_traits {
public:
typedef persistent_data::btree<1, map_traits, BLOCK_SIZE> value_type;
};
uint32_t time_;
persistent_data::transaction_manager<BLOCK_SIZE>::ptr tm_;
typedef persistent_data::btree<1, detail_traits, BLOCK_SIZE> detail_tree;
typedef persistent_data::btree<1, dev_traits, BLOCK_SIZE> dev_tree;
typedef persistent_data::btree<2, map_traits, BLOCK_SIZE> mapping_tree;
typedef persistent_data::btree<1, map_traits, BLOCK_SIZE> single_mapping_tree;
typedef persistent_data::btree<1, uint64_traits, BLOCK_SIZE> dev_tree;
typedef persistent_data::btree<2, uint64_traits, BLOCK_SIZE> mapping_tree;
typedef persistent_data::btree<1, uint64_traits, BLOCK_SIZE> single_mapping_tree;
detail_tree details_;
dev_tree mappings_top_level_;

View File

@ -54,6 +54,8 @@ namespace persistent_data {
};
}
#include "transaction_manager.tcc"
//----------------------------------------------------------------
#endif