1 : // -*- mode: c++; fill-column: 79 -*-
2 : // $Id: stx-cbtreedb.h 7 2010-04-14 11:24:20Z tb $
3 :
4 : /** \file stx-cbtreedb.h
5 : * Contains all classes of the constant B-tree database implementation.
6 : */
7 :
8 : /*
9 : * STX Constant B-Tree Database Template Classes v0.7.0
10 : * Copyright (C) 2010 Timo Bingmann
11 : *
12 : * This library is free software; you can redistribute it and/or modify it
13 : * under the terms of the GNU Lesser General Public License as published by the
14 : * Free Software Foundation; either version 2.1 of the License, or (at your
15 : * option) any later version.
16 : *
17 : * This library is distributed in the hope that it will be useful, but WITHOUT
18 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 : * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
20 : * for more details.
21 : *
22 : * You should have received a copy of the GNU Lesser General Public License
23 : * along with this library; if not, write to the Free Software Foundation,
24 : * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 : */
26 :
27 : #ifndef _STX_CBTREEDB_H_
28 : #define _STX_CBTREEDB_H_
29 :
30 : #include <string.h>
31 : #include <stdint.h>
32 :
33 : #include <stdexcept>
34 : #include <vector>
35 : #include <map>
36 : #include <iostream>
37 :
38 : /// STX - Some Template Extensions namespace
39 : namespace stx {
40 :
41 : // *** Compile-time assertion macros borrowed from Boost
42 :
43 : #ifndef STX_STATIC_ASSERT
44 :
45 : template <bool> struct STATIC_ASSERTION_FAILURE;
46 : template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
47 : template <int x> struct static_assert_test {};
48 :
49 : #define STX_MACRO_JOIN(X,Y) STX_MACRO_DO_JOIN(X,Y)
50 : #define STX_MACRO_DO_JOIN(X,Y) STX_MACRO_DO_JOIN2(X,Y)
51 : #define STX_MACRO_DO_JOIN2(X,Y) X##Y
52 :
53 : #define STX_STATIC_ASSERT(A) \
54 : typedef static_assert_test<sizeof(STATIC_ASSERTION_FAILURE< static_cast<bool>(A) >)> \
55 : STX_MACRO_JOIN(static_assert_typedef_, __LINE__)
56 :
57 : #endif
58 :
59 : /**
60 : * @brief Template super-class enclosing all classes which can operate on a
61 : * constant B-tree database file.
62 : *
63 : * By parameterizing this class an instance of all sub-classes is created. The
64 : * parameters described important database parameters and database files should
65 : * be read and written using the _same_ class parameters!
66 : *
67 : * The first template parameter is the key type, which must be an integral,
68 : * fixed-length type like uint32_t or a struct.
69 : *
70 : * The second template parameter is the order functional used to sort the keys,
71 : * it must form a total order relation over the keys.
72 : *
73 : * The size of B-tree pages containing nodes are defined by the third
74 : * parameter. The number of key slots in each node is calculated from this
75 : * number and sizeof(_Key). There are some obvious constraints on the
76 : * relationship of page size and key size which are checked by compile-time
77 : * assertions.
78 : *
79 : * The fourth template parameter is an uint32_t version number stored in the
80 : * signature page of the database. It can be used by an application to mark its
81 : * own database. When opening databases this parameter must match the
82 : * signature.
83 : *
84 : * For more information see http://idlebox.net/2010/stx-cbtreedb/
85 : */
86 : template < typename _Key = uint32_t,
87 : typename _Compare = std::less<_Key>,
88 : unsigned int _BTreePageSize = 1024,
89 : uint32_t _AppVersionId = 0>
90 : class CBTreeDB
91 : {
92 : public:
93 : // *** Template Parameters
94 :
95 : /// first template parameter: the key type of the B-tree. This must be an
96 : /// integral, fixed-length type as it is used in arrays in the tree nodes.
97 : typedef _Key key_type;
98 :
99 : /// second template parameter: key comparison function object type.
100 : typedef _Compare key_compare;
101 :
102 : /// third template parameter: B-tree page size. Usually 1024 or 2048.
103 : static const unsigned int BTreePageSize = _BTreePageSize;
104 :
105 : /// fourth template parameter: application-defined 32-bit identifier to
106 : /// mark database format or version.
107 : static const uint32_t AppVersionId = _AppVersionId;
108 :
109 : // *** Structure parameters calculated from page size
110 :
111 : /// size of fields before key+offset array in LeafNode and additional
112 : /// offset at end.
113 : static const unsigned int LeafNodeHead = 2 * sizeof(uint16_t) + sizeof(uint64_t) + sizeof(uint32_t);
114 :
115 : /// number of key+offsets in each LeafNode
116 : static const unsigned int LeafNodeNumKeys = (BTreePageSize - LeafNodeHead) / (sizeof(key_type) + sizeof(uint32_t));
117 :
118 : STX_STATIC_ASSERT( LeafNodeNumKeys >= 1 );
119 :
120 : /// number of unused fill bytes in each LeafNode
121 : static const unsigned int LeafNodeFiller = BTreePageSize - LeafNodeHead - LeafNodeNumKeys * (sizeof(key_type) + sizeof(uint32_t));
122 :
123 : /// size of fields before key array in InnerNode
124 : static const unsigned int InnerNodeHead = 2 * sizeof(uint16_t) + sizeof(uint32_t);
125 :
126 : /// number of keys in each InnerNode
127 : static const unsigned int InnerNodeNumKeys = (BTreePageSize - InnerNodeHead) / sizeof(key_type);
128 :
129 : STX_STATIC_ASSERT( InnerNodeNumKeys >= 2 );
130 :
131 : /// number of unused fill bytes in each InnerNode
132 : static const unsigned int InnerNodeFiller = BTreePageSize - InnerNodeHead - InnerNodeNumKeys * sizeof(key_type);
133 :
134 : public:
135 : /**
136 : * Our own exception class derived from std::runtime_error.
137 : */
138 : class Exception : public std::runtime_error
139 0 : {
140 : public:
141 : /// Create new exception object with error message.
142 0 : explicit Exception(const std::string& str)
143 0 : : std::runtime_error("CBTreeDB: " + str)
144 : {
145 0 : }
146 : };
147 :
148 : /// Instead of assert() we use this macro to throw exceptions. These could
149 : /// be disabled for production releases.
150 : #define CBTREEDB_ASSERT(x) do { if (!(x)) throw(Exception("Assertion failed: " #x)); } while(0)
151 :
152 : /// Short macro to throw exceptions if a program logic expression
153 : /// fails. These must not be disabled in production releases as they may
154 : /// depend on user input.
155 : #define CBTREEDB_CHECK(x,msg) do { if (!(x)) throw(Exception(msg)); } while(0)
156 :
157 : public:
158 : /**
159 : * @brief Signature page which heads all cbtreedb files.
160 : *
161 : * It contains a signature and many important fields to correctly access
162 : * the database file. Due to disk page alignment reasons, the signature
163 : * block is stored with a full B-tree page size.
164 : */
165 : struct SignaturePage
166 : {
167 : char signature[8]; ///< "cbtreedb" or custom string
168 : uint32_t header_crc32; ///< CRC32 of following bytes
169 : uint32_t version; ///< 0x00010000
170 : uint32_t app_version_id; ///< custom id defined by template
171 :
172 : uint32_t items; ///< key-value pairs in db
173 : uint32_t key_size; ///< sizeof(key_type)
174 :
175 : uint64_t btree_offset; ///< b-tree offset in file
176 : uint64_t btree_size; ///< b-tree total size in bytes
177 : uint64_t btree_firstleaf; ///< offset of first leaf in file
178 : uint32_t btree_pagesize; ///< size of b-tree nodes
179 : uint32_t btree_levels; ///< number of levels in tree
180 : uint32_t btree_leaves; ///< number of leaf nodes in tree
181 : uint8_t btree_sha256[32]; ///< SHA256 digest of all tree nodes
182 :
183 : uint64_t value_offset; ///< file offset of value data area
184 : uint64_t value_size; ///< total size of value data area
185 : uint8_t value_sha256[32]; ///< SHA256 digest of all value data
186 : }
187 : __attribute__((packed));
188 :
189 : /// Fixed signature page size: always equal to the B-tree page.
190 : static const unsigned int SignaturePageSize = BTreePageSize;
191 :
192 : STX_STATIC_ASSERT( sizeof(SignaturePage) <= BTreePageSize );
193 :
194 : protected:
195 : /**
196 : * @brief Leaf node structure of the B-tree leaf pages.
197 : *
198 : * Each leaf node contains a key array and an array of relative value
199 : * offsets. It does not contain the size of the value elements, because
200 : * these can be computed from two successive relative offsets. This works
201 : * for the last offset only because of the extra offset of the successor
202 : * value item in the next leaf. The value offsets are relative to a
203 : * starting 64-bit offset, because all leaf's data items are stored in
204 : * order.
205 : */
206 : struct LeafNode
207 : {
208 : /// level of this leaf node -> always 0.
209 : uint16_t level;
210 :
211 : /// number of used slots in the arrays.
212 : uint16_t slots;
213 :
214 : /// base of value offsets enumerated in array.
215 : uint64_t baseoffset;
216 :
217 : union
218 : {
219 : struct
220 : {
221 : /// key array of ascending key in this leaf.
222 : key_type keys[LeafNodeNumKeys];
223 :
224 : /// file offset of value data associated with key.
225 : uint32_t offsets[LeafNodeNumKeys+1];
226 : }
227 : __attribute__((packed));
228 :
229 : /// union with filler char array to assure page size.
230 : char filler[BTreePageSize - LeafNodeHead + sizeof(uint32_t)];
231 : };
232 :
233 : /// Initializes structure with zero.
234 9438 : inline explicit LeafNode()
235 9438 : : level(0), slots(0), baseoffset(0)
236 : {
237 9438 : memset(filler, 0, sizeof(filler));
238 9438 : }
239 :
240 : /// Returns true if no more keys can be added.
241 1186941 : inline bool IsFull() const
242 : {
243 1186941 : return (slots >= LeafNodeNumKeys);
244 : }
245 :
246 : /// Returns the currently last key in the node
247 10447433 : inline const key_type& LastKey() const
248 : {
249 10447433 : CBTREEDB_ASSERT(slots > 0 && slots < LeafNodeNumKeys+1);
250 10447433 : return keys[slots-1];
251 : }
252 : }
253 : __attribute__((packed));
254 :
255 : STX_STATIC_ASSERT( sizeof(LeafNode) == BTreePageSize );
256 :
257 : /**
258 : * @brief Inner node structure of the B-tree inner pages.
259 : *
260 : * Each inner node has n+1 children nodes, where n is the number of keys in
261 : * the node. The n+1 children nodes are stored consecutively starting at
262 : * childrenoffset.
263 : */
264 : struct InnerNode
265 : {
266 : /// level of this inner node, always > 0.
267 : uint16_t level;
268 :
269 : /// number of used slots in the arrays.
270 : uint16_t slots;
271 :
272 : /// base offset of child B-tree nodes enumerated by keys.
273 : uint32_t childrenoffset;
274 :
275 : union
276 : {
277 : /// key array of ascending keys in this inner node.
278 : key_type keys[InnerNodeNumKeys];
279 :
280 : /// union with filler char array to assure page size.
281 : char filler[BTreePageSize - InnerNodeHead];
282 : };
283 :
284 : /// Initializes structure with zero.
285 77 : inline explicit InnerNode(uint16_t level_)
286 77 : : level(level_), slots(0), childrenoffset(0)
287 : {
288 77 : memset(filler, 0, sizeof(filler));
289 77 : }
290 :
291 : /// Returns true if no more keys can be added.
292 9400 : inline bool IsFull() const
293 : {
294 9400 : return (slots >= InnerNodeNumKeys);
295 : }
296 :
297 : /// Returns the currently last key in the node
298 7977574 : inline const key_type& LastKey() const
299 : {
300 7977574 : CBTREEDB_ASSERT(slots > 0 && slots < InnerNodeNumKeys+1);
301 7977574 : return keys[slots-1];
302 : }
303 : }
304 : __attribute__((packed));
305 :
306 : STX_STATIC_ASSERT( sizeof(InnerNode) == BTreePageSize );
307 :
308 : protected:
309 : /**
310 : * @brief CRC32 Cyclic redundancy check implementation class.
311 : *
312 : * Copied from the Botan-1.6.4 cryptography library.
313 : */
314 : class CRC32
315 : {
316 : private:
317 : /// CRC intermediate value
318 : uint32_t m_crc;
319 :
320 : public:
321 : /// Initialize new CRC object
322 146 : CRC32()
323 : {
324 146 : clear();
325 146 : }
326 :
327 : /// Clear current CRC object
328 146 : void clear()
329 : {
330 146 : m_crc = 0xFFFFFFFF;
331 146 : }
332 :
333 : /// Update this CRC value with new data.
334 146 : CRC32& update(const unsigned char* input, uint32_t length)
335 : {
336 : static const uint32_t table[256] = {
337 : 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419,
338 : 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4,
339 : 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07,
340 : 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
341 : 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856,
342 : 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
343 : 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
344 : 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
345 : 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3,
346 : 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A,
347 : 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599,
348 : 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
349 : 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190,
350 : 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,
351 : 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E,
352 : 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
353 : 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
354 : 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
355 : 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3,
356 : 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
357 : 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,
358 : 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5,
359 : 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010,
360 : 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
361 : 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17,
362 : 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6,
363 : 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
364 : 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
365 : 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344,
366 : 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
367 : 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A,
368 : 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
369 : 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1,
370 : 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
371 : 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,
372 : 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
373 : 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE,
374 : 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31,
375 : 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C,
376 : 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
377 : 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B,
378 : 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
379 : 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1,
380 : 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
381 : 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278,
382 : 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7,
383 : 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66,
384 : 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
385 : 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
386 : 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8,
387 : 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
388 : 0x2D02EF8D };
389 :
390 146 : register uint32_t tmp = m_crc;
391 :
392 1432 : while(length >= 16)
393 : {
394 1140 : tmp = table[(tmp ^ input[ 0]) & 0xFF] ^ (tmp >> 8);
395 1140 : tmp = table[(tmp ^ input[ 1]) & 0xFF] ^ (tmp >> 8);
396 1140 : tmp = table[(tmp ^ input[ 2]) & 0xFF] ^ (tmp >> 8);
397 1140 : tmp = table[(tmp ^ input[ 3]) & 0xFF] ^ (tmp >> 8);
398 1140 : tmp = table[(tmp ^ input[ 4]) & 0xFF] ^ (tmp >> 8);
399 1140 : tmp = table[(tmp ^ input[ 5]) & 0xFF] ^ (tmp >> 8);
400 1140 : tmp = table[(tmp ^ input[ 6]) & 0xFF] ^ (tmp >> 8);
401 1140 : tmp = table[(tmp ^ input[ 7]) & 0xFF] ^ (tmp >> 8);
402 1140 : tmp = table[(tmp ^ input[ 8]) & 0xFF] ^ (tmp >> 8);
403 1140 : tmp = table[(tmp ^ input[ 9]) & 0xFF] ^ (tmp >> 8);
404 1140 : tmp = table[(tmp ^ input[10]) & 0xFF] ^ (tmp >> 8);
405 1140 : tmp = table[(tmp ^ input[11]) & 0xFF] ^ (tmp >> 8);
406 1140 : tmp = table[(tmp ^ input[12]) & 0xFF] ^ (tmp >> 8);
407 1140 : tmp = table[(tmp ^ input[13]) & 0xFF] ^ (tmp >> 8);
408 1140 : tmp = table[(tmp ^ input[14]) & 0xFF] ^ (tmp >> 8);
409 1140 : tmp = table[(tmp ^ input[15]) & 0xFF] ^ (tmp >> 8);
410 1140 : input += 16;
411 1140 : length -= 16;
412 : }
413 :
414 175 : for(uint32_t j = 0; j != length; ++j)
415 29 : tmp = table[(tmp ^ input[j]) & 0xFF] ^ (tmp >> 8);
416 :
417 146 : m_crc = tmp;
418 :
419 146 : return *this;
420 : }
421 :
422 : /// Update this CRC value with new data.
423 146 : CRC32& update(const void* input, uint32_t length)
424 : {
425 146 : return update(reinterpret_cast<const unsigned char*>(input), length);
426 : }
427 :
428 : /// Return final CRC value of this object.
429 146 : uint32_t final() const
430 : {
431 146 : return (m_crc ^ 0xFFFFFFFF);
432 : }
433 :
434 : /// Calculate CRC32 digest of bytes in the given range.
435 146 : static uint32_t digest(const void* input, uint32_t length)
436 : {
437 146 : return CRC32().update(input, length).final();
438 : }
439 :
440 : /// Calculate CRC32 digest of a string.
441 4 : static uint32_t digest(const std::string& input)
442 : {
443 4 : return digest(input.data(), input.size());
444 : }
445 : };
446 :
447 : protected:
448 : /**
449 : * @brief SHA-256 Message Digest implementation class.
450 : *
451 : * Copied from the Botan-1.6.4 cryptography library.
452 : */
453 : class SHA256
454 : {
455 : private:
456 : /// local typedef from Botan library
457 : typedef uint8_t byte;
458 :
459 : /// local typedef from Botan library
460 : typedef uint32_t u32bit;
461 :
462 : /// local typedef from Botan library
463 : typedef uint64_t u64bit;
464 :
465 : /// length of the resulting digest
466 : static const u32bit OUTPUT_LENGTH = 32;
467 :
468 : /// block of bytes to process in hash function
469 : static const u32bit HASH_BLOCK_SIZE = 64;
470 :
471 : /// length of size suffix hashed during finalization
472 : static const u32bit COUNT_SIZE = 8;
473 :
474 : private:
475 : u32bit W[64];
476 : u32bit digest[8];
477 :
478 : byte buffer[HASH_BLOCK_SIZE];
479 :
480 : u64bit count;
481 : u32bit position;
482 :
483 : private:
484 : /// Rotation Functions
485 : template<typename T> inline T rotate_left(T input, u32bit rot)
486 : { return static_cast<T>((input << rot) | (input >> (8*sizeof(T)-rot))); }
487 :
488 : /// Rotation Functions
489 387081216 : template<typename T> inline T rotate_right(T input, u32bit rot)
490 387081216 : { return static_cast<T>((input >> rot) | (input << (8*sizeof(T)-rot))); }
491 :
492 : /// Byte Extraction Function
493 16240 : template<typename T> inline byte get_byte(u32bit byte_num, T input)
494 16240 : { return static_cast<byte>(input >> ((sizeof(T)-1-(byte_num&(sizeof(T)-1))) << 3)); }
495 :
496 : /// Byte to Word Conversions
497 10752256 : inline u32bit make_u32bit(byte input0, byte input1, byte input2, byte input3)
498 : { return static_cast<u32bit>((static_cast<u32bit>(input0) << 24) |
499 : (static_cast<u32bit>(input1) << 16) |
500 10752256 : (static_cast<u32bit>(input2) << 8) | input3); }
501 :
502 : /// SHA-256 Rho Function
503 86018048 : inline u32bit rho(u32bit X, u32bit rot1, u32bit rot2, u32bit rot3)
504 : {
505 : return (rotate_right(X, rot1) ^ rotate_right(X, rot2) ^
506 86018048 : rotate_right(X, rot3));
507 : }
508 :
509 : /// SHA-256 Sigma Function
510 64513536 : inline u32bit sigma(u32bit X, u32bit rot1, u32bit rot2, u32bit shift)
511 : {
512 64513536 : return (rotate_right(X, rot1) ^ rotate_right(X, rot2) ^ (X >> shift));
513 : }
514 :
515 : /// SHA-256 F1 Function
516 : inline void F1(u32bit A, u32bit B, u32bit C, u32bit& D,
517 : u32bit E, u32bit F, u32bit G, u32bit& H,
518 43009024 : u32bit msg, u32bit magic)
519 : {
520 43009024 : magic += rho(E, 6, 11, 25) + ((E & F) ^ (~E & G)) + msg;
521 43009024 : D += magic + H;
522 43009024 : H += magic + rho(A, 2, 13, 22) + ((A & B) ^ (A & C) ^ (B & C));
523 43009024 : }
524 :
525 : /// SHA-256 Compression Function
526 672016 : void hash(const byte input[])
527 : {
528 11424272 : for(u32bit j = 0; j != 16; ++j)
529 10752256 : W[j] = make_u32bit(input[4*j], input[4*j+1], input[4*j+2], input[4*j+3]);
530 32928784 : for(u32bit j = 16; j != 64; ++j)
531 32256768 : W[j] = sigma(W[j- 2], 17, 19, 10) + W[j- 7] +
532 : sigma(W[j-15], 7, 18, 3) + W[j-16];
533 :
534 672016 : u32bit A = digest[0], B = digest[1], C = digest[2],
535 672016 : D = digest[3], E = digest[4], F = digest[5],
536 672016 : G = digest[6], H = digest[7];
537 :
538 672016 : F1(A,B,C,D,E,F,G,H,W[ 0],0x428A2F98);
539 672016 : F1(H,A,B,C,D,E,F,G,W[ 1],0x71374491);
540 672016 : F1(G,H,A,B,C,D,E,F,W[ 2],0xB5C0FBCF);
541 672016 : F1(F,G,H,A,B,C,D,E,W[ 3],0xE9B5DBA5);
542 672016 : F1(E,F,G,H,A,B,C,D,W[ 4],0x3956C25B);
543 672016 : F1(D,E,F,G,H,A,B,C,W[ 5],0x59F111F1);
544 672016 : F1(C,D,E,F,G,H,A,B,W[ 6],0x923F82A4);
545 672016 : F1(B,C,D,E,F,G,H,A,W[ 7],0xAB1C5ED5);
546 672016 : F1(A,B,C,D,E,F,G,H,W[ 8],0xD807AA98);
547 672016 : F1(H,A,B,C,D,E,F,G,W[ 9],0x12835B01);
548 672016 : F1(G,H,A,B,C,D,E,F,W[10],0x243185BE);
549 672016 : F1(F,G,H,A,B,C,D,E,W[11],0x550C7DC3);
550 672016 : F1(E,F,G,H,A,B,C,D,W[12],0x72BE5D74);
551 672016 : F1(D,E,F,G,H,A,B,C,W[13],0x80DEB1FE);
552 672016 : F1(C,D,E,F,G,H,A,B,W[14],0x9BDC06A7);
553 672016 : F1(B,C,D,E,F,G,H,A,W[15],0xC19BF174);
554 672016 : F1(A,B,C,D,E,F,G,H,W[16],0xE49B69C1);
555 672016 : F1(H,A,B,C,D,E,F,G,W[17],0xEFBE4786);
556 672016 : F1(G,H,A,B,C,D,E,F,W[18],0x0FC19DC6);
557 672016 : F1(F,G,H,A,B,C,D,E,W[19],0x240CA1CC);
558 672016 : F1(E,F,G,H,A,B,C,D,W[20],0x2DE92C6F);
559 672016 : F1(D,E,F,G,H,A,B,C,W[21],0x4A7484AA);
560 672016 : F1(C,D,E,F,G,H,A,B,W[22],0x5CB0A9DC);
561 672016 : F1(B,C,D,E,F,G,H,A,W[23],0x76F988DA);
562 672016 : F1(A,B,C,D,E,F,G,H,W[24],0x983E5152);
563 672016 : F1(H,A,B,C,D,E,F,G,W[25],0xA831C66D);
564 672016 : F1(G,H,A,B,C,D,E,F,W[26],0xB00327C8);
565 672016 : F1(F,G,H,A,B,C,D,E,W[27],0xBF597FC7);
566 672016 : F1(E,F,G,H,A,B,C,D,W[28],0xC6E00BF3);
567 672016 : F1(D,E,F,G,H,A,B,C,W[29],0xD5A79147);
568 672016 : F1(C,D,E,F,G,H,A,B,W[30],0x06CA6351);
569 672016 : F1(B,C,D,E,F,G,H,A,W[31],0x14292967);
570 672016 : F1(A,B,C,D,E,F,G,H,W[32],0x27B70A85);
571 672016 : F1(H,A,B,C,D,E,F,G,W[33],0x2E1B2138);
572 672016 : F1(G,H,A,B,C,D,E,F,W[34],0x4D2C6DFC);
573 672016 : F1(F,G,H,A,B,C,D,E,W[35],0x53380D13);
574 672016 : F1(E,F,G,H,A,B,C,D,W[36],0x650A7354);
575 672016 : F1(D,E,F,G,H,A,B,C,W[37],0x766A0ABB);
576 672016 : F1(C,D,E,F,G,H,A,B,W[38],0x81C2C92E);
577 672016 : F1(B,C,D,E,F,G,H,A,W[39],0x92722C85);
578 672016 : F1(A,B,C,D,E,F,G,H,W[40],0xA2BFE8A1);
579 672016 : F1(H,A,B,C,D,E,F,G,W[41],0xA81A664B);
580 672016 : F1(G,H,A,B,C,D,E,F,W[42],0xC24B8B70);
581 672016 : F1(F,G,H,A,B,C,D,E,W[43],0xC76C51A3);
582 672016 : F1(E,F,G,H,A,B,C,D,W[44],0xD192E819);
583 672016 : F1(D,E,F,G,H,A,B,C,W[45],0xD6990624);
584 672016 : F1(C,D,E,F,G,H,A,B,W[46],0xF40E3585);
585 672016 : F1(B,C,D,E,F,G,H,A,W[47],0x106AA070);
586 672016 : F1(A,B,C,D,E,F,G,H,W[48],0x19A4C116);
587 672016 : F1(H,A,B,C,D,E,F,G,W[49],0x1E376C08);
588 672016 : F1(G,H,A,B,C,D,E,F,W[50],0x2748774C);
589 672016 : F1(F,G,H,A,B,C,D,E,W[51],0x34B0BCB5);
590 672016 : F1(E,F,G,H,A,B,C,D,W[52],0x391C0CB3);
591 672016 : F1(D,E,F,G,H,A,B,C,W[53],0x4ED8AA4A);
592 672016 : F1(C,D,E,F,G,H,A,B,W[54],0x5B9CCA4F);
593 672016 : F1(B,C,D,E,F,G,H,A,W[55],0x682E6FF3);
594 672016 : F1(A,B,C,D,E,F,G,H,W[56],0x748F82EE);
595 672016 : F1(H,A,B,C,D,E,F,G,W[57],0x78A5636F);
596 672016 : F1(G,H,A,B,C,D,E,F,W[58],0x84C87814);
597 672016 : F1(F,G,H,A,B,C,D,E,W[59],0x8CC70208);
598 672016 : F1(E,F,G,H,A,B,C,D,W[60],0x90BEFFFA);
599 672016 : F1(D,E,F,G,H,A,B,C,W[61],0xA4506CEB);
600 672016 : F1(C,D,E,F,G,H,A,B,W[62],0xBEF9A3F7);
601 672016 : F1(B,C,D,E,F,G,H,A,W[63],0xC67178F2);
602 :
603 672016 : digest[0] += A; digest[1] += B; digest[2] += C;
604 672016 : digest[3] += D; digest[4] += E; digest[5] += F;
605 672016 : digest[6] += G; digest[7] += H;
606 672016 : }
607 :
608 : /// Copy out the digest
609 406 : void copy_out(byte output[])
610 : {
611 13398 : for(u32bit j = 0; j != OUTPUT_LENGTH; ++j)
612 12992 : output[j] = get_byte(j % 4, digest[j/4]);
613 406 : }
614 :
615 : protected:
616 :
617 : /// Update the hash
618 1215328 : void add_data(const byte input[], u32bit length)
619 : {
620 1215328 : count += length;
621 :
622 1215328 : if (position)
623 : {
624 1112696 : memcpy(buffer + position, input,
625 : std::min<u32bit>(length, sizeof(buffer) - position));
626 :
627 1112696 : if(position + length >= HASH_BLOCK_SIZE)
628 : {
629 74176 : hash(buffer);
630 74176 : input += (HASH_BLOCK_SIZE - position);
631 74176 : length -= (HASH_BLOCK_SIZE - position);
632 74176 : position = 0;
633 : }
634 : }
635 :
636 3028090 : while(length >= HASH_BLOCK_SIZE)
637 : {
638 597434 : hash(input);
639 597434 : input += HASH_BLOCK_SIZE;
640 597434 : length -= HASH_BLOCK_SIZE;
641 : }
642 :
643 1215328 : memcpy(buffer + position, input,
644 : std::min<u32bit>(length, sizeof(buffer) - position));
645 :
646 1215328 : position += length;
647 1215328 : }
648 :
649 : /// Write the count bits to the buffer
650 406 : void write_count(byte out[])
651 : {
652 3654 : for(u32bit j = 0; j != 8; ++j)
653 : {
654 3248 : out[j+COUNT_SIZE-8] = get_byte(j % 8, 8 * count);
655 : }
656 406 : }
657 :
658 : /// Finalize a Hash
659 406 : void final_result(byte output[OUTPUT_LENGTH])
660 : {
661 406 : buffer[position] = 0x80;
662 24415 : for(u32bit j = position+1; j != HASH_BLOCK_SIZE; ++j)
663 24009 : buffer[j] = 0;
664 406 : if(position >= HASH_BLOCK_SIZE - COUNT_SIZE)
665 : {
666 0 : hash(buffer);
667 0 : memset(buffer, 0, sizeof(buffer));
668 : }
669 406 : write_count(buffer + HASH_BLOCK_SIZE - COUNT_SIZE);
670 :
671 406 : hash(buffer);
672 406 : copy_out(output);
673 406 : clear();
674 406 : }
675 :
676 : public:
677 : /// SHA_256 / MDx_HashFunction Constructor
678 406 : SHA256()
679 : {
680 406 : clear();
681 406 : }
682 :
683 : /// Clear memory of sensitive data
684 844 : void clear() throw()
685 : {
686 844 : memset(buffer, 0, sizeof(buffer));
687 844 : count = position = 0;
688 :
689 844 : memset(W, 0, sizeof(W));
690 844 : digest[0] = 0x6A09E667;
691 844 : digest[1] = 0xBB67AE85;
692 844 : digest[2] = 0x3C6EF372;
693 844 : digest[3] = 0xA54FF53A;
694 844 : digest[4] = 0x510E527F;
695 844 : digest[5] = 0x9B05688C;
696 844 : digest[6] = 0x1F83D9AB;
697 844 : digest[7] = 0x5BE0CD19;
698 844 : }
699 :
700 : /// Update this SHA256 calculation with new data.
701 1215328 : SHA256& update(const void* input, uint32_t length)
702 : {
703 1215328 : add_data(reinterpret_cast<const unsigned char*>(input), length);
704 1215328 : return *this;
705 : }
706 :
707 : /// Return final SHA256 digest of this object in the buffer.
708 138 : void final(byte output[OUTPUT_LENGTH])
709 : {
710 138 : final_result(output);
711 138 : }
712 :
713 : /// Return final SHA256 digest of this object as a string.
714 0 : std::string final()
715 : {
716 : byte result[OUTPUT_LENGTH];
717 0 : final_result(result);
718 0 : return std::string(reinterpret_cast<char*>(result), OUTPUT_LENGTH);
719 : }
720 :
721 : /// Returns true if the final SHA256 digest of this object equals the
722 : /// given data.
723 264 : bool final_equals(byte compare[OUTPUT_LENGTH])
724 : {
725 : byte result[OUTPUT_LENGTH];
726 264 : final_result(result);
727 264 : return (memcmp(compare, result, OUTPUT_LENGTH) == 0);
728 : }
729 :
730 : /// Return final SHA256 digest of this object as a string encoded in
731 : /// hexadecimal.
732 4 : std::string final_hex()
733 : {
734 : byte result[OUTPUT_LENGTH];
735 4 : final_result(result);
736 :
737 4 : std::string out(OUTPUT_LENGTH*2, '\0');
738 :
739 : static const char xdigits[16] = {
740 : '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
741 : };
742 :
743 4 : std::string::iterator oi = out.begin();
744 132 : for (unsigned int i = 0; i < OUTPUT_LENGTH; ++i)
745 : {
746 128 : *oi++ = xdigits[ (result[i] & 0xF0) >> 4 ];
747 128 : *oi++ = xdigits[ (result[i] & 0x0F) ];
748 : }
749 :
750 0 : return out;
751 : }
752 :
753 : /// Calculate SHA256 digest of bytes in the given range.
754 0 : static std::string digest_bin(const void* input, uint32_t length)
755 : {
756 0 : return SHA256().update(input, length).final();
757 : }
758 :
759 : /// Calculate SHA256 digest of a string.
760 0 : static std::string digest_bin(const std::string& input)
761 : {
762 0 : return digest_bin(input.data(), input.size());
763 : }
764 :
765 : /// Calculate SHA256 digest of bytes in the given range. Result is
766 : /// encoded in hexadecimal.
767 4 : static std::string digest_hex(const void* input, uint32_t length)
768 : {
769 4 : return SHA256().update(input, length).final_hex();
770 : }
771 :
772 : /// Calculate SHA256 digest of a string. Result is encoded in
773 : /// hexadecimal.
774 4 : static std::string digest_hex(const std::string& input)
775 : {
776 4 : return digest_hex(input.data(), input.size());
777 : }
778 : };
779 :
780 : protected:
781 : /**
782 : * @brief BTreePage is a reference-counted buffer holding one page of the
783 : * B-tree index.
784 : *
785 : * Note that this wrapper object may also contain an invalid/uninitialized
786 : * page pointer. The enclosed data can be casted to either a LeafNode
787 : * object or an InnerNode object. The corresponding cast direction is
788 : * checked against the page's level number..
789 : */
790 : class BTreePage
791 : {
792 : protected:
793 : /**
794 : * @brief Implementation of BTreePage: holds the data buffer and a
795 : * reference counter.
796 : */
797 : struct Impl
798 : {
799 : /// reference counter
800 : unsigned int refs;
801 :
802 : /// data buffer
803 : char data[BTreePageSize];
804 : };
805 :
806 : /// pointer to reference-counted data buffer object.
807 : struct Impl* m_impl;
808 :
809 : public:
810 : /// Default Constructor: create new invalid page buffer
811 30012527 : BTreePage()
812 30012527 : : m_impl(NULL)
813 : {
814 30012527 : }
815 :
816 : /// Copy Constructor: increment reference counter on buffer.
817 0 : BTreePage(const BTreePage& btp)
818 0 : : m_impl(btp.m_impl)
819 : {
820 0 : if (m_impl)
821 0 : ++m_impl->refs;
822 0 : }
823 :
824 : /// Destructor: decrement reference counter on buffer and possibly
825 : /// deallocate it.
826 30012527 : ~BTreePage()
827 : {
828 30012527 : if (m_impl && --m_impl->refs == 0)
829 60612 : delete m_impl;
830 30012527 : }
831 :
832 : /// Assignment Operator: increment reference counter on buffer.
833 47249775 : BTreePage& operator=(const BTreePage& btp)
834 : {
835 47249775 : if (this != &btp)
836 : {
837 47249775 : if (m_impl && --m_impl->refs == 0)
838 0 : delete m_impl;
839 :
840 47249775 : m_impl = btp.m_impl;
841 :
842 47249775 : if (m_impl)
843 47249072 : ++m_impl->refs;
844 : }
845 :
846 47249775 : return *this;
847 : }
848 :
849 : /// Determine whether the wrapper object contains valid page.
850 0 : bool IsValid() const
851 : {
852 0 : return (m_impl != NULL);
853 : }
854 :
855 : /// Release enclosed page and initialize a new page buffer.
856 60612 : void Create()
857 : {
858 60612 : if (m_impl && --m_impl->refs == 0)
859 0 : delete m_impl;
860 :
861 60612 : m_impl = new Impl;
862 60612 : m_impl->refs = 1;
863 60612 : }
864 :
865 : /// Accessor: return enclosed buffer pointer.
866 79216 : char* GetBuffer()
867 : {
868 79216 : CBTREEDB_ASSERT(m_impl);
869 79216 : return m_impl->data;
870 : }
871 :
872 : /// Return the enclosed node's level in the tree.
873 77163192 : uint16_t GetLevel() const
874 : {
875 77163192 : CBTREEDB_ASSERT(m_impl);
876 77163192 : return reinterpret_cast<InnerNode*>(m_impl->data)->level;
877 : }
878 :
879 : /// Returns true if the buffer contains a leaf node.
880 59865448 : bool IsLeafNode() const
881 : {
882 59865448 : return (GetLevel() == 0);
883 : }
884 :
885 : /// Return buffer casted as an inner node.
886 17297933 : InnerNode* GetAsInnerNode() const
887 : {
888 17297933 : CBTREEDB_ASSERT(m_impl && !IsLeafNode());
889 17297933 : return reinterpret_cast<InnerNode*>(m_impl->data);
890 : }
891 :
892 : /// Return buffer casted as a leaf node.
893 12634791 : LeafNode* GetAsLeafNode() const
894 : {
895 12634791 : CBTREEDB_ASSERT(m_impl && IsLeafNode());
896 12634791 : return reinterpret_cast<LeafNode*>(m_impl->data);
897 : }
898 : };
899 :
900 : protected:
901 : /**
902 : * @brief PageCache and PageCacheImpl implement a LRU-strategy cache of
903 : * B-tree pages used by CBTreeDB reader objects.
904 : *
905 : * One cache object can be shared between multiple readers. However, this
906 : * page cache is not thread safe. You may have to wrap some mutex libraries
907 : * if needed.
908 : *
909 : * The cached pages are put into a hash table for quick lookup by
910 : * (btreeid,pageid). Simultaneously the HashCells are linked into a doubly
911 : * chained "LRU"-list with the most recently used page at the head. This
912 : * allows O(1) algorithms for both Store() and Retrieve() functions. When
913 : * the maximum number of pages is exceeded, the tail pages of the LRU-list
914 : * are removed. The drawing below illustrates the data structure used by
915 : * the class.
916 : *
917 : * \htmlonly
918 : * <div style="text-align: center">
919 : * <p>Structure of PageCache's arrays and nodes</p>
920 : * <object type="image/svg+xml" data="drawing-1.svg" style="height: 25em"></object>
921 : * </div>
922 : * \endhtmlonly
923 : */
924 : class PageCacheImpl
925 : {
926 : protected:
927 :
928 : /// reference counter
929 : unsigned int m_refs;
930 :
931 : /// maximum number of pages in cache
932 : unsigned int m_maxsize;
933 :
934 : /// current number of pages in cache
935 : unsigned int m_size;
936 :
937 : #ifdef CBTREEDB_SELF_VERIFY
938 : /**
939 : * @brief counter to tag pages with a virtual LRU timestamp.
940 : * This is just for verification purposes and is only used in the
941 : * testsuite as the counter may overflow in real applications.
942 : */
943 : uint32_t m_lrutime;
944 : #endif
945 :
946 : /// Structure for each slot in the cache hash array
947 : struct HashCell
948 122394 : {
949 : /// pointer forward to next hash cell in bucket
950 : struct HashCell *bucket_next;
951 :
952 : /// pointer backward to previous hash cell in bucket
953 : struct HashCell *bucket_prev;
954 :
955 : /// pointer forward in LRU double-linked list
956 : struct HashCell *list_next;
957 :
958 : /// pointer backward in LRU double-linked list
959 : struct HashCell *list_prev;
960 :
961 : #ifdef CBTREEDB_SELF_VERIFY
962 : /// virtual LRU timestamp, just for testing.
963 : uint32_t lrutime;
964 : #endif
965 :
966 : /// b-tree object identifier of page
967 : void* btreeid;
968 :
969 : /// page identifier withing b-tree
970 : uint32_t pageid;
971 :
972 : /// page holder object
973 : BTreePage page;
974 : };
975 :
976 : /// hash cell array holding pointers to active cells
977 : std::vector<struct HashCell*> m_hasharray;
978 :
979 : /**
980 : * @brief sentinel hash cell for LRU double-linked list.
981 : * list_next is the head and list_prev is the tail of the list.
982 : */
983 : struct HashCell m_sentinel;
984 :
985 : /// Simple hash function mapping (btreeid,pageid) -> bucket.
986 30012952 : inline unsigned int hashfunc(void* btreeid, uint32_t pageid)
987 : {
988 : // since hot pageids are usually ascending, I guess this is a
989 : // pretty good hash function.
990 30012952 : return (reinterpret_cast<uintptr_t>(btreeid) + pageid) % m_hasharray.size();
991 : }
992 :
993 : public:
994 : /// Create a new page cache containg maxsize pages
995 75 : explicit PageCacheImpl(unsigned int maxsize)
996 75 : : m_refs(0), m_maxsize(maxsize), m_size(0)
997 : {
998 75 : m_hasharray.resize(m_maxsize / 2, NULL);
999 :
1000 75 : m_sentinel.list_prev = m_sentinel.list_next = &m_sentinel;
1001 : #ifdef CBTREEDB_SELF_VERIFY
1002 75 : m_lrutime = 0;
1003 75 : m_sentinel.lrutime = 0;
1004 : #endif
1005 75 : }
1006 :
1007 : /// Removes all cached pages and destroys cache.
1008 75 : ~PageCacheImpl()
1009 : {
1010 75 : Clear();
1011 75 : }
1012 :
1013 : /// Increment reference counter by one.
1014 75 : void RefInc()
1015 : {
1016 75 : ++m_refs;
1017 75 : }
1018 :
1019 : /// Decrement reference counter by one and return it.
1020 75 : unsigned int RefDec()
1021 : {
1022 75 : return --m_refs;
1023 : }
1024 :
1025 : /// Remove all pages from the cache and reset status.
1026 75 : void Clear()
1027 : {
1028 : // free up hash cells
1029 75 : struct HashCell* hc = m_sentinel.list_next;
1030 2082 : while (hc != &m_sentinel)
1031 : {
1032 1932 : struct HashCell* nc = hc->list_next;
1033 1932 : delete hc;
1034 1932 : hc = nc;
1035 : }
1036 :
1037 : // zero hash array
1038 4767 : for (unsigned int i = 0; i < m_hasharray.size(); ++i)
1039 4692 : m_hasharray[i] = NULL;
1040 :
1041 75 : m_sentinel.list_prev = m_sentinel.list_next = &m_sentinel;
1042 75 : m_size = 0;
1043 : #ifdef CBTREEDB_SELF_VERIFY
1044 75 : m_sentinel.lrutime = 0;
1045 75 : m_lrutime = 0;
1046 : #endif
1047 75 : }
1048 :
1049 : /// Store a page object in a cache cell identified by (btreeid,pageid).
1050 61123 : void Store(void* btreeid, uint32_t pageid, const BTreePage& page)
1051 : {
1052 : // check whether its already in the cache
1053 :
1054 61123 : unsigned int h = hashfunc(btreeid, pageid);
1055 :
1056 61123 : struct HashCell* hc = m_hasharray[h];
1057 :
1058 7729236 : while( hc && ! (hc->btreeid == btreeid && hc->pageid == pageid) )
1059 : {
1060 : // advance in bucket list
1061 7606990 : hc = hc->bucket_next;
1062 : }
1063 :
1064 61123 : if ( hc ) // found in cache: unlink from LRU list and place in front
1065 : {
1066 1 : if (hc != m_sentinel.list_next)
1067 : {
1068 : // remove cell wherever it is
1069 1 : hc->list_next->list_prev = hc->list_prev;
1070 1 : hc->list_prev->list_next = hc->list_next;
1071 :
1072 : // place at head
1073 1 : hc->list_prev = &m_sentinel;
1074 1 : hc->list_next = m_sentinel.list_next;
1075 1 : m_sentinel.list_next->list_prev = hc;
1076 1 : m_sentinel.list_next = hc;
1077 :
1078 : // copy page, it may have changed
1079 1 : hc->page = page;
1080 :
1081 : #ifdef CBTREEDB_SELF_VERIFY
1082 1 : hc->lrutime = ++m_lrutime;
1083 : #endif
1084 : }
1085 : // else hc is the head -> do nothing.
1086 : }
1087 : else // not found in cache.
1088 : {
1089 : // remove last page in LRU list if necessary
1090 181434 : while (m_size >= m_maxsize)
1091 : {
1092 59190 : struct HashCell* lc = m_sentinel.list_prev;
1093 :
1094 59190 : CBTREEDB_ASSERT( lc != &m_sentinel );
1095 :
1096 : // unlink from bucket list
1097 59190 : if (reinterpret_cast<uintptr_t>(lc->bucket_prev) > m_hasharray.size())
1098 : {
1099 59144 : if (lc->bucket_next)
1100 17394 : lc->bucket_next->bucket_prev = lc->bucket_prev;
1101 :
1102 59144 : lc->bucket_prev->bucket_next = lc->bucket_next;
1103 : }
1104 : else // at first place in bucket list
1105 : {
1106 46 : if (lc->bucket_next)
1107 2 : lc->bucket_next->bucket_prev = lc->bucket_prev;
1108 :
1109 46 : m_hasharray[ reinterpret_cast<uintptr_t>(lc->bucket_prev) ] = lc->bucket_next;
1110 : }
1111 :
1112 : // unlink from LRU list
1113 59190 : lc->list_next->list_prev = lc->list_prev;
1114 59190 : lc->list_prev->list_next = lc->list_next;
1115 :
1116 59190 : delete lc;
1117 :
1118 59190 : --m_size;
1119 : }
1120 :
1121 : // create new hash cell
1122 61122 : hc = new HashCell;
1123 :
1124 : #ifdef CBTREEDB_SELF_VERIFY
1125 61122 : hc->lrutime = ++m_lrutime;
1126 : #endif
1127 61122 : hc->btreeid = btreeid;
1128 61122 : hc->pageid = pageid;
1129 61122 : hc->page = page;
1130 :
1131 : // set hash cell as head of LRU list
1132 61122 : hc->list_prev = &m_sentinel;
1133 61122 : hc->list_next = m_sentinel.list_next;
1134 61122 : m_sentinel.list_next->list_prev = hc;
1135 61122 : m_sentinel.list_next = hc;
1136 :
1137 : // insert new hash cell to correct bucket
1138 61122 : hc->bucket_prev = reinterpret_cast<HashCell*>( h );
1139 61122 : hc->bucket_next = m_hasharray[h];
1140 :
1141 61122 : if (m_hasharray[h])
1142 60989 : m_hasharray[h]->bucket_prev = hc;
1143 :
1144 61122 : m_hasharray[h] = hc;
1145 :
1146 61122 : ++m_size;
1147 : }
1148 :
1149 : #ifdef CBTREEDB_SELF_VERIFY
1150 61123 : CBTREEDB_ASSERT( Verify() );
1151 : #endif
1152 61123 : }
1153 :
1154 : /// Retrieve a cached page identified by (btreeid,pageid). Returns true
1155 : /// if the page was found.
1156 29951829 : bool Retrieve(void* btreeid, uint32_t pageid, BTreePage& outpage)
1157 : {
1158 : // check whether its in the cache
1159 :
1160 29951829 : unsigned int h = hashfunc(btreeid, pageid);
1161 :
1162 29951829 : struct HashCell* hc = m_hasharray[h];
1163 :
1164 1950267461 : while( hc && ! (hc->btreeid == btreeid && hc->pageid == pageid) )
1165 : {
1166 : // advance in bucket list
1167 1890363803 : hc = hc->bucket_next;
1168 : }
1169 :
1170 29951829 : if ( hc ) // found in cache: unlink from LRU list and place in front
1171 : {
1172 29890908 : if (hc != m_sentinel.list_next)
1173 : {
1174 : // remove cell wherever it is
1175 26454804 : hc->list_next->list_prev = hc->list_prev;
1176 26454804 : hc->list_prev->list_next = hc->list_next;
1177 :
1178 : // place at head
1179 26454804 : hc->list_prev = &m_sentinel;
1180 26454804 : hc->list_next = m_sentinel.list_next;
1181 26454804 : m_sentinel.list_next->list_prev = hc;
1182 26454804 : m_sentinel.list_next = hc;
1183 :
1184 : #ifdef CBTREEDB_SELF_VERIFY
1185 26454804 : hc->lrutime = ++m_lrutime;
1186 : #endif
1187 : }
1188 : // else hc is the head -> do nothing.
1189 :
1190 29890908 : outpage = hc->page;
1191 :
1192 : #ifdef CBTREEDB_SELF_VERIFY
1193 29890908 : CBTREEDB_ASSERT( Verify() );
1194 : #endif
1195 29890908 : return true;
1196 : }
1197 : else
1198 : {
1199 : #ifdef CBTREEDB_SELF_VERIFY
1200 60921 : CBTREEDB_ASSERT( Verify() );
1201 : #endif
1202 60921 : return false;
1203 : }
1204 : }
1205 :
1206 : /// Change maximum number of pages in cache, note that this does not
1207 : /// immediately have effect.
1208 0 : void SetMaxSize(unsigned int maxsize)
1209 : {
1210 0 : m_maxsize = maxsize;
1211 0 : }
1212 :
1213 : /// Return a vector listing all currently contained (btreeid,pageid)
1214 : /// pairs in LRU order. Used by the test cases for verification.
1215 5 : std::vector< std::pair<void*, uint32_t> > GetPagelist() const
1216 : {
1217 5 : std::vector< std::pair<void*, uint32_t> > v;
1218 :
1219 5 : struct HashCell* hc = m_sentinel.list_next;
1220 :
1221 42 : while (hc != &m_sentinel)
1222 : {
1223 32 : v.push_back( std::make_pair(hc->btreeid, hc->pageid) );
1224 32 : hc = hc->list_next;
1225 : }
1226 :
1227 0 : return v;
1228 : }
1229 :
1230 : /// Verify the integrity of the LRU list and hash table.
1231 30013957 : bool Verify() const
1232 : {
1233 : { // traverse LRU list forwards
1234 :
1235 30013957 : unsigned int size = 0;
1236 30013957 : struct HashCell* hc = m_sentinel.list_next;
1237 :
1238 -496543401 : while (hc != &m_sentinel)
1239 : {
1240 -556571315 : if (!(hc->list_prev->list_next == hc)) return false;
1241 -556571315 : if (!(hc->list_next->list_prev == hc)) return false;
1242 : #ifdef CBTREEDB_SELF_VERIFY
1243 -556571315 : if (!(hc->lrutime > hc->list_next->lrutime)) return false;
1244 : #endif
1245 :
1246 -556571315 : ++size;
1247 -556571315 : hc = hc->list_next;
1248 : }
1249 :
1250 30013957 : if (size != m_size) return false;
1251 : }
1252 :
1253 : { // traverse LRU list backwards
1254 :
1255 30013957 : unsigned int size = 0;
1256 30013957 : struct HashCell* hc = m_sentinel.list_prev;
1257 :
1258 -496543401 : while (hc != &m_sentinel)
1259 : {
1260 -556571315 : if (!(hc->list_prev->list_next == hc)) return false;
1261 -556571315 : if (!(hc->list_next->list_prev == hc)) return false;
1262 : #ifdef CBTREEDB_SELF_VERIFY
1263 -556571315 : if (!(hc->lrutime < hc->list_prev->lrutime || hc->list_prev == &m_sentinel)) return false;
1264 : #endif
1265 :
1266 -556571315 : ++size;
1267 -556571315 : hc = hc->list_prev;
1268 : }
1269 :
1270 30013957 : if (size != m_size) return false;
1271 : }
1272 :
1273 : { // check and count hash cells in buckets
1274 :
1275 30013957 : unsigned int size = 0;
1276 :
1277 1950810185 : for (unsigned int b = 0; b < m_hasharray.size(); ++b)
1278 : {
1279 1920796228 : struct HashCell* hc = m_hasharray[b];
1280 :
1281 1920796228 : if (!hc) continue;
1282 :
1283 30042749 : if (!(reinterpret_cast<uintptr_t>(hc->bucket_prev) == b)) return false;
1284 :
1285 30042749 : ++size;
1286 :
1287 -526528566 : while (hc->bucket_next != NULL)
1288 : {
1289 -586614064 : if (!(hc->bucket_next->bucket_prev == hc)) return false;
1290 :
1291 -586614064 : hc = hc->bucket_next;
1292 :
1293 -586614064 : ++size;
1294 : }
1295 : }
1296 :
1297 30013957 : if (size != m_size) return false;
1298 : }
1299 :
1300 30013957 : return true;
1301 : }
1302 : };
1303 :
1304 : public:
1305 : /**
1306 : * @brief PageCache and PageCacheImpl implement a LRU-strategy cache of
1307 : * B-tree pages used by CBTreeDB reader objects.
1308 : *
1309 : * One cache object can be shared between multiple readers. However, this
1310 : * page cache is not thread safe. You may have to wrap some mutex libraries
1311 : * if needed.
1312 : *
1313 : * The cached pages are put into a hash table for quick lookup by
1314 : * (btreeid,pageid). Simultaneously the HashCells are linked into a doubly
1315 : * chained "LRU"-list with the most recently used page at the head. This
1316 : * allows O(1) algorithms for both Store() and Retrieve() functions. When
1317 : * the maximum number of pages is exceeded, the tail pages of the LRU-list
1318 : * are removed. The drawing below illustrates the data structure used by
1319 : * the class.
1320 : *
1321 : * \htmlonly
1322 : * <div style="text-align: center">
1323 : * <p>Structure of PageCache's arrays and nodes</p>
1324 : * <object type="image/svg+xml" data="drawing-1.svg" style="height: 25em"></object>
1325 : * </div>
1326 : * \endhtmlonly
1327 : */
1328 : class PageCache
1329 : {
1330 : protected:
1331 :
1332 : /// pointer to implementation class.
1333 : PageCacheImpl* m_impl;
1334 :
1335 : public:
1336 : /// Create a new page cache containg maxsize pages
1337 75 : explicit PageCache(unsigned int maxpages)
1338 75 : : m_impl(new PageCacheImpl(maxpages))
1339 : {
1340 75 : m_impl->RefInc();
1341 75 : }
1342 :
1343 : /// Copy Constructor: increment reference counter on base object.
1344 0 : PageCache(const PageCache& pc)
1345 0 : : m_impl(pc.m_impl)
1346 : {
1347 0 : m_impl->RefInc();
1348 0 : }
1349 :
1350 : /// Destructor: decrement reference counter on buffer and possibly
1351 : /// deallocate it.
1352 75 : ~PageCache()
1353 : {
1354 75 : if (m_impl->RefDec() == 0)
1355 75 : delete m_impl;
1356 75 : }
1357 :
1358 : /// Assignment Operator: increment reference counter on base object.
1359 0 : PageCache& operator=(const PageCache& pc)
1360 : {
1361 0 : if (this != &pc)
1362 : {
1363 0 : if (m_impl->RefDec() == 0)
1364 0 : delete m_impl;
1365 :
1366 0 : m_impl = pc.m_impl;
1367 0 : m_impl->RefInc();
1368 : }
1369 :
1370 0 : return *this;
1371 : }
1372 :
1373 : /// Remove all pages from the cache and reset status.
1374 0 : void Clear()
1375 : {
1376 0 : return m_impl->Clear();
1377 : }
1378 :
1379 : /// Store a page object in a cache cell identified by (btreeid,pageid).
1380 61123 : void Store(void* btreeid, uint32_t pageid, const BTreePage& page)
1381 : {
1382 61123 : return m_impl->Store(btreeid, pageid, page);
1383 : }
1384 :
1385 : /// Retrieve a cached page identified by (btreeid,pageid). Returns true
1386 : /// if the page was found.
1387 29951829 : bool Retrieve(void* btreeid, uint32_t pageid, BTreePage& outpage)
1388 : {
1389 29951829 : return m_impl->Retrieve(btreeid, pageid, outpage);
1390 : }
1391 :
1392 : /// Change maximum number of pages in cache, note that this does not
1393 : /// immediately have effect.
1394 0 : void SetMaxSize(unsigned int maxsize)
1395 : {
1396 0 : return m_impl->SetMaxSize(maxsize);
1397 : }
1398 :
1399 : /// Return a vector listing all currently contained (btreeid,pageid)
1400 : /// pairs in LRU order. Used by the test cases for verification.
1401 5 : std::vector< std::pair<void*, uint32_t> > GetPagelist() const
1402 : {
1403 5 : return m_impl->GetPagelist();
1404 : }
1405 :
1406 : /// Verify the integrity of the LRU list and hash table.
1407 1005 : bool Verify() const
1408 : {
1409 1005 : return m_impl->Verify();
1410 : }
1411 : };
1412 :
1413 : protected:
1414 : /**
1415 : * @brief Implementation class used to read constant B-tree database files.
1416 : *
1417 : * Refer to \ref sec_architecture and \ref sec_example on how to use this class.
1418 : */
1419 : class ReaderImpl
1420 : {
1421 : protected:
1422 :
1423 : /// reference counter
1424 : unsigned int m_refs;
1425 :
1426 : /// key comparison functional
1427 : key_compare m_key_less;
1428 :
1429 : /// signature characters to expect file to begin with.
1430 : char m_signaturestr[8];
1431 :
1432 : /// file stream object currently opened.
1433 : std::istream* m_istream;
1434 :
1435 : /// signature page read from file
1436 : SignaturePage m_signature;
1437 :
1438 : /// pointer to b-tree page cache to used.
1439 : PageCache* m_pagecache;
1440 :
1441 : /// Read one B-tree page from the file (or from cache).
1442 29951328 : BTreePage ReadIndexPage(uint32_t pageoffset)
1443 : {
1444 29951328 : CBTREEDB_CHECK(pageoffset + m_signature.btree_pagesize <= m_signature.btree_size,
1445 : "Invalid B-tree page offset to retrieve.");
1446 :
1447 29951328 : CBTREEDB_ASSERT(m_istream);
1448 :
1449 29951328 : BTreePage page;
1450 :
1451 29951328 : if (m_pagecache)
1452 : {
1453 29951328 : if (m_pagecache->Retrieve(this, pageoffset, page))
1454 29890716 : return page;
1455 : }
1456 :
1457 60612 : m_istream->seekg(m_signature.btree_offset + pageoffset);
1458 60612 : CBTREEDB_CHECK(m_istream->good(), "Could not read B-tree page.");
1459 :
1460 60612 : page.Create();
1461 :
1462 60612 : m_istream->read(page.GetBuffer(), BTreePageSize);
1463 60612 : CBTREEDB_CHECK(m_istream->good(), "Could not read B-tree page.");
1464 :
1465 60612 : if (m_pagecache)
1466 : {
1467 60612 : m_pagecache->Store(this, pageoffset, page);
1468 : }
1469 :
1470 60612 : return page;
1471 : }
1472 :
1473 : /// Read byte range [offset, offset+size) from value data area into the
1474 : /// given buffer.
1475 5734949 : bool ReadValueRange(uint64_t offset, void* data, uint32_t size)
1476 : {
1477 5734949 : CBTREEDB_ASSERT(m_istream);
1478 :
1479 5734949 : if (offset + size > m_signature.value_size) return false;
1480 :
1481 5734949 : m_istream->seekg(m_signature.value_offset + offset);
1482 5734949 : if (m_istream->bad()) return false;
1483 :
1484 5734949 : m_istream->read(reinterpret_cast<char*>(data), size);
1485 5734949 : if (m_istream->bad()) return false;
1486 :
1487 5734949 : return true;
1488 : }
1489 :
1490 : /// Function to test key equality, constructed from m_key_less.
1491 9183984 : bool KeyEqual(const key_type& a, const key_type& b)
1492 : {
1493 9183984 : return !m_key_less(a,b) && !m_key_less(b,a);
1494 : }
1495 :
1496 : /// Function to test key inequality, constructed from m_key_less.
1497 9175356 : bool KeyUnequal(const key_type& a, const key_type& b)
1498 : {
1499 9175356 : return m_key_less(a,b) || m_key_less(b,a);
1500 : }
1501 :
1502 : /// Find the first key slot containing a greater-or-equal key.
1503 : template <typename NodeType>
1504 26473240 : int BinarySearch(const NodeType* node, key_type key)
1505 : {
1506 26473240 : register int lo = 0, hi = node->slots;
1507 :
1508 216156188 : while (lo < hi)
1509 : {
1510 163209708 : register int mid = (hi + lo) >> 1;
1511 :
1512 163209708 : if (m_key_less(node->keys[mid], key)) {
1513 78432956 : lo = mid + 1;
1514 : }
1515 : else {
1516 84776752 : hi = mid;
1517 : }
1518 : }
1519 :
1520 : #ifdef CBTREEDB_SELF_VERIFY
1521 : // verify result using simple linear search
1522 : {
1523 26473240 : int i = 0;
1524 1728825800 : while(i < node->slots && m_key_less(node->keys[i], key))
1525 1675879320 : ++i;
1526 :
1527 26473240 : CBTREEDB_ASSERT(i == lo);
1528 : }
1529 : #endif
1530 26473240 : return lo;
1531 : }
1532 :
1533 : public:
1534 : /// Create new reader, which is initially set to closed state.
1535 73 : ReaderImpl(const key_compare& key_less)
1536 73 : : m_refs(0), m_key_less(key_less), m_istream(NULL), m_pagecache(NULL)
1537 : {
1538 73 : memcpy(m_signaturestr, "cbtreedb", 8);
1539 73 : }
1540 :
1541 : /// Increment reference counter by one.
1542 138 : void RefInc()
1543 : {
1544 138 : ++m_refs;
1545 138 : }
1546 :
1547 : /// Decrement reference counter by one and return it.
1548 138 : unsigned int RefDec()
1549 : {
1550 138 : return --m_refs;
1551 : }
1552 :
1553 : /**
1554 : * Change the database signature (first 8 bytes) from 'cbtreedb' to a
1555 : * custom string. The signature is always 8 bytes long. Longer strings
1556 : * are truncated, shorter ones padded with nulls.
1557 : */
1558 73 : void SetSignature(const char* newsignature)
1559 : {
1560 73 : unsigned int i = 0;
1561 657 : for(; i < 8 && newsignature[i]; ++i)
1562 584 : m_signaturestr[i] = newsignature[i];
1563 :
1564 73 : for(; i < 8; ++i)
1565 0 : m_signaturestr[i] = 0;
1566 73 : }
1567 :
1568 : /**
1569 : * Attempt to open a cbtreedb database file. Reads and verifies the
1570 : * signature and initializes the reader. Note that this function does
1571 : * not through an exception if the file could not be loaded! The
1572 : * istream object must exist as long as the Reader is used.
1573 : *
1574 : * @param file database file input stream to attach.
1575 : * @param errortext in case of error, set to an informative text.
1576 : * @return true if loaded and verified correctly.
1577 : */
1578 73 : bool Open(std::istream& file, std::string* errortext = NULL)
1579 : {
1580 73 : m_istream = NULL;
1581 :
1582 73 : file.seekg(0, std::ios::beg);
1583 73 : if (file.bad()) {
1584 0 : if (errortext) *errortext = "Could not open database.";
1585 0 : return false;
1586 : }
1587 :
1588 73 : file.read(reinterpret_cast<char*>(&m_signature), sizeof(m_signature));
1589 73 : if (file.bad()) {
1590 0 : if (errortext) *errortext = "Could not read signature.";
1591 0 : return false;
1592 : }
1593 :
1594 73 : if (memcmp(m_signature.signature, m_signaturestr, 8) != 0) {
1595 0 : if (errortext) *errortext = "Could not verify signature.";
1596 0 : return false;
1597 : }
1598 :
1599 73 : if (m_signature.version != 0x00010000) {
1600 0 : if (errortext) *errortext = "Signature contains unknown version.";
1601 0 : return false;
1602 : }
1603 :
1604 73 : if (m_signature.app_version_id != AppVersionId) {
1605 0 : if (errortext) *errortext = "Signature mismatches application version identifier.";
1606 0 : return false;
1607 : }
1608 :
1609 73 : uint32_t crc = CRC32::digest(reinterpret_cast<char*>(&m_signature)+16, sizeof(m_signature)-16);
1610 :
1611 73 : if (m_signature.header_crc32 != crc) {
1612 0 : if (errortext) *errortext = "Header checksum mismatches.";
1613 0 : return false;
1614 : }
1615 :
1616 73 : if (m_signature.key_size != sizeof(key_type)) {
1617 2 : if (errortext) *errortext = "Database not compatible with this reader: key sizes mismatch.";
1618 2 : return false;
1619 : }
1620 :
1621 71 : if (m_signature.btree_pagesize != BTreePageSize) {
1622 2 : if (errortext) *errortext = "Database not compatible with this reader: page sizes mismatch.";
1623 2 : return false;
1624 : }
1625 :
1626 : // test database compatibility with order relation by checking root
1627 : // node's key sequence.
1628 :
1629 69 : m_istream = &file;
1630 :
1631 69 : BTreePage root = ReadIndexPage(0);
1632 :
1633 69 : if ( root.IsLeafNode() )
1634 : {
1635 28 : LeafNode* leaf = root.GetAsLeafNode();
1636 :
1637 532 : for(uint16_t s = 0; s < leaf->slots - 1; ++s)
1638 : {
1639 504 : if (!m_key_less(leaf->keys[s], leaf->keys[s+1])) {
1640 0 : m_istream = NULL;
1641 0 : if (errortext) *errortext = "Database not compatible with this reader: root keys order mismatches.";
1642 0 : return false;
1643 : }
1644 : }
1645 : }
1646 : else
1647 : {
1648 41 : InnerNode* inner = root.GetAsInnerNode();
1649 :
1650 1203 : for(uint16_t s = 0; s < inner->slots - 1; ++s)
1651 : {
1652 1164 : if (!m_key_less(inner->keys[s], inner->keys[s+1])) {
1653 2 : m_istream = NULL;
1654 2 : if (errortext) *errortext = "Database not compatible with this reader: root keys order mismatches.";
1655 2 : return false;
1656 : }
1657 : }
1658 : }
1659 :
1660 67 : return true;
1661 : }
1662 :
1663 : /**
1664 : * Close the opened database.
1665 : */
1666 65 : void Close()
1667 : {
1668 65 : if (m_istream)
1669 65 : m_istream = NULL;
1670 65 : }
1671 :
1672 : /// Change the currently used page cache object
1673 73 : void SetPageCache(PageCache* newpagecache)
1674 : {
1675 73 : m_pagecache = newpagecache;
1676 73 : }
1677 :
1678 : /**
1679 : * Returns the number of items in the loaded database.
1680 : */
1681 67 : uint32_t Size() const
1682 : {
1683 67 : CBTREEDB_CHECK(m_istream, "No database loaded.");
1684 :
1685 67 : return m_signature.items;
1686 : }
1687 :
1688 : /**
1689 : * Returns a const reference to the signature page of the currently
1690 : * loaded database.
1691 : */
1692 0 : const SignaturePage& GetSignature() const
1693 : {
1694 0 : return m_signature;
1695 : }
1696 :
1697 : protected:
1698 : /**
1699 : * Internal function to look down the B-tree and find a key. If found,
1700 : * returns the offset and size of the corresponding value data area.
1701 : */
1702 9175496 : bool FindKey(const key_type& key, uint64_t& outoffset, uint32_t& outsize)
1703 : {
1704 9175496 : if (m_signature.btree_size == 0) return false;
1705 :
1706 9175496 : BTreePage page = ReadIndexPage(0);
1707 :
1708 9175496 : bool checklastkey = false;
1709 9175496 : key_type lastkey = key_type();
1710 :
1711 35648736 : while( ! page.IsLeafNode() )
1712 : {
1713 17297744 : InnerNode* inner = page.GetAsInnerNode();
1714 :
1715 17297744 : CBTREEDB_CHECK(!checklastkey || !m_key_less(lastkey, inner->LastKey()),
1716 : "BTree corrupt (lastkey does not match).");
1717 :
1718 17297744 : int slot = BinarySearch(inner, key);
1719 :
1720 17297744 : uint32_t next = inner->childrenoffset;
1721 17297744 : if (inner->level > 1)
1722 8126504 : next += slot * sizeof(InnerNode);
1723 : else
1724 9171240 : next += slot * sizeof(LeafNode);
1725 :
1726 17297744 : int oldlevel = inner->level;
1727 :
1728 17297744 : if (slot < inner->slots) {
1729 17102632 : checklastkey = true;
1730 17102632 : lastkey = inner->keys[slot];
1731 : }
1732 :
1733 17297744 : page = ReadIndexPage(next);
1734 :
1735 17297744 : CBTREEDB_CHECK(page.GetLevel() == oldlevel-1,
1736 : "BTree corrupt (level order mismatch).");
1737 : }
1738 :
1739 9175496 : LeafNode* leaf = page.GetAsLeafNode();
1740 :
1741 9175496 : CBTREEDB_CHECK(!checklastkey || KeyEqual(leaf->LastKey(), lastkey),
1742 : "BTree corrupt (lastkey in leaf does not match).");
1743 :
1744 9175496 : int slot = BinarySearch(leaf, key);
1745 :
1746 9175496 : if (slot >= leaf->slots || KeyUnequal(leaf->keys[slot], key))
1747 4587748 : return false;
1748 :
1749 : // Return offset and size via pointers.
1750 :
1751 4587748 : outoffset = leaf->baseoffset + leaf->offsets[slot];
1752 :
1753 : // figure out value size of this slot: compute it from the offsets
1754 4587748 : CBTREEDB_CHECK(leaf->offsets[slot] <= leaf->offsets[slot+1],
1755 : "BTree corrupt (offsets are not ascending).");
1756 :
1757 4587748 : outsize = leaf->offsets[slot+1] - leaf->offsets[slot];
1758 :
1759 4587748 : return true;
1760 : }
1761 :
1762 : public:
1763 : /**
1764 : * Check if a key is in the constant database.
1765 : *
1766 : * @param key key to lookup
1767 : * @return true if found.
1768 : */
1769 2293874 : bool Exists(const key_type& key)
1770 : {
1771 2293874 : CBTREEDB_CHECK(m_istream, "No database loaded.");
1772 :
1773 : uint64_t offset;
1774 : uint32_t size;
1775 :
1776 2293874 : return FindKey(key, offset, size);
1777 : }
1778 :
1779 : /**
1780 : * Find a key in the constant database. If found the corresponding
1781 : * value is copied into the output buffer.
1782 : *
1783 : * @param key key to lookup
1784 : * @param outvalue buffer filled with the associated value if the key is found
1785 : * @param maxsize maximum size of buffer
1786 : * @return true if found.
1787 : */
1788 2293874 : bool Lookup(const key_type& key, void* outvalue, uint32_t maxsize)
1789 : {
1790 2293874 : CBTREEDB_CHECK(m_istream, "No database loaded.");
1791 :
1792 : uint64_t offset;
1793 : uint32_t size;
1794 :
1795 2293874 : if (!FindKey(key, offset, size))
1796 1146937 : return false;
1797 :
1798 1146937 : uint32_t readsize = size;
1799 1146937 : if (readsize > maxsize) readsize = maxsize;
1800 :
1801 1146937 : return ReadValueRange(offset, outvalue, readsize);
1802 : }
1803 :
1804 : /**
1805 : * Find a key in the constant database. If found the coresponding value
1806 : * is copied into the output string buffer.
1807 : *
1808 : * @param key key to lookup
1809 : * @param outvalue string filled with the associated value if the key is found
1810 : * @return true if found.
1811 : */
1812 2293874 : bool Lookup(const key_type& key, std::string& outvalue)
1813 : {
1814 2293874 : CBTREEDB_CHECK(m_istream, "No database loaded.");
1815 :
1816 : uint64_t offset;
1817 : uint32_t size;
1818 :
1819 2293874 : if (!FindKey(key, offset, size))
1820 1146937 : return false;
1821 :
1822 1146937 : outvalue.resize(size);
1823 :
1824 1146937 : return ReadValueRange(offset, const_cast<char*>(outvalue.data()), size);
1825 : }
1826 :
1827 : /**
1828 : * Find a key in the constant database. If found the corresponding
1829 : * value is copied into the output string buffer. If the key does not
1830 : * exist, an empty string is returned.
1831 : *
1832 : * @param key key to lookup
1833 : * @return string containing the value
1834 : */
1835 2293874 : std::string operator[](const key_type& key)
1836 : {
1837 2293874 : CBTREEDB_CHECK(m_istream, "No database loaded.");
1838 :
1839 : uint64_t offset;
1840 : uint32_t size;
1841 :
1842 2293874 : if (!FindKey(key, offset, size))
1843 1146937 : return std::string();
1844 :
1845 1146937 : std::string outvalue;
1846 1146937 : outvalue.resize(size);
1847 :
1848 1146937 : if (!ReadValueRange(offset, const_cast<char*>(outvalue.data()), size))
1849 0 : return std::string();
1850 :
1851 1146937 : return outvalue;
1852 : }
1853 :
1854 : protected:
1855 : /**
1856 : * Internal function to look directly into the B-tree's leaf pages and
1857 : * find a key by index. If found, returns the key, offset and size of
1858 : * the corresponding value area.
1859 : */
1860 3440811 : bool FindIndex(uint32_t index, key_type& outkey, uint64_t& outoffset, uint32_t& outsize)
1861 : {
1862 3440811 : if (index >= m_signature.items) return false;
1863 :
1864 : // directly compute offset of leaf containing to the key index
1865 :
1866 3440811 : uint32_t offset = index / LeafNodeNumKeys * BTreePageSize;
1867 3440811 : unsigned int slot = index % LeafNodeNumKeys;
1868 :
1869 3440811 : BTreePage page = ReadIndexPage(m_signature.btree_firstleaf + offset);
1870 :
1871 3440811 : CBTREEDB_CHECK(page.IsLeafNode(),
1872 : "BTree corrupt (expecting leaf node).");
1873 :
1874 3440811 : LeafNode* leaf = page.GetAsLeafNode();
1875 :
1876 3440811 : CBTREEDB_CHECK(slot < leaf->slots,
1877 : "BTree corrupt (index beyond range in leaf node).");
1878 :
1879 : // copy key and offset
1880 3440811 : outkey = leaf->keys[slot];
1881 3440811 : outoffset = leaf->baseoffset + leaf->offsets[slot];
1882 :
1883 : // figure out value size of this slot: compute it from the offsets
1884 3440811 : CBTREEDB_CHECK(leaf->offsets[slot] <= leaf->offsets[slot+1],
1885 : "BTree corrupt (offsets are not ascending).");
1886 :
1887 3440811 : outsize = leaf->offsets[slot+1] - leaf->offsets[slot];
1888 :
1889 3440811 : return true;
1890 : }
1891 :
1892 : public:
1893 : /**
1894 : * Returns only the key by index. Looks directly into the leaf pages.
1895 : *
1896 : * @param index zero-based index of item to retrieve
1897 : * @param outkey set to key of item
1898 : * @return size of associated value if found
1899 : */
1900 1146937 : uint32_t GetIndex(uint32_t index, key_type& outkey)
1901 : {
1902 1146937 : CBTREEDB_CHECK(m_istream, "No database loaded.");
1903 :
1904 : uint64_t offset;
1905 : uint32_t size;
1906 :
1907 1146937 : if (!FindIndex(index, outkey, offset, size))
1908 0 : return 0;
1909 :
1910 1146937 : return size;
1911 : }
1912 :
1913 : /**
1914 : * Return a key and associated value by index. Looks directly into the
1915 : * leaf pages.
1916 : *
1917 : * @param index zero-based index of item to retrieve
1918 : * @param outkey set to key of item
1919 : * @param outvalue buffer to hold data of value
1920 : * @param maxsize maximum size of buffer
1921 : * @return size of associated value
1922 : */
1923 1146937 : uint32_t GetIndex(uint32_t index, key_type& outkey, void* outvalue, uint32_t maxsize)
1924 : {
1925 1146937 : CBTREEDB_CHECK(m_istream, "No database loaded.");
1926 :
1927 : uint64_t offset;
1928 : uint32_t size;
1929 :
1930 1146937 : if (!FindIndex(index, outkey, offset, size))
1931 0 : return 0;
1932 :
1933 1146937 : uint32_t outsize = size;
1934 1146937 : if (outsize > maxsize) outsize = maxsize;
1935 :
1936 1146937 : if (!ReadValueRange(offset, outvalue, outsize))
1937 0 : return 0;
1938 :
1939 1146937 : return size;
1940 : }
1941 :
1942 : /**
1943 : * Return a key and associated value by index. Looks directly into the
1944 : * leaf pages.
1945 : *
1946 : * @param index zero-based index of item to retrieve
1947 : * @param outkey set to key of item
1948 : * @param outvalue string to hold data of value
1949 : * @return size of associated value
1950 : */
1951 1146937 : uint32_t GetIndex(uint32_t index, key_type& outkey, std::string& outvalue)
1952 : {
1953 1146937 : CBTREEDB_CHECK(m_istream, "No database loaded.");
1954 :
1955 : uint64_t offset;
1956 : uint32_t size;
1957 :
1958 1146937 : if (!FindIndex(index, outkey, offset, size))
1959 0 : return 0;
1960 :
1961 1146937 : outvalue.resize(size);
1962 :
1963 1146937 : if (!ReadValueRange(offset, const_cast<char*>(outvalue.data()), size))
1964 0 : return 0;
1965 :
1966 1146937 : return size;
1967 : }
1968 :
1969 : /**
1970 : * Verify all aspects of the loaded database.
1971 : *
1972 : * @return true if database is ok.
1973 : */
1974 67 : bool Verify()
1975 : {
1976 67 : CBTREEDB_CHECK(m_istream, "No database loaded.");
1977 :
1978 67 : if (!VerifyBTree()) return false;
1979 :
1980 67 : if (!VerifyBTreeChecksum()) return false;
1981 :
1982 67 : if (!VerifyValueChecksum()) return false;
1983 :
1984 67 : return true;
1985 : }
1986 :
1987 : /**
1988 : * Verify B-tree structure in the loaded database.
1989 : *
1990 : * @return true if database is ok.
1991 : */
1992 132 : bool VerifyBTree()
1993 : {
1994 132 : CBTREEDB_CHECK(m_istream, "No database loaded.");
1995 :
1996 132 : if (m_signature.btree_size == 0) return true;
1997 :
1998 132 : key_type minkey = key_type(), maxkey = key_type();
1999 132 : uint64_t lastoffset = 0;
2000 132 : return VerifyBTreeNode(0, &minkey, &maxkey, &lastoffset);
2001 : }
2002 :
2003 : protected:
2004 : /**
2005 : * Internal function: Recursively verify B-tree structure.
2006 : */
2007 18604 : bool VerifyBTreeNode(uint32_t offset, key_type* minkey, key_type* maxkey, uint64_t* lastoffset)
2008 : {
2009 18604 : BTreePage page = ReadIndexPage(offset);
2010 :
2011 18604 : if ( page.IsLeafNode() )
2012 : {
2013 18456 : LeafNode* leaf = page.GetAsLeafNode();
2014 :
2015 18456 : if (*lastoffset != leaf->baseoffset + leaf->offsets[0]) return false;
2016 :
2017 2313876 : for(uint16_t s = 0; s < leaf->slots - 1; ++s)
2018 : {
2019 2295420 : if (!m_key_less(leaf->keys[s], leaf->keys[s+1])) return false;
2020 :
2021 2295420 : if (!(leaf->offsets[s] <= leaf->offsets[s+1])) return false;
2022 : }
2023 :
2024 18456 : *minkey = leaf->keys[0];
2025 18456 : *maxkey = leaf->keys[leaf->slots - 1];
2026 18456 : *lastoffset = leaf->baseoffset + leaf->offsets[leaf->slots];
2027 : }
2028 : else
2029 : {
2030 148 : InnerNode* inner = page.GetAsInnerNode();
2031 :
2032 18324 : for(uint16_t s = 0; s < inner->slots - 1; ++s)
2033 : {
2034 18176 : if (!m_key_less(inner->keys[s], inner->keys[s+1])) return false;
2035 : }
2036 :
2037 18620 : for(uint16_t s = 0; s <= inner->slots; ++s)
2038 : {
2039 18472 : uint32_t childoffset = inner->childrenoffset;
2040 :
2041 18472 : if (inner->level > 1)
2042 72 : childoffset += s * sizeof(InnerNode);
2043 : else
2044 18400 : childoffset += s * sizeof(LeafNode);
2045 :
2046 18472 : key_type subminkey = key_type(), submaxkey = key_type();
2047 :
2048 18472 : if (!VerifyBTreeNode(childoffset, &subminkey, &submaxkey, lastoffset)) return false;
2049 :
2050 18472 : if (s == 0)
2051 148 : *minkey = subminkey;
2052 : else
2053 18324 : if (!m_key_less(inner->keys[s-1], subminkey)) return false;
2054 :
2055 18472 : if (s == inner->slots)
2056 148 : *maxkey = submaxkey;
2057 : else
2058 18324 : if (!KeyEqual(inner->keys[s], submaxkey)) return false;
2059 : }
2060 : }
2061 :
2062 18604 : return true;
2063 : }
2064 :
2065 : public:
2066 : /**
2067 : * Verify the SHA256 checksum of the B-tree pages in the loaded
2068 : * database.
2069 : *
2070 : * @return true if database is ok.
2071 : */
2072 132 : bool VerifyBTreeChecksum()
2073 : {
2074 132 : CBTREEDB_CHECK(m_istream, "No database loaded.");
2075 :
2076 132 : SHA256 sha;
2077 :
2078 18736 : for(uint32_t offset = 0; offset < m_signature.btree_size; offset += BTreePageSize)
2079 : {
2080 18604 : BTreePage page = ReadIndexPage(offset);
2081 :
2082 18604 : sha.update(page.GetBuffer(), BTreePageSize);
2083 : }
2084 :
2085 132 : return ( sha.final_equals(m_signature.btree_sha256) );
2086 : }
2087 :
2088 : /**
2089 : * Verify the SHA256 checksum of value data area in the loaded
2090 : * database.
2091 : *
2092 : * @return true if database is ok.
2093 : */
2094 132 : bool VerifyValueChecksum()
2095 : {
2096 132 : CBTREEDB_CHECK(m_istream, "No database loaded.");
2097 :
2098 132 : SHA256 sha;
2099 :
2100 : char buffer[64*1024];
2101 :
2102 396 : for(uint64_t offset = 0; offset < m_signature.value_size; offset += sizeof(buffer))
2103 : {
2104 264 : uint64_t remsize = std::min<uint64_t>(sizeof(buffer), m_signature.value_size - offset);
2105 :
2106 264 : if (!ReadValueRange(offset, buffer, remsize))
2107 0 : return false;
2108 :
2109 264 : sha.update(buffer, remsize);
2110 : }
2111 :
2112 132 : return( sha.final_equals(m_signature.value_sha256) );
2113 : }
2114 : };
2115 :
2116 : public:
2117 : /**
2118 : * @brief Class used to read constant B-tree database files.
2119 : *
2120 : * This is a reference counted front-end to ReaderImpl.
2121 : *
2122 : * Refer to \ref sec_architecture and \ref sec_example on how to use this class.
2123 : */
2124 : class Reader
2125 : {
2126 : protected:
2127 :
2128 : /// pointer to implementation class.
2129 : ReaderImpl* m_impl;
2130 :
2131 : public:
2132 : /// Create new reader, which is initially set to closed state.
2133 73 : Reader(const key_compare& key_less=key_compare())
2134 73 : : m_impl(new ReaderImpl(key_less))
2135 : {
2136 73 : m_impl->RefInc();
2137 73 : }
2138 :
2139 : /// Copy Constructor: increment reference counter on base object.
2140 65 : Reader(const Reader& rd)
2141 65 : : m_impl(rd.m_impl)
2142 : {
2143 65 : m_impl->RefInc();
2144 65 : }
2145 :
2146 : /// Destructor: decrement reference counter on buffer and possibly
2147 : /// deallocate it.
2148 138 : ~Reader()
2149 : {
2150 138 : if (m_impl->RefDec() == 0)
2151 73 : delete m_impl;
2152 138 : }
2153 :
2154 : /// Assignment Operator: increment reference counter on base object.
2155 0 : Reader& operator=(const Reader& rd)
2156 : {
2157 0 : if (this != &rd)
2158 : {
2159 0 : if (m_impl->RefDec() == 0)
2160 0 : delete m_impl;
2161 :
2162 0 : m_impl = rd.m_impl;
2163 0 : m_impl->RefInc();
2164 : }
2165 :
2166 0 : return *this;
2167 : }
2168 :
2169 : /**
2170 : * Change the database signature (first 8 bytes) from 'cbtreedb' to a
2171 : * custom string. The signature is always 8 bytes long. Longer strings
2172 : * are truncated, shorter ones padded with nulls.
2173 : */
2174 73 : void SetSignature(const char* newsignature)
2175 : {
2176 73 : return m_impl->SetSignature(newsignature);
2177 : }
2178 :
2179 : /**
2180 : * Attempt to open a cbtreedb database file. Reads and verifies the
2181 : * signature and initializes the reader. Note that this function does
2182 : * not through an exception if the file could not be loaded! The
2183 : * istream object must exist as long as the Reader is used.
2184 : *
2185 : * @param file database file input stream to attach.
2186 : * @param errortext in case of error, set to an informative text.
2187 : * @return true if loaded and verified correctly.
2188 : */
2189 73 : bool Open(std::istream& file, std::string* errortext = NULL)
2190 : {
2191 73 : return m_impl->Open(file, errortext);
2192 : }
2193 :
2194 : /**
2195 : * Close the opened database.
2196 : */
2197 65 : void Close()
2198 : {
2199 65 : return m_impl->Close();
2200 : }
2201 :
2202 : /// Change the currently used page cache object
2203 73 : void SetPageCache(PageCache* newpagecache)
2204 : {
2205 73 : return m_impl->SetPageCache(newpagecache);
2206 : }
2207 :
2208 : /**
2209 : * Returns the number of items in the loaded database.
2210 : */
2211 67 : uint32_t Size() const
2212 : {
2213 67 : return m_impl->Size();
2214 : }
2215 :
2216 : /**
2217 : * Returns a const reference to the signature page of the currently
2218 : * loaded database.
2219 : */
2220 0 : const SignaturePage& GetSignature() const
2221 : {
2222 0 : return m_impl->GetSignature();
2223 : }
2224 :
2225 : /**
2226 : * Check if a key is in the constant database.
2227 : *
2228 : * @param key key to lookup
2229 : * @return true if found.
2230 : */
2231 2293874 : bool Exists(const key_type& key)
2232 : {
2233 2293874 : return m_impl->Exists(key);
2234 : }
2235 :
2236 : /**
2237 : * Find a key in the constant database. If found the corresponding
2238 : * value is copied into the output buffer.
2239 : *
2240 : * @param key key to lookup
2241 : * @param outvalue buffer filled with the associated value if the key is found
2242 : * @param maxsize maximum size of buffer
2243 : * @return true if found.
2244 : */
2245 2293874 : bool Lookup(const key_type& key, void* outvalue, uint32_t maxsize)
2246 : {
2247 2293874 : return m_impl->Lookup(key, outvalue, maxsize);
2248 : }
2249 :
2250 : /**
2251 : * Find a key in the constant database. If found the coresponding value
2252 : * is copied into the output string buffer.
2253 : *
2254 : * @param key key to lookup
2255 : * @param outvalue string filled with the associated value if the key is found
2256 : * @return true if found.
2257 : */
2258 2293874 : bool Lookup(const key_type& key, std::string& outvalue)
2259 : {
2260 2293874 : return m_impl->Lookup(key, outvalue);
2261 : }
2262 :
2263 : /**
2264 : * Find a key in the constant database. If found the corresponding
2265 : * value is copied into the output string buffer. If the key does not
2266 : * exist, an empty string is returned.
2267 : *
2268 : * @param key key to lookup
2269 : * @return string containing the value
2270 : */
2271 2293874 : std::string operator[](const key_type& key)
2272 : {
2273 2293874 : return (*m_impl)[key];
2274 : }
2275 :
2276 : /**
2277 : * Returns only the key by index. Looks directly into the leaf pages.
2278 : *
2279 : * @param index zero-based index of item to retrieve
2280 : * @param outkey set to key of item
2281 : * @return size of key's data
2282 : */
2283 1146937 : uint32_t GetIndex(uint32_t index, key_type& outkey)
2284 : {
2285 1146937 : return m_impl->GetIndex(index, outkey);
2286 : }
2287 :
2288 : /**
2289 : * Return a key and associated value by index. Looks directly into the
2290 : * leaf pages.
2291 : *
2292 : * @param index zero-based index of item to retrieve
2293 : * @param outkey set to key of item
2294 : * @param outvalue buffer to hold data of value
2295 : * @param maxsize maximum size of buffer
2296 : * @return size of associated value
2297 : */
2298 1146937 : uint32_t GetIndex(uint32_t index, key_type& outkey, void* outvalue, uint32_t maxsize)
2299 : {
2300 1146937 : return m_impl->GetIndex(index, outkey, outvalue, maxsize);
2301 : }
2302 :
2303 : /**
2304 : * Return a key and associated value by index. Looks directly into the
2305 : * leaf pages.
2306 : *
2307 : * @param index zero-based index of item to retrieve
2308 : * @param outkey set to key of item
2309 : * @param outvalue string to hold data of value
2310 : * @return size of associated value
2311 : */
2312 1146937 : uint32_t GetIndex(uint32_t index, key_type& outkey, std::string& outvalue)
2313 : {
2314 1146937 : return m_impl->GetIndex(index, outkey, outvalue);
2315 : }
2316 :
2317 : /**
2318 : * Verify all aspects of the loaded database.
2319 : *
2320 : * @return true if database is ok.
2321 : */
2322 67 : bool Verify()
2323 : {
2324 67 : return m_impl->Verify();
2325 : }
2326 :
2327 : /**
2328 : * Verify B-tree structure in the loaded database.
2329 : *
2330 : * @return true if database is ok.
2331 : */
2332 65 : bool VerifyBTree()
2333 : {
2334 65 : return m_impl->VerifyBTree();
2335 : }
2336 :
2337 : /**
2338 : * Verify the SHA256 checksum of the B-tree pages in the loaded
2339 : * database.
2340 : *
2341 : * @return true if database is ok.
2342 : */
2343 65 : bool VerifyBTreeChecksum()
2344 : {
2345 65 : return m_impl->VerifyBTreeChecksum();
2346 : }
2347 :
2348 : /**
2349 : * Verify the SHA256 checksum of value data area in the loaded
2350 : * database.
2351 : *
2352 : * @return true if database is ok.
2353 : */
2354 65 : bool VerifyValueChecksum()
2355 : {
2356 65 : return m_impl->VerifyValueChecksum();
2357 : }
2358 : };
2359 :
2360 : protected:
2361 :
2362 : /**
2363 : * @brief BTreeBuilder is used to construct an index very similar to a
2364 : * B-tree from an ordered sequence.
2365 : *
2366 : * The tree builder class is fed with an ordered sequence of keys together
2367 : * with their value size and offset. The information is stored into the
2368 : * nodes of the tree in memory.
2369 : */
2370 : class BTreeBuilder
2371 69 : {
2372 : protected:
2373 :
2374 : /// key comparison functional
2375 : key_compare m_key_less;
2376 :
2377 : /// total number of items stored in the tree
2378 : unsigned int m_size;
2379 :
2380 : /// leaves of the B-tree
2381 : std::vector<LeafNode> m_leaves;
2382 :
2383 : /// typedef of each inner level of the B-tree
2384 : typedef std::vector<InnerNode> innerlevel_type;
2385 :
2386 : /// vector holding a list of b-tree inner levels. Each level contains a
2387 : /// list of inner nodes.
2388 : std::vector<innerlevel_type> m_inners;
2389 :
2390 : public:
2391 : /// Construct a new empty tree builder.
2392 69 : BTreeBuilder(const key_compare& key_less)
2393 69 : : m_key_less(key_less), m_size(0)
2394 0 : {
2395 69 : }
2396 :
2397 : protected:
2398 : /// Add a key value in an inner node at given level. Called by Add when
2399 : /// a leaf node overflows.
2400 9400 : void AddInner(uint16_t level, const key_type& key)
2401 : {
2402 9400 : if (m_inners.size() < level)
2403 : {
2404 46 : CBTREEDB_ASSERT(m_inners.size()+1 == level);
2405 :
2406 : // Create vector for this level
2407 46 : m_inners.push_back(innerlevel_type());
2408 : }
2409 :
2410 9400 : if (m_inners[level-1].size() == 0)
2411 : {
2412 : // Create first inner node on this level
2413 46 : m_inners[level-1].push_back(InnerNode(level));
2414 : }
2415 :
2416 9400 : InnerNode* inner = &m_inners[level-1].back();
2417 :
2418 9400 : CBTREEDB_ASSERT(inner->slots == 0 || m_key_less(inner->LastKey(), key));
2419 :
2420 9400 : if (inner->IsFull())
2421 : {
2422 31 : CBTREEDB_ASSERT(m_key_less(inner->LastKey(), key));
2423 :
2424 : // Put last key of leaf key into inner node(s) of higher level
2425 31 : AddInner(inner->level + 1, key);
2426 :
2427 : // Create new inner node
2428 31 : m_inners[level-1].push_back(InnerNode(level));
2429 31 : inner = &m_inners[level-1].back();
2430 : }
2431 : else
2432 : {
2433 : // Append Key
2434 9369 : inner->keys[inner->slots++] = key;
2435 : }
2436 9400 : }
2437 :
2438 : public:
2439 : /**
2440 : * Insert a new key into the tree together with (offset,size) of the
2441 : * associated data value. The keys must be delivered to this function
2442 : * in ascending order!
2443 : */
2444 1186941 : void Add(const key_type& key, uint64_t offset, uint32_t size)
2445 : {
2446 1186941 : if (m_leaves.size() == 0) {
2447 : // create first leaf node
2448 69 : m_leaves.push_back(LeafNode());
2449 : }
2450 :
2451 1186941 : LeafNode* leaf = &m_leaves.back();
2452 :
2453 1186941 : if (leaf->slots > 0) {
2454 1186872 : CBTREEDB_ASSERT(m_key_less(leaf->LastKey(), key));
2455 : }
2456 :
2457 1186941 : if (leaf->IsFull())
2458 : {
2459 : // put last key into inner node(s)
2460 9369 : AddInner(1, leaf->LastKey());
2461 :
2462 : // create new leaf
2463 9369 : m_leaves.push_back(LeafNode());
2464 9369 : leaf = &m_leaves.back();
2465 9369 : leaf->baseoffset = offset;
2466 : }
2467 :
2468 : // append key + value items relative offset
2469 1186941 : leaf->keys[leaf->slots] = key;
2470 :
2471 1186941 : CBTREEDB_ASSERT(offset >= leaf->baseoffset);
2472 1186941 : leaf->offsets[leaf->slots] = offset - leaf->baseoffset;
2473 1186941 : leaf->offsets[leaf->slots+1] = leaf->offsets[leaf->slots] + size;
2474 :
2475 1186941 : ++leaf->slots;
2476 1186941 : ++m_size;
2477 1186941 : }
2478 :
2479 : /// Returns the number of items added to the tree.
2480 171222 : unsigned int Size() const
2481 : {
2482 171222 : return m_size;
2483 : }
2484 :
2485 : /// Return highest key currently inserted in the tree
2486 85532 : const key_type& GetLastKey() const
2487 : {
2488 85532 : CBTREEDB_CHECK(m_size > 0, "No keys inserted in the tree yet");
2489 :
2490 85532 : return m_leaves.back().LastKey();
2491 : }
2492 :
2493 : /// Return key previously inserted at given index
2494 85564 : void GetIndex(unsigned int index, key_type& outkey, uint32_t& outsize) const
2495 : {
2496 85564 : CBTREEDB_CHECK(index < m_size, "Attempting to retrieve out of bounds index.");
2497 :
2498 85564 : unsigned int leafnum = index / LeafNodeNumKeys;
2499 85564 : unsigned int slot = index % LeafNodeNumKeys;
2500 :
2501 85564 : CBTREEDB_ASSERT(leafnum < m_leaves.size());
2502 :
2503 85564 : const LeafNode* leaf = &m_leaves[leafnum];
2504 :
2505 85564 : CBTREEDB_ASSERT(slot < leaf->slots);
2506 :
2507 85564 : outkey = leaf->keys[slot];
2508 85564 : outsize = leaf->offsets[slot+1] - leaf->offsets[slot];
2509 85564 : }
2510 :
2511 : #if 0 // extra debug code for printing the tree
2512 :
2513 : /**
2514 : * Debugging function which prints out all keys in the currently
2515 : * constructed B-tree.
2516 : */
2517 : void Print(std::ostream& os) const
2518 : {
2519 : os << "Leaves:" << std::endl;
2520 : for (unsigned int i = 0; i < m_leaves.size(); ++i)
2521 : {
2522 : os << i << ":";
2523 : for (unsigned int j = 0; j < m_leaves[i].slots; ++j)
2524 : {
2525 : os << " " << m_leaves[i].keys[j];
2526 : }
2527 : os << std::endl;
2528 : }
2529 :
2530 : for (unsigned int l = 0; l < m_inners.size(); ++l)
2531 : {
2532 : os << "Level " << (l+1) << std::endl;
2533 :
2534 : for (unsigned int i = 0; i < m_inners[l].size(); ++i)
2535 : {
2536 : os << i << ":";
2537 : for (unsigned int j = 0; j < m_inners[l][i].slots; ++j)
2538 : {
2539 : os << " " << m_inners[l][i].keys[j];
2540 : }
2541 : os << std::endl;
2542 : }
2543 : }
2544 : }
2545 :
2546 : #endif // extra debug code for printing the tree
2547 :
2548 : /**
2549 : * Write function which outputs the constructed B-tree to a stream. The
2550 : * levels are outputted from root to leaf nodes in order. Updates the
2551 : * given signature page with B-tree information.
2552 : */
2553 69 : void Write(std::ostream& os, SignaturePage& signature)
2554 : {
2555 69 : signature.btree_pagesize = BTreePageSize;
2556 69 : signature.btree_levels = m_inners.size() + 1;
2557 69 : signature.btree_leaves = m_leaves.size();
2558 :
2559 : // Fill in childrenoffset field by precomputing the offsets.
2560 : {
2561 : // start with offset after the root (inner) node.
2562 69 : uint32_t offset = sizeof(InnerNode);
2563 :
2564 115 : for (int l = m_inners.size()-1; l >= 0; --l)
2565 : {
2566 123 : for (unsigned int i = 0; i < m_inners[l].size(); ++i)
2567 : {
2568 77 : InnerNode& inner = m_inners[l][i];
2569 :
2570 77 : inner.childrenoffset = offset;
2571 :
2572 : // add all children node sizes to offset.
2573 77 : offset += (inner.slots + 1) * sizeof(InnerNode);
2574 : }
2575 : }
2576 : }
2577 :
2578 : // Write out inner nodes
2579 :
2580 69 : uint64_t writesize = 0;
2581 69 : SHA256 sha;
2582 :
2583 115 : for (int l = m_inners.size()-1; l >= 0; --l)
2584 : {
2585 123 : for (unsigned int i = 0; i < m_inners[l].size(); ++i)
2586 : {
2587 77 : os.write(reinterpret_cast<char*>(&m_inners[l][i]), sizeof(m_inners[l][i]));
2588 77 : CBTREEDB_CHECK(os.good(), "Error writing B-tree inner node page to output stream.");
2589 :
2590 77 : sha.update(&m_inners[l][i], sizeof(m_inners[l][i]));
2591 :
2592 77 : writesize += sizeof(m_inners[l][i]);
2593 : }
2594 : }
2595 :
2596 : // Write out leaf nodes
2597 :
2598 69 : signature.btree_firstleaf = writesize;
2599 :
2600 9507 : for (unsigned int i = 0; i < m_leaves.size(); ++i)
2601 : {
2602 9438 : os.write(reinterpret_cast<char*>(&m_leaves[i]), sizeof(m_leaves[i]));
2603 9438 : CBTREEDB_CHECK(os.good(), "Error writing B-tree leaf node page to output stream.");
2604 :
2605 9438 : sha.update(&m_leaves[i], sizeof(m_leaves[i]));
2606 :
2607 9438 : writesize += sizeof(m_leaves[i]);
2608 : }
2609 :
2610 69 : sha.final(signature.btree_sha256);
2611 69 : signature.btree_size = writesize;
2612 69 : }
2613 : };
2614 :
2615 : public:
2616 : /**
2617 : * @brief Writer is used to construct an constant B-tree database from an
2618 : * unsorted input sequence.
2619 : *
2620 : * The writer class is fed with a possibly unordered sequence of keys
2621 : * together with their data. The complete data is buffered (and sorted) by
2622 : * the class! This means it will use a lot of virtual memory, so make sure
2623 : * your swap is large enough or use WriterSequential.
2624 : */
2625 : class Writer
2626 37 : {
2627 : protected:
2628 : /// Typedef key -> data mapping
2629 : typedef std::map<key_type, std::string, key_compare> datamap_type;
2630 :
2631 : /// STL map to store all key -> values.
2632 : datamap_type m_datamap;
2633 :
2634 : /// key comparison functional
2635 : key_compare m_key_less;
2636 :
2637 : /// Signature characters to begin file with.
2638 : char m_signaturestr[8];
2639 :
2640 : public:
2641 : /// Constructor
2642 37 : Writer(const key_compare& key_less=key_compare())
2643 : : m_datamap(key_less),
2644 37 : m_key_less(key_less)
2645 : {
2646 37 : memcpy(m_signaturestr, "cbtreedb", 8);
2647 37 : }
2648 :
2649 : /// Add a new key -> values mapping to the database.
2650 1101377 : void Add(const key_type& key, const void* data, size_t size)
2651 : {
2652 1101377 : m_datamap.insert( typename datamap_type::value_type(key, std::string(reinterpret_cast<const char*>(data), size)) );
2653 1101377 : }
2654 :
2655 : /// Add a new key -> values mapping to the database.
2656 0 : void Add(const key_type& key, const std::string& data)
2657 : {
2658 0 : Add(key, data.data(), data.size());
2659 0 : }
2660 :
2661 : /// Return number of items inserted into mapping
2662 35 : size_t Size() const
2663 : {
2664 35 : return m_datamap.size();
2665 : }
2666 :
2667 : /**
2668 : * Change the database signature from 'cbtreedb' to a custom
2669 : * string. The signature is always 8 bytes long. Longer strings are
2670 : * truncated, shorter ones padded with nulls.
2671 : */
2672 37 : void SetSignature(const char* newsignature)
2673 : {
2674 37 : unsigned int i = 0;
2675 333 : for(; i < 8 && newsignature[i]; ++i)
2676 296 : m_signaturestr[i] = newsignature[i];
2677 :
2678 37 : for(; i < 8; ++i)
2679 0 : m_signaturestr[i] = 0;
2680 37 : }
2681 :
2682 : /// Write the complete database out to a file. Because the stream must
2683 : /// be seekable, a simple ostream will not suffice.
2684 37 : void Write(std::ostream& os) const
2685 : {
2686 37 : os.clear();
2687 37 : os.seekp(0, std::ios::beg);
2688 :
2689 : // Write zeroed signature block to be overwritten when the file is
2690 : // finialized.
2691 :
2692 : SignaturePage signature;
2693 37 : memset(&signature, 0, sizeof(signature));
2694 37 : os.write(reinterpret_cast<char*>(&signature), sizeof(signature));
2695 :
2696 : char signature_padding[SignaturePageSize - sizeof(SignaturePage)];
2697 37 : memset(signature_padding, 0, SignaturePageSize - sizeof(SignaturePage));
2698 37 : os.write(signature_padding, SignaturePageSize - sizeof(SignaturePage));
2699 :
2700 37 : CBTREEDB_CHECK(os.good(), "Error writing signature block out output stream.");
2701 :
2702 : // Prepare signature for data
2703 :
2704 37 : memcpy(signature.signature, m_signaturestr, 8);
2705 37 : signature.version = 0x00010000;
2706 37 : signature.app_version_id = AppVersionId;
2707 37 : signature.items = m_datamap.size();
2708 37 : signature.key_size = sizeof(key_type);
2709 :
2710 : // Construct a and write a constant B-Tree
2711 :
2712 37 : BTreeBuilder btree(m_key_less);
2713 37 : uint64_t dataoffset = 0;
2714 :
2715 1101414 : for (typename datamap_type::const_iterator di = m_datamap.begin();
2716 : di != m_datamap.end(); ++di)
2717 : {
2718 1101377 : btree.Add(di->first, dataoffset, di->second.size());
2719 1101377 : dataoffset += di->second.size();
2720 : }
2721 :
2722 37 : signature.btree_offset = BTreePageSize;
2723 37 : btree.Write(os, signature);
2724 :
2725 37 : CBTREEDB_CHECK(os.good(), "Error writing B-tree pages to output stream.");
2726 37 : CBTREEDB_ASSERT(os.tellp() == std::ostream::pos_type(SignaturePageSize + signature.btree_size));
2727 :
2728 : // Write all value blobs to file
2729 :
2730 37 : SHA256 sha;
2731 :
2732 1101414 : for (typename datamap_type::const_iterator di = m_datamap.begin();
2733 : di != m_datamap.end(); ++di)
2734 : {
2735 1101377 : os.write(di->second.data(), di->second.size());
2736 1101377 : CBTREEDB_CHECK(os.good(), "Error writing data block to output stream.");
2737 :
2738 1101377 : sha.update(di->second.data(), di->second.size());
2739 : }
2740 :
2741 37 : CBTREEDB_ASSERT(os.tellp() == std::ostream::pos_type(SignaturePageSize + signature.btree_size + dataoffset));
2742 :
2743 : // Fill in signature page
2744 :
2745 37 : signature.value_offset = SignaturePageSize + signature.btree_size;
2746 37 : signature.value_size = dataoffset;
2747 37 : sha.final(signature.value_sha256);
2748 :
2749 : // Calculate header checksum
2750 :
2751 37 : signature.header_crc32 = CRC32::digest(reinterpret_cast<char*>(&signature)+16, sizeof(signature)-16);
2752 :
2753 37 : os.seekp(0, std::ios::beg);
2754 37 : CBTREEDB_CHECK(os.good(), "Error seeking back to signature page in output stream.");
2755 37 : CBTREEDB_ASSERT(os.tellp() == std::ostream::pos_type(0));
2756 :
2757 37 : os.write(reinterpret_cast<char*>(&signature), sizeof(signature));
2758 37 : CBTREEDB_CHECK(os.good(), "Error writing signature page to output stream.");
2759 37 : }
2760 : };
2761 :
2762 : public:
2763 : /**
2764 : * @brief WriterSequential is used to construct a constant B-tree database
2765 : * from an _ordered_ input sequence.
2766 : *
2767 : * The writer class is fed in two phases. In phase one the ordered sequence
2768 : * of keys together with their value data size (without contents) is
2769 : * delivered to the class via the Add() function. Phase two is started by
2770 : * calling WriteHeader() followed by a sequence to WriteValue() calls for
2771 : * each of the predeclared key-value pairs. The value data is written
2772 : * directly to the file and not buffered. The write loop is terminated by
2773 : * WriteFinalize(), which finalizes the database file.
2774 : */
2775 : class WriterSequential
2776 32 : {
2777 : protected:
2778 : /// key comparison functional
2779 : key_compare m_key_less;
2780 :
2781 : /// phase 1: b-tree object built from sequential predeclared sequence
2782 : BTreeBuilder m_btree;
2783 :
2784 : /// phase 1: value data offset counter for predeclared sequence
2785 : uint64_t m_dataoffset;
2786 :
2787 : /// signature characters to begin file with
2788 : char m_signaturestr[8];
2789 :
2790 : /// signature page of current written file
2791 : SignaturePage m_signature;
2792 :
2793 : /// phase 2: output stream
2794 : std::ostream* m_ostream;
2795 :
2796 : /// phase 2: current position in array
2797 : uint32_t m_currpos;
2798 :
2799 : /// phase 2: current offset in output stream
2800 : uint64_t m_curroffset;
2801 :
2802 : /// phase 2: running digest of the value area
2803 : SHA256 m_datasha;
2804 :
2805 : public:
2806 : /// Constructor
2807 32 : WriterSequential(const key_compare& key_less=key_compare())
2808 : : m_key_less(key_less),
2809 : m_btree(key_less),
2810 : m_dataoffset(0),
2811 : m_ostream(NULL),
2812 32 : m_currpos(-1)
2813 : {
2814 32 : memcpy(m_signaturestr, "cbtreedb", 8);
2815 32 : }
2816 :
2817 : /// Add a new key -> value size mapping to the database. The keys must
2818 : /// be added in ascending order.
2819 85564 : void Add(const key_type& key, uint32_t size)
2820 : {
2821 85564 : CBTREEDB_CHECK(m_btree.Size() == 0 || m_key_less(m_btree.GetLastKey(), key),
2822 : "Key sequence for Add() must be ascending.");
2823 85564 : CBTREEDB_CHECK(m_ostream == NULL,
2824 : "Cannot declare keys after starting phase 2.");
2825 :
2826 85564 : m_btree.Add(key, m_dataoffset, size);
2827 85564 : m_dataoffset += size;
2828 85564 : }
2829 :
2830 : /// Return number of pairs inserted into mapping
2831 30 : size_t Size() const
2832 : {
2833 30 : return m_btree.Size();
2834 : }
2835 :
2836 : /**
2837 : * Change the database signature from 'cbtreedb' to a custom
2838 : * string. The signature is always 8 bytes long. Longer strings are
2839 : * truncated, shorter ones padded with nulls.
2840 : */
2841 32 : void SetSignature(const char* newsignature)
2842 : {
2843 32 : unsigned int i = 0;
2844 288 : for(; i < 8 && newsignature[i]; ++i)
2845 256 : m_signaturestr[i] = newsignature[i];
2846 :
2847 32 : for(; i < 8; ++i)
2848 0 : m_signaturestr[i] = 0;
2849 32 : }
2850 :
2851 : /// Write header and b-tree to file stream. Starts Phase 2.
2852 32 : void WriteHeader(std::ostream& os)
2853 : {
2854 32 : CBTREEDB_CHECK(m_ostream == NULL,
2855 : "Cannot write header again in phase 2.");
2856 :
2857 32 : os.seekp(0, std::ios::beg);
2858 :
2859 : // write zeroed signature page to be overwritten when the file is
2860 : // finialized.
2861 32 : memset(&m_signature, 0, sizeof(m_signature));
2862 32 : os.write(reinterpret_cast<char*>(&m_signature), sizeof(m_signature));
2863 :
2864 : char signature_padding[SignaturePageSize - sizeof(SignaturePage)];
2865 32 : memset(signature_padding, 0, SignaturePageSize - sizeof(SignaturePage));
2866 32 : os.write(signature_padding, SignaturePageSize - sizeof(SignaturePage));
2867 :
2868 32 : CBTREEDB_CHECK(os.good(), "Error writing signature page to output stream.");
2869 :
2870 : // prepare signature for data
2871 :
2872 32 : memcpy(m_signature.signature, m_signaturestr, 8);
2873 32 : m_signature.version = 0x00010000;
2874 32 : m_signature.app_version_id = AppVersionId;
2875 32 : m_signature.btree_offset = SignaturePageSize;
2876 32 : m_signature.items = m_btree.Size();
2877 32 : m_signature.key_size = sizeof(key_type);
2878 32 : m_signature.value_size = m_dataoffset;
2879 :
2880 : // write constant B-tree
2881 :
2882 32 : m_btree.Write(os, m_signature);
2883 32 : CBTREEDB_CHECK(os.good(), "Error writing B-tree pages to output stream.");
2884 32 : CBTREEDB_ASSERT(os.tellp() == std::ostream::pos_type(SignaturePageSize + m_signature.btree_size));
2885 :
2886 : // prepare for writing value area
2887 :
2888 32 : m_ostream = &os;
2889 32 : m_currpos = 0;
2890 32 : m_curroffset = 0;
2891 32 : m_datasha.clear();
2892 32 : }
2893 :
2894 : /// Sequentially write value blobs to file. The key-value sequence must
2895 : /// match the pre-declared sequence.
2896 85564 : void WriteValue(const key_type& key, const void* data, uint32_t size)
2897 : {
2898 85564 : CBTREEDB_CHECK(m_ostream != NULL,
2899 : "Cannot write data, because phase 2 was not started.");
2900 :
2901 85564 : CBTREEDB_CHECK(m_currpos < m_btree.Size(),
2902 : "Invalid key in WriteData() beyond end of predeclaration.");
2903 :
2904 85564 : CBTREEDB_CHECK(m_ostream->tellp() == std::ostream::pos_type(SignaturePageSize + m_signature.btree_size + m_curroffset),
2905 : "Output stream data position is incorrect.");
2906 :
2907 : key_type expectedkey;
2908 : uint32_t expectedsize;
2909 :
2910 85564 : m_btree.GetIndex(m_currpos, expectedkey, expectedsize);
2911 :
2912 85564 : CBTREEDB_CHECK(!m_key_less(key, expectedkey) && !m_key_less(expectedkey, key), // test equality
2913 : "Key in WriteData() mismatches predeclared sequence.");
2914 :
2915 85564 : CBTREEDB_CHECK(size == expectedsize,
2916 : "Value data size in WriteData() mismatches predeclared sequence.");
2917 :
2918 85564 : m_ostream->write(reinterpret_cast<const char*>(data), size);
2919 85564 : CBTREEDB_CHECK(m_ostream->good(), "Error writing data blocks to output stream.");
2920 :
2921 85564 : m_datasha.update(data, size);
2922 :
2923 85564 : ++m_currpos;
2924 85564 : m_curroffset += size;
2925 85564 : }
2926 :
2927 : /// Sequentially write value blobs to file. The key-value sequence must
2928 : /// match the pre-declared sequence.
2929 0 : void WriteValue(const key_type& key, const std::string& data)
2930 : {
2931 0 : return WriteValue(key, data.data(), data.size());
2932 : }
2933 :
2934 : /// Finalize database file
2935 32 : void WriteFinalize()
2936 : {
2937 32 : CBTREEDB_CHECK(m_ostream != NULL,
2938 : "Cannot write data, because phase 2 was not started.");
2939 :
2940 32 : CBTREEDB_CHECK(m_currpos == m_btree.Size(),
2941 : "WriteFinalize() called before end of predeclared sequence.");
2942 :
2943 32 : CBTREEDB_CHECK(m_ostream->tellp() == std::ostream::pos_type(SignaturePageSize + m_signature.btree_size + m_signature.value_size),
2944 : "Output stream data position is incorrect.");
2945 :
2946 : // Fill in signature page
2947 :
2948 32 : m_signature.value_offset = SignaturePageSize + m_signature.btree_size;
2949 32 : m_datasha.final(m_signature.value_sha256);
2950 :
2951 : // Calculate header checksum
2952 :
2953 32 : m_signature.header_crc32 = CRC32::digest(reinterpret_cast<char*>(&m_signature)+16, sizeof(m_signature)-16);
2954 :
2955 32 : m_ostream->seekp(0, std::ios::beg);
2956 32 : CBTREEDB_CHECK(m_ostream->good(), "Error seeking back to signature page in output stream.");
2957 :
2958 32 : m_ostream->write(reinterpret_cast<char*>(&m_signature), sizeof(m_signature));
2959 32 : CBTREEDB_CHECK(m_ostream->good(), "Error writing signature page to output stream.");
2960 :
2961 32 : m_ostream->flush();
2962 32 : m_ostream = NULL;
2963 32 : }
2964 : };
2965 : };
2966 :
2967 : } // namespace stx
2968 :
2969 : #endif // _STX_CBTREEDB_H_
|