XTL  0.1
eXtended Template Library
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
btree.hpp
Go to the documentation of this file.
1 
5 #pragma once
6 
7 #include <xtd/xtd.hpp>
8 
9 #include <xtd/lru_cache.hpp>
10 #include <xtd/mapped_file.hpp>
11 #include <xtd/filesystem.hpp>
12 #include <xtd/debug.hpp>
13 
14 #define _DEBUG_ME_ 1
15 
16 namespace xtd{
17 
18  namespace _{
19  namespace btree{
20 
21  enum class page_type{
22  file_header_page,
23  leaf_page,
24  branch_page,
25  };
26 
30  template <size_t _PageSize>
31  class data_page{
32  public:
33 
34  page_type _page_type;
35 
37 
38  static size_t page_size(){ return _PageSize; }
39 
40  };
41 
44  template <>
45  class data_page<-1>{
46  public:
47 
48  page_type _page_type;
49 
51 
52  static size_t page_size(){ return xtd::memory::page_size(); }
53 
54  };
55 
56  template <size_t _PageSize, size_t _CacheSize> class lru_cache;
57 
58  template <size_t _PageSize>
59  class page_loader{
60  template <size_t, size_t> friend class lru_cache;
62  public:
63 
64  page_loader(const xtd::filesystem::path& oPath) : _File(oPath){}
65  page_loader(const page_loader&) = delete;
66  page_loader(page_loader&& src) : _File(std::move(src._File)){}
67 
68  typename data_page<_PageSize>::pointer operator()(size_t pagenum){
69  return _File.template get<data_page<_PageSize>>(pagenum);
70  }
71  };
72 
73  template <size_t _PageSize, size_t _CacheSize>
74  class lru_cache : public xtd::lru_cache<size_t, typename data_page<_PageSize>::pointer, _CacheSize, page_loader<_PageSize> >{
76 
77  public:
78  using value_type = typename data_page<_PageSize>::pointer;
79  static const size_t cache_size = _CacheSize;
80  explicit lru_cache(const xtd::filesystem::path& oPath) : _super_t(page_loader<_PageSize>(oPath)) {}
81 
82  typename data_page<_PageSize>::pointer append(size_t & newpage){
83  if (_super_t::size() >= cache_size){
84  _super_t::pop_back();
85  }
86  _super_t::push_front(std::make_pair(newpage, _loader._File.template append<data_page<_PageSize>>(newpage)));
87  return _super_t::front().second;
88  }
89  };
90 
94  template <size_t _PageSize>
95  class file_header : public data_page<_PageSize>{
96  public:
98  static const page_type type = page_type::file_header_page;
99  size_t _root_page;
100  size_t _free_page;
101  size_t _count;
102  };
103 
109  template <typename _KeyT, typename _ValueT, size_t _PageSize>
110  class leaf : public data_page<_PageSize>{
112  public:
113  using key_type = _KeyT;
114  using value_type = _ValueT;
116  static const page_type type = page_type::leaf_page;
117  struct page_header{
118  size_t _count;
119  size_t _prev_page;
120  size_t _next_page;
121  };
122 
123  struct record{
124  key_type _key;
125  value_type _value;
126  };
127 
128  #if _DEBUG_ME_
129  static inline size_t records_per_page(){
130  return 5;
131  }
132  #else
133  static inline size_t records_per_page(){
134  static size_t iRet = (_super_t::page_size() - sizeof(page_header) - sizeof(data_page<_PageSize>)) / sizeof(record);
135  return iRet;
136  }
137  #endif
138 
139  bool insert(const key_type& key, const value_type& value){
140  _records[_page_header._count]._key = key;
141  _records[_page_header._count]._value = value;
142  std::sort(&_records[0], &_records[_page_header._count], [](const record& lhs, const record& rhs)->bool{ return lhs._key < rhs._key; });
143  _page_header._count++;
144  return true;
145  }
146 
147  template <typename _CacheT>
148  void split(_CacheT& oCache, size_t left_page_idx, key_type& left_page_key, size_t& right_page_idx){
149  auto oNewPage = static_page_cast<leaf>(oCache[right_page_idx]);
150  D_(const leaf * pNewPage = oNewPage.get());
151  left_page_key = _records[records_per_page() / 2]._key;
152  for (size_t i = 1+(records_per_page() / 2); i < records_per_page(); ++i, ++oNewPage->_page_header._count, --_page_header._count){
153  oNewPage->_records[oNewPage->_page_header._count] = _records[i];
154  }
155  _page_header._next_page = right_page_idx;
156  oNewPage->_page_header._prev_page = left_page_idx;
157  }
158 
159  page_header _page_header;
160  record _records[1];
161  };
162 
168  template <typename _KeyT, typename _ValueT, size_t _PageSize>
169  class branch : public data_page<_PageSize>{
172  public:
174  using key_type = _KeyT;
175  using value_type = _ValueT;
176  static const page_type type = page_type::branch_page;
177  struct record{
178  key_type _key;
179  size_t _left;
180  };
181  struct page_header{
182  size_t _count;
183  size_t _right;
184  };
185 
186  page_header _page_header;
187  record _records[1];
188  #if _DEBUG_ME_
189  static inline size_t records_per_page(){
190  return 5;
191  }
192  #else
193  static inline size_t records_per_page(){
194  static size_t iRet = ((_super_t::page_size() - sizeof(page_header) - sizeof(data_page<_PageSize>)) / sizeof(record));
195  return iRet;
196  }
197  #endif
198 
199  template <typename _CacheT>
200  inline bool insert(_CacheT& oCache, const key_type& key, const value_type& value){
201  typename _super_t::pointer oPage;
202  for(;;){
203  TODO("binary search instead of full scan")
204  for (size_t i = 0; i < _page_header._count; ++i){
205  if (key <= _records[i]._key){
206  oPage = static_page_cast<_super_t>(oCache[_records[i]._left]);
207  if (page_type::leaf_page == oPage->_page_type){
208  auto oLeaf = xtd::static_page_cast<leaf_t>(oPage);
209  D_(const leaf_t * pLeaf = oLeaf.get());
210  if (oLeaf->_page_header._count >= leaf_t::records_per_page()){
211  _records[_page_header._count]._left = _records[i]._left;
212  oLeaf->split(oCache, _records[i]._left, _records[_page_header._count]._key, _records[i]._left);
213  std::sort(&_records[0], &_records[_page_header._count], [](const record& lhs, const record& rhs){ return lhs._key < rhs._key; });
214  _page_header._count++;
215  break;
216  }
217  return oLeaf->insert(key, value);
218  } else{
219  auto oBranch = xtd::static_page_cast<branch>(oPage);
220  D_(const branch * pBranch = oBranch.get());
221  if (oBranch->_page_header._count >= branch::records_per_page()){
222  _records[_page_header._count] = _records[i];
223  oBranch->split(oCache, _records[_page_header._count]._key, _records[i]._left);
224  std::sort(&_records[0], &_records[_page_header._count], [](const record& lhs, const record& rhs){ return lhs._key < rhs._key; });
225  _page_header._count++;
226  break;
227  }
228  return oBranch->insert(oCache, key, value);
229  }
230  }
231  }
232  oPage = static_page_cast<_super_t>(oCache[_page_header._right]);
233  if (page_type::leaf_page == oPage->_page_type){
234  auto oLeaf = xtd::static_page_cast<leaf_t>(oPage);
235  D_(const leaf_t * pLeaf = oLeaf.get());
236  if (oLeaf->_page_header._count >= leaf_t::records_per_page()){
237  _records[_page_header._count]._left = _page_header._right;
238  oLeaf->split(oCache, _page_header._right, _records[_page_header._count]._key, _page_header._right);
239  std::sort(&_records[0], &_records[_page_header._count], [](const record& lhs, const record& rhs){ return lhs._key < rhs._key; });
240  _page_header._count++;
241  continue;
242  }
243  return oLeaf->insert(key, value);
244  } else {
245  auto oBranch = xtd::static_page_cast<branch>(oPage);
246  D_(const branch * pBranch = oBranch.get());
247  if (oBranch->_page_header._count < branch::records_per_page()){
248  _records[_page_header._count]._left = _page_header._right;
249  oBranch->split(oCache, _records[_page_header._count]._key, _page_header._right);
250  std::sort(&_records[0], &_records[_page_header._count], [](const record& lhs, const record& rhs){ return lhs._key < rhs._key; });
251  _page_header._count++;
252  continue;
253  }
254  return oBranch->insert(oCache, key, value);
255  }
256  }
257  return false;
258  }
259  template <typename _CacheT>
260  inline void split(_CacheT& oCache, key_type& left_page_key, size_t & right_page_idx){
261  auto oNewPage = static_page_cast<branch>(oCache[right_page_idx]);
262  D_(const branch * pNewPage = oNewPage.get());
263  TODO("convert to memcpy")
264  for (size_t i = 1+records_per_page() / 2; i < records_per_page(); ++i, ++oNewPage->_page_header._count, --_page_header._count){
265  oNewPage->_records[oNewPage->_page_header._count] = _records[i];
266  }
267  left_page_key = _records[records_per_page() / 2]._key;
268  }
269 
270  };
271 
272  }
273  }
274 
279  template <typename _KeyT, typename _ValueT, size_t _PageSize = -1, size_t _CacheSize = 20> class btree{
280  public:
281  using key_type = _KeyT;
282  using value_type = _ValueT;
283 
284  private:
290  using page_type = _::btree::page_type;
291 
292  typename file_header::pointer _file_header;
293  public:
294 
295  btree(const xtd::filesystem::path& oPath) : _cache(oPath), _file_header(static_page_cast<file_header>(_cache[0])){}
296 
303  bool insert(const key_type& key, const value_type& value){
304  //no root page
305  if (0 == _file_header->_root_page){
306  auto oLeaf = static_page_cast<leaf_t>(_cache.append(_file_header->_root_page));
307  D_(const leaf_t * pLeaf = oLeaf.get());
308  if (oLeaf->insert(key, value)){
309  _file_header->_count++;
310  return true;
311  }
312  return false;
313  }
314 
315  auto oRoot = static_page_cast<data_page_t>(_cache[_file_header->_root_page]);
316 
317  if (page_type::branch_page == oRoot->_page_type){ //root is a branch page
318  //root page is full
319  //auto oBranch = oRoot.template cast<branch_t>();
320  auto oBranch = xtd::static_page_cast<branch_t>(oRoot);
321  D_(const branch_t * pBranch = oBranch.get());
322  if (oBranch->_page_header._count >= branch_t::records_per_page()){
323  size_t iNewRoot;
324  auto oNewRoot = static_page_cast<branch_t>(_cache[iNewRoot]);
325  oBranch->split(_cache, oNewRoot->_records[0]._key, oNewRoot->_page_header._right);
326  oNewRoot->_records[0]._left = _file_header->_root_page;
327  oNewRoot->_page_header._count = 1;
328  _file_header->_root_page = iNewRoot;
329  oBranch = std::move(oNewRoot);
330  }
331  // root page is not full
332  if (oBranch->insert(_cache, key, value)){
333  _file_header->_count++;
334  return true;
335  }
336  return false;
337  }
338  // root is a leaf page
339  auto oLeaf = xtd::static_page_cast<leaf_t>(oRoot);
340  D_(const leaf_t * pLeaf = oLeaf.get());
341  //leaf is not full
342  if (oLeaf->_page_header._count < leaf_t::records_per_page()){
343  if (oLeaf->insert(key, value)){
344  _file_header->_count++;
345  return true;
346  }
347  return false;
348  }
349  //leaf is full
350  auto iOldRootIdx = _file_header->_root_page;
351  auto oNewRoot = static_page_cast<branch_t>(_cache[_file_header->_root_page]);
352  oNewRoot->_records[0]._left = iOldRootIdx;
353  oLeaf->split(_cache, iOldRootIdx, oNewRoot->_records[0]._key, oNewRoot->_page_header._right);
354  oNewRoot->_page_header._count = 1;
355  if (oNewRoot->insert(_cache, key, value)){
356  _file_header->_count++;
357  return true;
358  }
359  return false;
360  }
361  };
362 }
btree file header present only on the first page of the file
Definition: btree.hpp:95
memory mapped files
lru cache
B-Tree key-value container.
Definition: btree.hpp:279
host, target and build configurations and settings Various components are purpose built for specific ...
handle necessary filesystem and path functionality until C++17 is finalized
Debugging.
base class of btree pages
Definition: btree.hpp:31
leaf page contains all the keys and values
Definition: btree.hpp:110
branch page contains keys and indexes to more branch or leaf pages
Definition: btree.hpp:169
bool insert(const key_type &key, const value_type &value)
insert a value in the container
Definition: btree.hpp:303