Commit f196f4f8 authored by Philipp Götze's avatar Philipp Götze

Merge branch 'more_data_structures' of...

Merge branch 'more_data_structures' of dbgit.prakinf.tu-ilmenau.de:code/nvm-based_data_structures into more_data_structures

Conflicts:
	CMakeLists.txt
	src/utils/PatriciaUtils.hpp
	test/BaseTrieTest.cpp
	test/CMakeLists.txt
	test/PatriciaTest.cpp
parents c44518af ef8fe5b3
Pipeline #677 failed with stages
in 2 minutes and 12 seconds
......@@ -107,6 +107,7 @@ add_subdirectory(src/pbptrees) # Persistent versions of B⁺-Tree, contains also
add_subdirectory(src/ptable) # BDCC-based analytical table structure
add_subdirectory(src/pskiplists)
add_subdirectory(src/ptries)
#########################
# Unit test using Catch #
#########################
......
#ifndef BASETRIE_HPP
#define BASETRIE_HPP
#include <zconf.h>
// std
#include <array>
#include <memory>
#include <cmath>
#include <iostream>
// pmdk
#include <libpmemobj++/make_persistent.hpp>
#include <libpmemobj++/p.hpp>
#include <libpmemobj++/persistent_ptr.hpp>
#include <libpmemobj++/transaction.hpp>
#include <libpmemobj++/utils.hpp>
namespace pobj = pmem::obj;
template <class T>
class alignas(64) BaseTrieNode
{
typedef pobj::persistent_ptr<BaseTrieNode<T>> pptr;
public:
BaseTrieNode() = default;
~BaseTrieNode()
{
auto pop = pobj::pool_by_vptr(this);
pmem::obj::transaction::run(pop, [&]()
{
for (const pptr& ptr : mChildren.get_rw())
{
if (ptr)
{
pmem::obj::delete_persistent<BaseTrieNode<T>>(ptr);
}
}
});
}
// true if new, false if overwrite
bool insert(const void* key, const size_t& key_size, const T& val);
bool lookup(const void* key, const size_t& key_size, T* val = nullptr);
bool remove(const void* key, const size_t& key_size);
int subNodes();
void print(int depth = 0) const;
void graph() const;
private:
pobj::p<std::array<pptr, 256>> mChildren {};
pobj::p<T> mValue;
pobj::p<uint16_t> mSize { 0 };
pobj::p<bool> mIsValue { false };
};
template<class T>
bool BaseTrieNode<T>::insert(const void* key, const size_t& key_size, const T &val)
{
// initialization
auto key_bytes = static_cast<const u_int8_t*>(key);
auto pop = pobj::pool_by_vptr(this);
// find target node
pptr cur_node = this;
for (size_t index = 0; index < key_size; ++index)
{
auto &child_ptr = cur_node->mChildren.get_rw()[key_bytes[index]];
if (!child_ptr)
{
pobj::transaction::run(pop, [&] () {
child_ptr = pobj::make_persistent<BaseTrieNode>();
cur_node->mSize = cur_node->mSize + 1;
});
}
cur_node = child_ptr;
}
// insert value
bool old_key = cur_node->mIsValue;
pobj::transaction::run(pop, [&] () {
cur_node->mValue = val;
cur_node->mIsValue = true;
});
return !old_key;
}
template<class T>
bool BaseTrieNode<T>::lookup(const void* key, const size_t& key_size, T* val)
{
auto key_bytes = static_cast<const u_int8_t*>(key);
pptr cur_node = this;
for (uint8_t index = 0; index < key_size; ++index)
{
auto child_ptr = cur_node->mChildren.get_ro()[key_bytes[index]];
if (!child_ptr)
{
return false;
}
cur_node = child_ptr;
}
if (cur_node->mIsValue && val)
{
*val = cur_node->mValue;
}
return cur_node->mIsValue;
}
template<class T>
bool BaseTrieNode<T>::remove(const void* key, const size_t& key_size)
{
auto key_bytes = static_cast<const u_int8_t*>(key);
auto pop = pobj::pool_by_vptr(this);
bool removed;
if (0 == key_size) // recursion base case
{
removed = mIsValue;
pobj::transaction::run(pop, [&]
{
mIsValue = false;
});
return removed;
}
auto& child_ptr = mChildren.get_rw()[*key_bytes];
if (!child_ptr)
{
return false;
}
removed = child_ptr->remove(key_bytes + 1, key_size - 1);
if (removed && child_ptr->mSize == 0)
{
pobj::transaction::run(pop, [&] {
pobj::delete_persistent<BaseTrieNode<T>>(child_ptr);
child_ptr = nullptr;
mSize = mSize - 1;
});
}
return removed;
}
template<class T>
int BaseTrieNode<T>::subNodes()
{
int count = 0, c = 0;
for (const auto& subnode : mChildren.get_ro())
{
if (c == mSize)
{
// we found all nodes
break;
}
if (subnode)
{
count += subnode->subNodes();
++c;
}
}
return count + 1;
}
template<class T>
void BaseTrieNode<T>::print(int depth) const
{
int count = 0;
for (int i = 0; i < mChildren.get_ro().size(); ++i)
{
if (auto c = mChildren.get_ro()[i])
{
std::cout << std::string(depth, '-') << static_cast<char>(i);
if (c->mIsValue)
{
std::cout << "\t\t\tVALUE";
}
std::cout << std::endl;
c->print(depth + 1);
}
}
}
template<class T>
void BaseTrieNode<T>::graph() const
{
std::cout << pptr(this).raw().off << " [shape=box, label = \"" << pptr(this).raw().off << "\"];" << std::endl;
for (int i = 0; i < mChildren.get_ro().size(); ++i)
{
if (mChildren.get_ro()[i])
{
std::cout << pptr(this).raw().off << " -> " << mChildren.get_ro()[i].raw().off << " [label=\" " << static_cast<char>(i) << "\"];" << std::endl;
mChildren.get_ro()[i]->graph();
}
}
}
#endif
project (pskiplists)
include_directories(${PROJECT_SOURCE_DIR})
get_property(I_DIRS DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
set(TEST_INCLUDE_DIRS ${TEST_INCLUDE_DIRS}
${I_DIRS}
CACHE INTERNAL "TESTING: Include Directories" FORCE
)
################
# Installation #
################
#
set(PROJECT_INCLUDES_F ${PROJECT_INCLUDES_F}
${CMAKE_CURRENT_SOURCE_DIR}/BaseTrie.hpp
${CMAKE_CURRENT_SOURCE_DIR}/PTreeNode.hpp
${CMAKE_CURRENT_SOURCE_DIR}/Patricia.hpp
PARENT_SCOPE)
#ifndef PTREENODE_HPP
#define PTREENODE_HPP
#include "../util/PatriciaUtils.hpp"
#include <zconf.h>
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <libpmemobj++/persistent_ptr.hpp>
#include <libpmemobj++/utils.hpp>
#include <libpmemobj++/transaction.hpp>
#include <libpmemobj++/make_persistent_array.hpp>
namespace pobj = pmem::obj;
template<class T>
class alignas(64) PTreeNode
{
template<class O>
friend class PatriciaTree;
template<class O>
using pptr = pobj::persistent_ptr<O>;
public:
PTreeNode(const u_int8_t* _key = nullptr, const int& _keylen = 0);
~PTreeNode();
private:
void setKey(const u_int8_t *key, const int &len);
void setChild(PTreeNode *pnode);
void unsetChild(PTreeNode *pnode);
void deleteAllNode();
int getDataCount() const;
void print(int done = 0) const;
int subNodes() const;
// returns the number of bytes neccessary to store the bits
static inline int OCTET_SPACE(int bits)
{
return ((bits - 1) / 8) + 1;
}
static inline u_int8_t CHECK_BIT(u_int8_t *K, int L)
{
return (K[L / 8] >> (7 - L % 8)) & 1;
}
private:
pptr<uint8_t[]> key{nullptr};
pobj::p<int> keylen{0};
pobj::p<T> data;
pobj::p<bool> alloc{false};
pptr<PTreeNode> parent{nullptr};
pptr<PTreeNode> link[2]{nullptr, nullptr};
};
template<class T>
PTreeNode<T>::PTreeNode(const u_int8_t* _key, const int& _keylen)
{
auto pop = pobj::pool_by_vptr(this);
pobj::transaction::run(pop, [&] {
if (_key)
{
setKey(_key, _keylen);
}
});
}
template<class T>
PTreeNode<T>::~PTreeNode()
{
auto pop = pobj::pool_by_vptr(this);
pobj::transaction::run(pop, [&] {
if (data && alloc)
{
if (key)
{
pobj::delete_persistent<uint8_t[]>(key, OCTET_SPACE(keylen));
}
}
});
}
template<class T>
void PTreeNode<T>::print(int done) const
{
int c = 0;
c += link[0] ? 1 : 0;
c += link[1] ? 1 : 0;
std::stringstream ss;
ss << std::string(done, '-');
if (key)
{
ss << toBinaryString(key.get(), keylen).substr(done);
}
else
{
ss << "<empty>";
}
if (data)
{
ss << "\t(" << toString(key.get(), OCTET_SPACE(keylen)) << ")";
ss << "\t\tData:\t";
ss << toHexString(data);
}
ss << "\t\t\t\t" << c << " children";
std::cout << ss.str() << std::endl;
if (link[0])
{
link[0]->print(keylen);
}
if (link[1])
{
link[1]->print(keylen);
}
}
template<class T>
void PTreeNode<T>::setKey(const u_int8_t* _key, const int &_keylen)
{
auto pop = pobj::pool_by_vptr(this);
pobj::transaction::run(pop, [&] {
if (key)
{
pobj::delete_persistent<uint8_t[]>(key, OCTET_SPACE(_keylen));
}
if (0 < (keylen = _keylen))
{
//key = (u_int8_t *) calloc(OCTET_SPACE(_keylen) + 1, sizeof(u_int8_t));
//pop.memcpy_persist(&key, _key, OCTET_SPACE(_keylen));
key = pobj::make_persistent<uint8_t[]>(OCTET_SPACE(_keylen)); // #TODO: This just copies the first byte! consider keylen as well
for (int i = 0; i < keylen; ++i)
{
key[i] = _key[i];
}
}
else
{
key = nullptr;
}
});
}
template<class T>
void PTreeNode<T>::setChild(PTreeNode *child)
{
assert (child);
auto pop = pobj::pool_by_vptr(this);
pobj::transaction::run(pop, [&] {
link[CHECK_BIT(child->key.get(), keylen)] = child;
child->parent = this;
});
}
template<class T>
void PTreeNode<T>::unsetChild(PTreeNode *child)
{
assert (child);
link[CHECK_BIT(child->key.get(), keylen)] = nullptr;
child->parent = nullptr;
}
template<class T>
void PTreeNode<T>::deleteAllNode()
{
auto pop = pobj::pool_by_vptr(this);
pobj::transaction::run(pop, [&] {
//pobj::delete_persistent<uint8_t>(key);
if (link[0])
{
link[0]->deleteAllNode();
assert (link[0]->link[0] == nullptr &&
link[0]->link[1] == nullptr);
pobj::delete_persistent<PTreeNode>(link[0]);
link[0] = nullptr;
}
if (link[1])
{
link[1]->deleteAllNode();
assert (link[1]->link[0] == nullptr &&
link[1]->link[1] == nullptr);
pobj::delete_persistent<PTreeNode>(link[1]);
link[1] = nullptr;
}
});
}
template<class T>
int PTreeNode<T>::getDataCount() const
{
// return dataCount; #TODO
return 0;
}
template<class T>
int PTreeNode<T>::subNodes() const
{
int count = 0;
if (link[0])
{
count += link[0]->subNodes();
}
if (link[1])
{
count += link[1]->subNodes();
}
return count + 1;
}
#endif
#ifndef PATRICIA_HPP
#define PATRICIA_HPP
#include "PTreeNode.hpp"
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <sys/types.h>
#include <libpmemobj++/make_persistent.hpp>
#include <libpmemobj++/p.hpp>
#include <libpmemobj++/persistent_ptr.hpp>
#include <libpmemobj++/transaction.hpp>
#include <libpmemobj++/utils.hpp>
const static u_int8_t bitmask[] =
{0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
static const int LINK_NULL = 0x00;
static const int LINK_0_OK = 0x01;
static const int LINK_1_OK = 0x02;
static const int LINK_BOTH_OK = 0x03;
template<class T>
class PTreeNode;
template<class T>
class PatriciaTree
{
template<class O>
using pptr = pobj::persistent_ptr<O>;
public:
PatriciaTree();
~PatriciaTree();
bool insert(const void* key, size_t len, const T& data);
bool lookup(const void* key, size_t len, T* val = nullptr) const;
bool remove(const void* key, size_t len);
unsigned int getCount();
int getSubnodes() const;
void print();
private:
void branchNode(PTreeNode<T>* a, PTreeNode<T>* b);
void deleteNode(PTreeNode<T>* t);
static inline int MIN(int N1, int N2)
{
return (N1 < N2) ? N1 : N2;
}
static inline u_int8_t CHECK_BIT(const u_int8_t* K, int L)
{
return (K[L / 8] >> (7 - L % 8)) & 1;
}
static inline bool MATCH_KEY(u_int8_t* K1, const u_int8_t* K2, int L)
{
bool first = (L >= 8 ? (memcmp(K1, K2, L / 8) == 0) : 1);
bool sec = (L % 8 > 0) ?
(K1[L / 8] & bitmask[L % 8]) == (K2[L / 8] & bitmask[L % 8])
: 1;
bool ret = first && sec;
return ret;
}
private:
pobj::p<unsigned int> dataCount{0};
pptr<PTreeNode<T>> root;
};
template<class T>
PatriciaTree<T>::PatriciaTree()
{
auto pop = pobj::pool_by_vptr(this);
pobj::transaction::run(pop, [&] {
root = pobj::make_persistent<PTreeNode<T>>();
});
}
template<class T>
PatriciaTree<T>::~PatriciaTree()
{
root->deleteAllNode();
}
template<class T>
bool PatriciaTree<T>::insert(const void* _key, size_t _len, const T& data)
{
auto pop = pobj::pool_by_vptr(this);
if (!root)
{
pobj::transaction::run(pop, [&] {
root = pobj::make_persistent<PTreeNode<T>>();
});
}
auto argKey = static_cast<const u_int8_t* >(_key);
int argKeylen = static_cast<int>(_len * 8);
pptr<PTreeNode<T>> bp, mp; // branch point, matched point
pptr<PTreeNode<T>> t = nullptr; // target node
assert (argKey && argKeylen > 0);
for (bp = root, mp = nullptr;
bp != nullptr && MATCH_KEY(bp->key.get(), argKey, bp->keylen);
bp = bp->link[CHECK_BIT(argKey, bp->keylen)])
{
mp = bp;
if (bp->keylen >= argKeylen)
{
break;
}
}
if (bp == nullptr)
{
bp = mp;
}
bool new_k = true;
pobj::transaction::run(pop, [&]() {
if (bp->keylen == argKeylen && MATCH_KEY(bp->key.get(), argKey, bp->keylen))
{
t = bp;
if (t->data)
{
new_k = false;
dataCount = dataCount - 1;
}
}
else
{
t = pobj::make_persistent<PTreeNode<T>>(argKey, argKeylen);
branchNode(bp.get(), t.get());
}
t->data = data;
dataCount = dataCount + 1;
});
return new_k;
}
template<class T>
unsigned int PatriciaTree<T>::getCount()
{
return dataCount;
}
template<class T>
void PatriciaTree<T>::print()
{
root->print();
}
template<class T>
void PatriciaTree<T>::branchNode(PTreeNode<T>* a, PTreeNode<T>* b)
{
int len, min, i;
pptr<PTreeNode<T>> parent, branch, child;
assert (b != nullptr);
// search different point between keys
min = MIN(a->keylen, b->keylen);
for (i = 0, len = 0; i < (min / 8) && a->key[i] == b->key[i]; i++)
{
len += 8;
}
for (; len < min; len++)
{
if (CHECK_BIT(a->key.get(), len) != CHECK_BIT(b->key.get(), len))
{
break;
}
}
assert (len