diff --git a/include/bitcoin/database/impl/primitives/arrayhead.ipp b/include/bitcoin/database/impl/primitives/arrayhead.ipp index 4d7c17a73..ec14fa05e 100644 --- a/include/bitcoin/database/impl/primitives/arrayhead.ipp +++ b/include/bitcoin/database/impl/primitives/arrayhead.ipp @@ -53,14 +53,20 @@ bool CLASS::enabled() const NOEXCEPT } TEMPLATE -Link CLASS::index(const Key& key) const NOEXCEPT +Link CLASS::index(size_t key) const NOEXCEPT { - // Key is the logical bucket index (no-hash). - if (key < buckets()) - return manager, Link::size>:: - cast_link(key); + if (key >= buckets()) + return {}; - return {}; + // Put index does not validate, allowing for head expansion. + return putter_index(key); +} + +TEMPLATE +Link CLASS::putter_index(size_t key) const NOEXCEPT +{ + // Key is the logical bucket index (no-hash). + return body::cast_link(key); } TEMPLATE @@ -117,15 +123,13 @@ bool CLASS::set_body_count(const Link& count) NOEXCEPT } TEMPLATE -Link CLASS::top(const Key& key) const NOEXCEPT +Link CLASS::at(size_t key) const NOEXCEPT { - return top(index(key)); -} + const auto link = index(key); + if (link.is_terminal()) + return {}; -TEMPLATE -Link CLASS::top(const Link& index) const NOEXCEPT -{ - const auto ptr = file_.get(link_to_position(index)); + const auto ptr = file_.get(link_to_position(link)); if (is_null(ptr)) return {}; diff --git a/include/bitcoin/database/impl/primitives/arraymap.ipp b/include/bitcoin/database/impl/primitives/arraymap.ipp index 53213f65d..a2181598b 100644 --- a/include/bitcoin/database/impl/primitives/arraymap.ipp +++ b/include/bitcoin/database/impl/primitives/arraymap.ipp @@ -129,50 +129,38 @@ code CLASS::reload() NOEXCEPT // ---------------------------------------------------------------------------- TEMPLATE -Link CLASS::top(const Link& link) const NOEXCEPT +bool CLASS::exists(size_t key) const NOEXCEPT { - if (link >= head_.buckets()) - return {}; - - return head_.top(link); + return !at(key).is_terminal(); } TEMPLATE -bool CLASS::exists(const Key& key) const NOEXCEPT +Link CLASS::at(size_t key) const NOEXCEPT { - return !first(key).is_terminal(); -} - -TEMPLATE -Link CLASS::first(const Key& key) const NOEXCEPT -{ - return head_.top(key); + return head_.at(key); } TEMPLATE template > -bool CLASS::find(const Key& key, Element& element) const NOEXCEPT +bool CLASS::at(size_t key, Element& element) const NOEXCEPT { - // This override avoids duplicated memory_ptr construct in get(first()). - const auto ptr = body_.get(); - return read(ptr, first(ptr, head_.top(key), key), element); + return get(at(key), element); } TEMPLATE template > bool CLASS::get(const Link& link, Element& element) const NOEXCEPT { - // This override is the normal form. return read(body_.get(), link, element); } TEMPLATE template > -bool CLASS::put(const Key& key, const Element& element) NOEXCEPT +bool CLASS::put(size_t key, const Element& element) NOEXCEPT { using namespace system; const auto count = element.count(); - const auto link = allocate(count); + const auto link = body_.allocate(count); const auto ptr = body_.get(link); if (!ptr) return false; @@ -180,11 +168,9 @@ bool CLASS::put(const Key& key, const Element& element) NOEXCEPT // iostream.flush is a nop (direct copy). iostream stream{ *ptr }; finalizer sink{ stream }; - sink.skip_bytes(Link::size); - sink.write_bytes(key); if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(Size * count);) } - return element.to_data(sink) && head_.push(link, head_.index(key)); + return element.to_data(sink) && head_.push(link, head_.putter_index(key)); } // protected @@ -195,10 +181,10 @@ template > bool CLASS::read(const memory_ptr& ptr, const Link& link, Element& element) NOEXCEPT { + using namespace system; if (!ptr || link.is_terminal()) return false; - using namespace system; const auto start = body::link_to_position(link); if (is_limited(start)) return false; diff --git a/include/bitcoin/database/primitives/arrayhead.hpp b/include/bitcoin/database/primitives/arrayhead.hpp index 431388a76..f203cef2f 100644 --- a/include/bitcoin/database/primitives/arrayhead.hpp +++ b/include/bitcoin/database/primitives/arrayhead.hpp @@ -29,7 +29,7 @@ namespace database { /// Dynamically expanding array map header. /// Less efficient than a fixed-size header. -template +template class arrayhead { public: @@ -57,15 +57,21 @@ class arrayhead bool get_body_count(Link& count) const NOEXCEPT; bool set_body_count(const Link& count) NOEXCEPT; - /// Convert natural key to head bucket index. - Link index(const Key& key) const NOEXCEPT; + /// Convert natural key to head bucket index (validated). + Link index(size_t key) const NOEXCEPT; + + /// Convert natural key to head bucket index (unvalidated). + Link putter_index(size_t key) const NOEXCEPT; /// Unsafe if verify false. - Link top(const Key& key) const NOEXCEPT; - Link top(const Link& index) const NOEXCEPT; + Link at(size_t key) const NOEXCEPT; + + /// Assign value to bucket index. bool push(const bytes& current, const Link& index) NOEXCEPT; private: + using body = manager, Link::size>; + template static auto& array_cast(memory::iterator buffer) NOEXCEPT { @@ -98,8 +104,8 @@ class arrayhead } // namespace database } // namespace libbitcoin -#define TEMPLATE template -#define CLASS arrayhead +#define TEMPLATE template +#define CLASS arrayhead #include diff --git a/include/bitcoin/database/primitives/arraymap.hpp b/include/bitcoin/database/primitives/arraymap.hpp index 282bd8414..44972d0dc 100644 --- a/include/bitcoin/database/primitives/arraymap.hpp +++ b/include/bitcoin/database/primitives/arraymap.hpp @@ -35,15 +35,13 @@ namespace database { /// Readers and writers are always prepositioned at data, and are limited to /// the extent the record/slab size is known (limit can always be removed). /// Streams are always initialized from first element byte up to file limit. -template +template class arraymap { public: DEFAULT_COPY_MOVE_DESTRUCT(arraymap); - using key = Key; using link = Link; - using iterator = database::iterator; arraymap(storage& header, storage& body, const Link& buckets) NOEXCEPT; @@ -89,18 +87,15 @@ class arraymap /// Query interface, iterator is not thread safe. /// ----------------------------------------------------------------------- - /// Return the link at the top of the conflict list (for table scanning). - Link top(const Link& list) const NOEXCEPT; - /// True if an instance of object with key exists. - bool exists(const Key& key) const NOEXCEPT; + bool exists(size_t key) const NOEXCEPT; - /// Return first element link or terminal if not found/error. - Link first(const Key& key) const NOEXCEPT; + /// Return element link at key or terminal if not found/error. + Link at(size_t key) const NOEXCEPT; /// Get first element matching the search key, false if not found/error. template = true> - bool find(const Key& key, Element& element) const NOEXCEPT; + bool at(size_t key, Element& element) const NOEXCEPT; /// Get element at link, false if deserialize error. template = true> @@ -109,7 +104,7 @@ class arraymap /// Allocate, set, commit element to key. /// Expands table AND HEADER as necessary. template = true> - bool put(const Key& key, const Element& element) NOEXCEPT; + bool put(size_t key, const Element& element) NOEXCEPT; protected: /// Get element at link using memory object, false if deserialize error. @@ -121,7 +116,7 @@ class arraymap static constexpr auto is_slab = (Size == max_size_t); using head = database::arrayhead; - using body = database::manager; + using body = database::manager, Size>; // Thread safe (index/top/push). // Not thread safe (create/open/close/backup/restore). @@ -132,14 +127,13 @@ class arraymap }; template -using array_map = arraymap, system::data_array, - Element::size>; +using array_map = arraymap, Element::size>; } // namespace database } // namespace libbitcoin -#define TEMPLATE template -#define CLASS arraymap +#define TEMPLATE template +#define CLASS arraymap #include diff --git a/include/bitcoin/database/tables/schema.hpp b/include/bitcoin/database/tables/schema.hpp index 864e5aca1..5327f3bb9 100644 --- a/include/bitcoin/database/tables/schema.hpp +++ b/include/bitcoin/database/tables/schema.hpp @@ -359,7 +359,7 @@ namespace schema struct prevout { static constexpr size_t pk = schema::spend_; - static constexpr size_t sk = zero; + ////static constexpr size_t sk = zero; static constexpr size_t minsize = schema::bit + // TODO: merge bit. schema::spend_ + diff --git a/test/mocks/chunk_storage.cpp b/test/mocks/chunk_storage.cpp index e0ccbc291..90bc73530 100644 --- a/test/mocks/chunk_storage.cpp +++ b/test/mocks/chunk_storage.cpp @@ -124,14 +124,20 @@ size_t chunk_storage::allocate(size_t chunk) NOEXCEPT memory_ptr chunk_storage::set(size_t offset, size_t size, uint8_t backfill) NOEXCEPT { - std::unique_lock field_lock(field_mutex_); - if (system::is_add_overflow(offset, size)) - return {}; - - std::unique_lock map_lock(map_mutex_); - const auto minimum = offset + size; - if (minimum > buffer_.size()) - buffer_.resize(minimum, backfill); + { + std::unique_lock field_lock(field_mutex_); + if (system::is_add_overflow(offset, size)) + { + return {}; + } + else + { + std::unique_lock map_lock(map_mutex_); + const auto minimum = offset + size; + if (minimum > buffer_.size()) + buffer_.resize(minimum, backfill); + } + } return get(offset); } diff --git a/test/primitives/arrayhead.cpp b/test/primitives/arrayhead.cpp index ece55c439..6df33db23 100644 --- a/test/primitives/arrayhead.cpp +++ b/test/primitives/arrayhead.cpp @@ -35,7 +35,7 @@ constexpr auto buckets = sub1(links); static_assert(buckets == 20u); using link = linkage; -using test_header = arrayhead; +using test_header = arrayhead; class nullptr_storage : public test::chunk_storage @@ -103,30 +103,14 @@ BOOST_AUTO_TEST_CASE(arrayhead__set_body_count__get__expected) BOOST_REQUIRE_EQUAL(count, expected); } -BOOST_AUTO_TEST_CASE(arrayhead__top__link__terminal) -{ - test::chunk_storage store; - test_header head{ store, buckets }; - BOOST_REQUIRE(head.create()); - BOOST_REQUIRE(head.top(9).is_terminal()); -} - -BOOST_AUTO_TEST_CASE(arrayhead__top__nullptr__terminal) -{ - nullptr_storage store; - test_header head{ store, buckets }; - BOOST_REQUIRE(head.create()); - BOOST_REQUIRE(head.top(9).is_terminal()); -} - -BOOST_AUTO_TEST_CASE(arrayhead__top__key__terminal) +BOOST_AUTO_TEST_CASE(arrayhead__at__key__terminal) { test::chunk_storage store; test_header head{ store, buckets }; // create() allocates and fills buckets with terminal. BOOST_REQUIRE(head.create()); - BOOST_REQUIRE(head.top(zero).is_terminal()); + BOOST_REQUIRE(head.at(zero).is_terminal()); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/primitives/arraymap.cpp b/test/primitives/arraymap.cpp index 205df7ab4..a9b27ec81 100644 --- a/test/primitives/arraymap.cpp +++ b/test/primitives/arraymap.cpp @@ -21,33 +21,29 @@ BOOST_AUTO_TEST_SUITE(arraymap_tests) -template +using namespace system; + +template class arraymap_ - : public arraymap + : public arraymap { public: - using base = arraymap; - using arraymap::arraymap; + using base = arraymap; + using arraymap::arraymap; }; -using namespace system; -using link5 = linkage<5>; -using key1 = data_array<1>; -using key10 = data_array<10>; - -// Key size does not factor into header byte size (for first key only). -constexpr size_t header_size = 105; -constexpr auto links = header_size / link5::size; -static_assert(links == 21u); +using link3 = linkage<3>; +struct record4 { static constexpr size_t size = 4; }; +using record_table = arraymap; // Bucket count is one less than link count, due to header.size field. -constexpr auto buckets = bc::sub1(links); -static_assert(buckets == 20u); +constexpr auto initial_buckets = 18; +constexpr auto initial_header_size = add1(initial_buckets) * link3::size; +constexpr auto initial_links = initial_header_size / link3::size; +static_assert(initial_links == 19u); -struct slab0 { static constexpr size_t size = max_size_t; }; -struct record4 { static constexpr size_t size = 4; }; -using slab_table = arraymap_; -using record_table = arraymap_; +////std::cout << head_store.buffer() << std::endl << std::endl; +////std::cout << body_store.buffer() << std::endl << std::endl; // record hashmap // ---------------------------------------------------------------------------- @@ -56,7 +52,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_construct__empty__expected) { test::chunk_storage head_store{}; test::chunk_storage body_store{}; - const record_table instance{ head_store, body_store, buckets }; + const record_table instance{ head_store, body_store, initial_buckets }; BOOST_REQUIRE(body_store.buffer().empty()); BOOST_REQUIRE(!instance.get_fault()); } @@ -67,128 +63,11 @@ BOOST_AUTO_TEST_CASE(arraymap__record_construct__non_empty__expected) test::chunk_storage head_store{}; test::chunk_storage body_store{}; body_store.buffer().resize(body_size); - const record_table instance{ head_store, body_store, buckets }; + const record_table instance{ head_store, body_store, initial_buckets }; BOOST_REQUIRE_EQUAL(body_store.buffer().size(), body_size); BOOST_REQUIRE(!instance.get_fault()); } -BOOST_AUTO_TEST_CASE(arraymap__record_getter__terminal__false) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - record_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - ////BOOST_REQUIRE(!instance.getter_(link5::terminal)); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__record_getter__empty__exhausted) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - record_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - ////BOOST_REQUIRE(instance.getter_(0)->is_exhausted()); - ////BOOST_REQUIRE(instance.getter_(19)->is_exhausted()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__record_getter__empty__false) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - record_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - ////BOOST_REQUIRE(!instance.getter_(key10{ 0x00 })); - ////BOOST_REQUIRE(!instance.getter_(key10{ 0x42 })); - BOOST_REQUIRE(!instance.get_fault()); -} - -// slab hashmap -// ---------------------------------------------------------------------------- - -BOOST_AUTO_TEST_CASE(arraymap__slab_construct__empty__expected) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - const slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(body_store.buffer().empty()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_construct__non_empty__expected_enabled) -{ - constexpr auto body_size = 12345u; - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - body_store.buffer().resize(body_size); - const slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE_EQUAL(body_store.buffer().size(), body_size); - BOOST_REQUIRE(instance.enabled()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_getter__terminal__false) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - ////BOOST_REQUIRE(!instance.getter_(link5::terminal)); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_getter__empty__exhausted) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - ////BOOST_REQUIRE(instance.getter_(0)->is_exhausted()); - ////BOOST_REQUIRE(instance.getter_(19)->is_exhausted()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_getter__empty__false) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - ////BOOST_REQUIRE(!instance.getter_(key10{ 0x00 })); - ////BOOST_REQUIRE(!instance.getter_(key10{ 0x42 })); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__enabled__non_empty_slab_zero_buckets__false) -{ - constexpr auto body_size = 12345u; - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - body_store.buffer().resize(body_size); - const slab_table instance{ head_store, body_store, 0 }; - BOOST_REQUIRE(!instance.enabled()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__enabled__empty_slab_one_bucket__false) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, 1 }; - BOOST_REQUIRE(!instance.enabled()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__enabled__empty_slab_nonzero_buckets__true) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.enabled()); - BOOST_REQUIRE(!instance.get_fault()); -} - // get/put // ---------------------------------------------------------------------------- @@ -199,7 +78,7 @@ class little_record static constexpr size_t size = sizeof(uint32_t); // record count or bytes count for slab (for allocate). - static constexpr link5 count() NOEXCEPT { return 1; } + static constexpr link3 count() NOEXCEPT { return 1; } bool from_data(database::reader& source) NOEXCEPT { @@ -220,7 +99,7 @@ class big_record { public: static constexpr size_t size = sizeof(uint32_t); - static constexpr link5 count() NOEXCEPT { return 1; } + static constexpr link3 count() NOEXCEPT { return 1; } bool from_data(database::reader& source) NOEXCEPT { @@ -241,10 +120,10 @@ BOOST_AUTO_TEST_CASE(arraymap__record_get__terminal__invalid) { test::chunk_storage head_store{}; test::chunk_storage body_store{}; - const hashmap instance{ head_store, body_store, buckets }; + const arraymap instance{ head_store, body_store, initial_buckets }; little_record record{}; - BOOST_REQUIRE(!instance.get(link5::terminal, record)); + BOOST_REQUIRE(!instance.get(link3::terminal, record)); BOOST_REQUIRE(!instance.get_fault()); } @@ -252,162 +131,90 @@ BOOST_AUTO_TEST_CASE(arraymap__record_get__empty__invalid) { test::chunk_storage head_store{}; test::chunk_storage body_store{}; - const hashmap instance{ head_store, body_store, buckets }; + const arraymap instance{ head_store, body_store, initial_buckets }; little_record record{}; BOOST_REQUIRE(!instance.get(0, record)); BOOST_REQUIRE(!instance.get_fault()); } -BOOST_AUTO_TEST_CASE(arraymap__record_get__populated__valid) +BOOST_AUTO_TEST_CASE(arraymap__record_get__big_end_populated__expected) { - data_chunk head_file; - data_chunk body_file + const data_chunk head_file { - 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, - 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, - 0x01, 0x02, 0x03, 0x04 + 0x00, 0x00, 0x10, // initial body size. + // -------------------- + 0xff, 0xff, 0xff, // head[0] -> null + 0x02, 0x00, 0x00, // head[1] -> link[2] + 0x03, 0x00, 0x00, // head[2] -> link[3] + 0xff, 0xff, 0xff, // head[3] -> null + 0xff, 0xff, 0xff, // head[4] -> null + 0xff, 0xff, 0xff, // head[5] -> null + 0xff, 0xff, 0xff, // head[6] -> null + 0xff, 0xff, 0xff, // head[7] -> null + 0xff, 0xff, 0xff, // head[8] -> null + 0x01, 0x00, 0x00, // head[9] -> link[1] + 0xff, 0xff, 0xff, // head[10] -> null + 0xff, 0xff, 0xff, // head[11] -> null + 0x00, 0x00, 0x00, // link[12] -> link[0] + 0xff, 0xff, 0xff, // head[13] -> null + 0xff, 0xff, 0xff, // head[14] -> null + 0xff, 0xff, 0xff, // head[15] -> null + 0xff, 0xff, 0xff, // head[16] -> null + 0xff, 0xff, 0xff // head[17] -> null }; - test::chunk_storage head_store{ head_file }; - test::chunk_storage body_store{ body_file }; - const hashmap instance{ head_store, body_store, buckets }; - - little_record record{}; - BOOST_REQUIRE(instance.get(0, record)); - BOOST_REQUIRE_EQUAL(record.value, 0x04030201_u32); - BOOST_REQUIRE(!instance.get_fault()); -} -BOOST_AUTO_TEST_CASE(arraymap__record_put__multiple__expected) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - - constexpr key1 key1_big{ 0x41 }; - constexpr key1 key1_little{ 0x42 }; - - link5 link{}; - BOOST_REQUIRE(instance.put_link(link, key1_big, big_record{ 0xa1b2c3d4_u32 })); - BOOST_REQUIRE(!link.is_terminal()); - BOOST_REQUIRE_EQUAL(link, 0u); - - link = instance.put_link(key1_little, little_record{ 0xa1b2c3d4_u32 }); - BOOST_REQUIRE(!link.is_terminal()); - BOOST_REQUIRE_EQUAL(link, 1u); - - big_record record1{}; - BOOST_REQUIRE(instance.get(0, record1)); - BOOST_REQUIRE_EQUAL(record1.value, 0xa1b2c3d4_u32); - - little_record record2{}; - BOOST_REQUIRE(instance.get(1, record2)); - BOOST_REQUIRE_EQUAL(record2.value, 0xa1b2c3d4_u32); - - // This expecatation relies on the fact of no hash table conflict between 0x41 and 0x42. - const data_chunk expected_file + const data_chunk body_file { - 0xff, 0xff, 0xff, 0xff, 0xff, - 0x41, - 0xa1, 0xb2, 0xc3, 0xd4, - - 0xff, 0xff, 0xff, 0xff, 0xff, - 0x42, - 0xd4, 0xc3, 0xb2, 0xa1 + 0xa1, 0xa2, 0xa3, 0xa4, // 0xa1a2a3a4 (big endian read/write) + 0xb1, 0xb2, 0xb3, 0xb4, // 0xb1b2b3b4 + 0xb5, 0xb6, 0xb7, 0xb8, // 0xb5b6b7b8 + 0x01, 0x02, 0x03, 0x04 // 0x01020304 }; - BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_file); - BOOST_REQUIRE(!instance.get_fault()); -} -class little_slab -{ -public: - static constexpr size_t size = max_size_t; - static constexpr link5 count() NOEXCEPT - { - return link5::size + array_count + sizeof(uint32_t); - } - - bool from_data(database::reader& source) NOEXCEPT - { - value = source.read_little_endian(); - return source; - } - - bool to_data(database::finalizer& sink) const NOEXCEPT - { - sink.write_little_endian(value); - return sink; - } - - uint32_t value{ 0 }; -}; + auto head_file_copy = head_file; + auto body_file_copy = body_file; + test::chunk_storage head_store{ head_file_copy }; + test::chunk_storage body_store{ body_file_copy }; + const arraymap instance{ head_store, body_store, initial_buckets }; -class big_slab -{ -public: - static constexpr size_t size = max_size_t; - static constexpr link5 count() NOEXCEPT - { - return link5::size + array_count + sizeof(uint32_t); - } - - bool from_data(database::reader& source) NOEXCEPT - { - value = source.read_big_endian(); - return source; - } - - bool to_data(database::finalizer& sink) const NOEXCEPT - { - sink.write_big_endian(value); - return sink; - } - - uint32_t value{ 0 }; -}; - -BOOST_AUTO_TEST_CASE(arraymap__slab_put__multiple__expected) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - - constexpr key1 key_big{ 0x41 }; - constexpr key1 key_little{ 0x42 }; - - link5 link{}; - BOOST_REQUIRE(instance.put_link(link, key_big, big_slab{ 0xa1b2c3d4_u32 })); - BOOST_REQUIRE(!link.is_terminal()); - BOOST_REQUIRE_EQUAL(link, 0u); - - link = instance.put_link(key_little, little_slab{ 0xa1b2c3d4_u32 }); - BOOST_REQUIRE(!link.is_terminal()); - BOOST_REQUIRE_EQUAL(link, big_slab::count()); - - big_slab slab1{}; - BOOST_REQUIRE(instance.get(0, slab1)); - BOOST_REQUIRE_EQUAL(slab1.value, 0xa1b2c3d4_u32); - - little_slab slab2{}; - BOOST_REQUIRE(instance.get(big_slab::count(), slab2)); - BOOST_REQUIRE_EQUAL(slab2.value, 0xa1b2c3d4_u32); + big_record record{}; - // This expecatation relies on the fact of no hash table conflict between 0x41 and 0x42. - const data_chunk expected_file - { - 0xff, 0xff, 0xff, 0xff, 0xff, - 0x41, - 0xa1, 0xb2, 0xc3, 0xd4, + // Get the link. + BOOST_REQUIRE(instance.get(0, record)); + BOOST_REQUIRE_EQUAL(record.value, 0xa1a2a3a4_u32); + BOOST_REQUIRE(instance.get(1, record)); + BOOST_REQUIRE_EQUAL(record.value, 0xb1b2b3b4_u32); + BOOST_REQUIRE(instance.get(2, record)); + BOOST_REQUIRE_EQUAL(record.value, 0xb5b6b7b8_u32); + BOOST_REQUIRE(instance.get(3, record)); + BOOST_REQUIRE_EQUAL(record.value, 0x01020304_u32); + BOOST_REQUIRE(!instance.get(4, record));; + + // Get from key. + BOOST_REQUIRE(!instance.at(0, record)); + BOOST_REQUIRE( instance.at(1, record)); + BOOST_REQUIRE_EQUAL(record.value, 0xb5b6b7b8_u32); + BOOST_REQUIRE( instance.at(2, record)); + BOOST_REQUIRE_EQUAL(record.value, 0x01020304_u32); + BOOST_REQUIRE(!instance.at(3, record)); + BOOST_REQUIRE(!instance.at(4, record)); + BOOST_REQUIRE(!instance.at(5, record)); + BOOST_REQUIRE(!instance.at(6, record)); + BOOST_REQUIRE(!instance.at(7, record)); + BOOST_REQUIRE(!instance.at(8, record)); + BOOST_REQUIRE( instance.at(9, record)); + BOOST_REQUIRE_EQUAL(record.value, 0xb1b2b3b4_u32); + BOOST_REQUIRE(!instance.at(10, record)); + BOOST_REQUIRE(!instance.at(11, record)); + BOOST_REQUIRE( instance.at(12, record)); + BOOST_REQUIRE_EQUAL(record.value, 0xa1a2a3a4_u32); + BOOST_REQUIRE(!instance.at(13, record)); + BOOST_REQUIRE(!instance.at(14, record)); + BOOST_REQUIRE(!instance.at(15, record)); + BOOST_REQUIRE(!instance.at(16, record)); + BOOST_REQUIRE(!instance.at(17, record)); - 0xff, 0xff, 0xff, 0xff, 0xff, - 0x42, - 0xd4, 0xc3, 0xb2, 0xa1 - }; - BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_file); BOOST_REQUIRE(!instance.get_fault()); } @@ -416,7 +223,7 @@ class record_excess { public: static constexpr size_t size = sizeof(uint32_t); - static constexpr link5 count() NOEXCEPT { return 1; } + static constexpr link3 count() NOEXCEPT { return 1; } bool from_data(database::reader& source) NOEXCEPT { @@ -433,597 +240,477 @@ class record_excess uint64_t value{ 0 }; }; -BOOST_AUTO_TEST_CASE(arraymap__record_get__excess__false) +BOOST_AUTO_TEST_CASE(arraymap__record_put__excess__false) { test::chunk_storage head_store{}; test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; + arraymap instance{ head_store, body_store, initial_buckets }; BOOST_REQUIRE(instance.create()); - - constexpr key1 key{ 0x41 }; - BOOST_REQUIRE(!instance.put_link(key, big_record{ 0xa1b2c3d4_u32 }).is_terminal()); + BOOST_REQUIRE(!instance.put(0, record_excess{ 0xa1b2c3d4_u32 })); record_excess record{}; BOOST_REQUIRE(!instance.get(0, record)); BOOST_REQUIRE(!instance.get_fault()); } -BOOST_AUTO_TEST_CASE(arraymap__record_get_key__excess__expected) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - - constexpr key1 key{ 0x41 }; - BOOST_REQUIRE(!instance.put_link(key, big_record{ 0xa1b2c3d4_u32 }).is_terminal()); - BOOST_REQUIRE_EQUAL(instance.get_key(0), key); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__record_put__excess__false) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - - constexpr key1 key{ 0x41 }; - BOOST_REQUIRE(instance.put_link(key, record_excess{ 0xa1b2c3d4_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.get_fault()); -} - -// advertises 32 but reads/writes 64 -class slab_excess -{ -public: - static constexpr size_t size = max_size_t; - static constexpr link5 count() NOEXCEPT { return sizeof(uint32_t); } - - bool from_data(database::reader& source) NOEXCEPT - { - value = source.read_big_endian(); - return source; - } - - bool to_data(database::finalizer& sink) const NOEXCEPT - { - sink.write_big_endian(value); - return sink; - } - - uint64_t value{ 0 }; -}; - -// advertises 32 but reads 65 (file is 64)/writes 64 -class file_excess -{ -public: - static constexpr size_t size = max_size_t; - static constexpr link5 count() NOEXCEPT { return sizeof(uint32_t); } - - bool from_data(database::reader& source) NOEXCEPT - { - value = source.read_big_endian(); - source.read_byte(); - return source; - } - - bool to_data(database::finalizer& sink) const NOEXCEPT - { - sink.write_big_endian(value); - return sink; - } - - uint64_t value{ 0 }; -}; - -BOOST_AUTO_TEST_CASE(arraymap__slab_get__excess__true) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - - constexpr key1 key{ 0x41 }; - BOOST_REQUIRE(!instance.put_link(key, big_slab{ 0xa1b2c3d4_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.put_link(key, big_slab{ 0xa1b2c3d4_u32 }).is_terminal()); - - // Excess read allowed to eof here (reader has only knowledge of size). - slab_excess slab{}; - BOOST_REQUIRE(instance.get(0, slab)); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_get_key__excess__expected) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - - constexpr key1 key{ 0x41 }; - BOOST_REQUIRE(!instance.put_link(key, big_slab{ 0xa1b2c3d4_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.put_link(key, big_slab{ 0xa1b2c3d4_u32 }).is_terminal()); - BOOST_REQUIRE_EQUAL(instance.get_key(0), key); - BOOST_REQUIRE_EQUAL(instance.get_key(10), key); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_get__file_excess__false) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - - constexpr key1 key{ 0x41 }; - BOOST_REQUIRE(!instance.put_link(key, big_slab{ 0xa1b2c3d4_u32 }).is_terminal()); - - // Excess read disallowed to here (past eof). - slab_excess slab{}; - BOOST_REQUIRE(!instance.get(0, slab)); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_put__excess__false) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - - constexpr key1 key{ 0x41 }; - BOOST_REQUIRE(instance.put_link(key, slab_excess{ 0xa1b2c3d4_u32 }).is_terminal()); -} - -BOOST_AUTO_TEST_CASE(arraymap__record_top__default__terminal) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - BOOST_REQUIRE(instance.top(0).is_terminal()); - BOOST_REQUIRE(instance.top(19).is_terminal()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__record_top__past_end__terminal) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - BOOST_REQUIRE(instance.top(20).is_terminal()); - BOOST_REQUIRE(instance.top(21).is_terminal()); -} - -BOOST_AUTO_TEST_CASE(arraymap__record_top__existing__expected) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - - BOOST_REQUIRE(!instance.put_link({ 0x41 }, big_record{ 0xa1b2c3d4_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.put_link({ 0x42 }, big_record{ 0xa2b2c3d4_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.put_link({ 0x43 }, big_record{ 0xa3b2c3d4_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.top(0).is_terminal()); - BOOST_REQUIRE(instance.top(1).is_terminal()); - BOOST_REQUIRE(instance.top(2).is_terminal()); - BOOST_REQUIRE(instance.top(3).is_terminal()); - BOOST_REQUIRE(instance.top(4).is_terminal()); - BOOST_REQUIRE(instance.top(5).is_terminal()); - BOOST_REQUIRE(instance.top(6).is_terminal()); - BOOST_REQUIRE(instance.top(7).is_terminal()); - BOOST_REQUIRE(instance.top(8).is_terminal()); - BOOST_REQUIRE(instance.top(9).is_terminal()); - BOOST_REQUIRE(instance.top(10).is_terminal()); - BOOST_REQUIRE(instance.top(11).is_terminal()); - BOOST_REQUIRE(instance.top(12).is_terminal()); - BOOST_REQUIRE(instance.top(13).is_terminal()); - BOOST_REQUIRE(instance.top(14).is_terminal()); - BOOST_REQUIRE(instance.top(15).is_terminal()); - BOOST_REQUIRE(instance.top(16).is_terminal()); - BOOST_REQUIRE(instance.top(17).is_terminal()); - BOOST_REQUIRE(!instance.top(18).is_terminal()); - BOOST_REQUIRE(!instance.top(19).is_terminal()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__record_exists__exists__true) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - - constexpr key1 key{ 0x41 }; - BOOST_REQUIRE(!instance.exists(key)); - BOOST_REQUIRE(!instance.put_link(key, big_record{ 0xa1b2c3d4_u32 }).is_terminal()); - BOOST_REQUIRE(instance.exists(key)); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_exists__exists__true) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - - constexpr key1 key{ 0x41 }; - BOOST_REQUIRE(!instance.exists(key)); - BOOST_REQUIRE(!instance.put_link(key, big_slab{ 0xa1b2c3d4_u32 }).is_terminal()); - BOOST_REQUIRE(instance.exists(key)); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__record_first__exists__true) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - - constexpr key1 key{ 0x41 }; - BOOST_REQUIRE(instance.first(key).is_terminal()); - const auto link = instance.put_link(key, big_record{ 0xa1b2c3d4_u32 }); - BOOST_REQUIRE(!link.is_terminal()); - BOOST_REQUIRE_EQUAL(instance.first(key), link); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_first__exists__true) +BOOST_AUTO_TEST_CASE(arraymap__record_get__excess__false) { test::chunk_storage head_store{}; test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; + arraymap instance{ head_store, body_store, initial_buckets }; BOOST_REQUIRE(instance.create()); + BOOST_REQUIRE(instance.put(0, big_record{ 0xa1b2c3d4_u32 })); - constexpr key1 key{ 0x41 }; - BOOST_REQUIRE(instance.first(key).is_terminal()); - const auto link = instance.put_link(key, big_slab{ 0xa1b2c3d4_u32 }); - BOOST_REQUIRE(!link.is_terminal()); - BOOST_REQUIRE_EQUAL(instance.first(key), link); + record_excess record{}; + BOOST_REQUIRE(!instance.get(0, record)); BOOST_REQUIRE(!instance.get_fault()); } -BOOST_AUTO_TEST_CASE(arraymap__record_it__exists__non_terminal) +BOOST_AUTO_TEST_CASE(arraymap__record_put__big_end_no_expansion__expected) { test::chunk_storage head_store{}; test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; + arraymap instance{ head_store, body_store, initial_buckets }; BOOST_REQUIRE(instance.create()); - constexpr key1 key{ 0x41 }; - BOOST_REQUIRE(instance.it(key).self().is_terminal()); - BOOST_REQUIRE(!instance.put_link(key, big_record{ 0xa1b2c3d4_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.it(key).self().is_terminal()); + const data_chunk expected_head = base16_chunk + ( + "000000" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + ); - big_record record{}; - BOOST_REQUIRE(instance.get(instance.it(key).self(), record)); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__record_it__multiple__iterated) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - - constexpr key1 key_a{ 0xaa }; - constexpr key1 key_b{ 0xbb }; - constexpr key1 key_c{ 0xcc }; + BOOST_REQUIRE_EQUAL(head_store.buffer(), expected_head); + BOOST_REQUIRE(body_store.buffer().empty()); - BOOST_REQUIRE(!instance.put_link(key_a, big_record{ 0x000000a1_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.put_link(key_a, big_record{ 0x000000a2_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.put_link(key_a, big_record{ 0x000000a3_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.put_link(key_b, big_record{ 0x000000b1_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.put_link(key_b, big_record{ 0x000000b2_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.put_link(key_b, big_record{ 0x000000b3_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.put_link(key_c, big_record{ 0x000000c1_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.put_link(key_c, big_record{ 0x000000c2_u32 }).is_terminal()); - BOOST_REQUIRE(!instance.put_link(key_c, big_record{ 0x000000c3_u32 }).is_terminal()); + const data_chunk expected_head1 = base16_chunk + ( + "000000" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "000000" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + ); + + const data_chunk expected_body1 = base16_chunk + ( + "a1b2c3d4" + ); + + BOOST_REQUIRE(instance.put(5_size, big_record{ 0xa1b2c3d4_u32 })); + BOOST_REQUIRE_EQUAL(head_store.buffer(), expected_head1); + BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_body1); + + const data_chunk expected_head2 = base16_chunk + ( + "000000" + // ----- + "020000" + "010000" + "ffffff" + "ffffff" + "ffffff" + "000000" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "030000" + ); + + const data_chunk expected_body2 = base16_chunk + ( + "a1b2c3d4" + "d4c3b2a1" + "42424242" + "12345678" + ); + + BOOST_REQUIRE(instance.put(1_size, big_record{ 0xd4c3b2a1_u32 })); + BOOST_REQUIRE(instance.put(0_size, big_record{ 0x42424242_u32 })); + BOOST_REQUIRE(instance.put(17_size, big_record{ 0x12345678_u32 })); + BOOST_REQUIRE_EQUAL(head_store.buffer(), expected_head2); + BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_body2); + + BOOST_REQUIRE(!instance.get_fault()); +} + +BOOST_AUTO_TEST_CASE(arraymap__record_put__little_end_default_expansion__expected) +{ + test::chunk_storage head_store{}; + test::chunk_storage body_store{}; + arraymap instance{ head_store, body_store, initial_buckets }; + BOOST_REQUIRE(instance.create()); + + const data_chunk expected_head = base16_chunk + ( + "000000" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + ); + + BOOST_REQUIRE_EQUAL(head_store.buffer(), expected_head); + BOOST_REQUIRE(body_store.buffer().empty()); - auto it_a = instance.it(key_a); + const data_chunk expected_head1 = base16_chunk + ( + "000000" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "000000" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + ); + + const data_chunk expected_body1 = base16_chunk + ( + "d4c3b2a1" + ); + + BOOST_REQUIRE(instance.put(5_size, little_record{ 0xa1b2c3d4_u32 })); + BOOST_REQUIRE_EQUAL(head_store.buffer(), expected_head1); + BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_body1); + + const data_chunk expected_head2 = base16_chunk + ( + "000000" + // ----- + "020000" + "010000" + "ffffff" + "ffffff" + "ffffff" + "000000" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "030000" + ); + + const data_chunk expected_body2 = base16_chunk + ( + "d4c3b2a1" + "a1b2c3d4" + "42424242" + "78563412" + ); + + BOOST_REQUIRE(instance.put(1_size, little_record{ 0xd4c3b2a1_u32 })); + BOOST_REQUIRE(instance.put(0_size, little_record{ 0x42424242_u32 })); + BOOST_REQUIRE(instance.put(20_size, little_record{ 0x12345678_u32 })); + BOOST_REQUIRE_EQUAL(head_store.buffer(), expected_head2); + BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_body2); + + const data_chunk expected_head3 = base16_chunk + ( + "000000" + // ----- + "020000" + "010000" + "ffffff" + "ffffff" + "ffffff" + "000000" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "030000" + "ffffff" + "050000" + "040000" + ); + + const data_chunk expected_body3 = base16_chunk + ( + "d4c3b2a1" + "a1b2c3d4" + "42424242" + "78563412" + "88664422" + "77553311" + ); + + BOOST_REQUIRE(instance.put(23_size, little_record{ 0x22446688_u32 })); + BOOST_REQUIRE(instance.put(22_size, little_record{ 0x11335577_u32 })); + BOOST_REQUIRE_EQUAL(head_store.buffer(), expected_head3); + BOOST_REQUIRE_EQUAL(body_store.buffer(), expected_body3); + + BOOST_REQUIRE(!instance.get_fault()); +} + +// exists + +BOOST_AUTO_TEST_CASE(arraymap__record_exists__default__false) +{ + test::chunk_storage head_store{}; + test::chunk_storage body_store{}; + arraymap instance{ head_store, body_store, initial_buckets }; + BOOST_REQUIRE(instance.create()); + BOOST_REQUIRE(!instance.exists(0)); + BOOST_REQUIRE(!instance.exists(17)); + BOOST_REQUIRE(!instance.get_fault()); +} + +BOOST_AUTO_TEST_CASE(arraymap__record_exists__past_end__false) +{ + test::chunk_storage head_store{}; + test::chunk_storage body_store{}; + arraymap instance{ head_store, body_store, initial_buckets }; + BOOST_REQUIRE(instance.create()); + BOOST_REQUIRE(!instance.exists(18)); + BOOST_REQUIRE(!instance.exists(19)); +} + +BOOST_AUTO_TEST_CASE(arraymap__record_exists__existing__expected) +{ + data_chunk store_head = base16_chunk + ( + "000000" + // ----- + "020000" + "010000" + "ffffff" + "ffffff" + "ffffff" + "000000" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "030000" + "ffffff" + "050000" + "040000" + ); + + data_chunk store_body = base16_chunk + ( + "d4c3b2a1" + "a1b2c3d4" + "42424242" + "78563412" + "88664422" + "77553311" + ); + + test::chunk_storage head_store{ store_head }; + test::chunk_storage body_store{ store_body }; + arraymap instance{ head_store, body_store, initial_buckets }; + + BOOST_REQUIRE(instance.exists(0)); + BOOST_REQUIRE(instance.exists(1)); + BOOST_REQUIRE(!instance.exists(2)); + BOOST_REQUIRE(!instance.exists(3)); + BOOST_REQUIRE(!instance.exists(4)); + BOOST_REQUIRE(instance.exists(5)); + BOOST_REQUIRE(!instance.exists(6)); + BOOST_REQUIRE(!instance.exists(7)); + BOOST_REQUIRE(!instance.exists(8)); + BOOST_REQUIRE(!instance.exists(9)); + BOOST_REQUIRE(!instance.exists(10)); + BOOST_REQUIRE(!instance.exists(11)); + BOOST_REQUIRE(!instance.exists(12)); + BOOST_REQUIRE(!instance.exists(13)); + BOOST_REQUIRE(!instance.exists(14)); + BOOST_REQUIRE(!instance.exists(15)); + BOOST_REQUIRE(!instance.exists(16)); + BOOST_REQUIRE(!instance.exists(17)); + BOOST_REQUIRE(!instance.exists(18)); + BOOST_REQUIRE(!instance.exists(19)); + BOOST_REQUIRE(instance.exists(20)); + BOOST_REQUIRE(!instance.exists(21)); + BOOST_REQUIRE(instance.exists(22)); + BOOST_REQUIRE(instance.exists(23)); + BOOST_REQUIRE(!instance.get_fault()); +} + +BOOST_AUTO_TEST_CASE(arraymap__record_at__existing__expected) +{ + data_chunk store_head = base16_chunk + ( + "000000" + // ----- + "020000" + "010000" + "ffffff" + "ffffff" + "ffffff" + "000000" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "030000" + "ffffff" + "050000" + "040000" + ); + + data_chunk store_body = base16_chunk + ( + "d4c3b2a1" + "a1b2c3d4" + "42424242" + "78563412" + "88664422" + "77553311" + ); + + test::chunk_storage head_store{ store_head }; + test::chunk_storage body_store{ store_body }; + arraymap instance{ head_store, body_store, initial_buckets }; + + BOOST_REQUIRE_EQUAL(instance.at(0), 2u); + BOOST_REQUIRE_EQUAL(instance.at(1), 1u); + BOOST_REQUIRE_EQUAL(instance.at(2), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(3), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(4), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(5), 0u); + BOOST_REQUIRE_EQUAL(instance.at(6), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(7), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(8), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(9), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(10), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(11), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(12), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(13), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(14), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(15), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(16), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(17), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(18), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(19), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(20), 3u); + BOOST_REQUIRE_EQUAL(instance.at(21), link3::terminal); + BOOST_REQUIRE_EQUAL(instance.at(22), 5u); + BOOST_REQUIRE_EQUAL(instance.at(23), 4u); + + BOOST_REQUIRE(!instance.get_fault()); +} + +BOOST_AUTO_TEST_CASE(arraymap__record_at__exists__true) +{ + test::chunk_storage head_store{}; + test::chunk_storage body_store{}; + arraymap instance{ head_store, body_store, initial_buckets }; + BOOST_REQUIRE(instance.create()); + + constexpr uint32_t expected = 0xa1b2c3d4; + BOOST_REQUIRE(instance.at(1).is_terminal()); + BOOST_REQUIRE(instance.put(1, big_record{ expected })); + BOOST_REQUIRE_EQUAL(instance.at(1), 0u); big_record record{}; - BOOST_REQUIRE(instance.get(it_a.self(), record)); - BOOST_REQUIRE_EQUAL(record.value, 0x000000a3_u32); - BOOST_REQUIRE(it_a.advance()); - BOOST_REQUIRE(instance.get(it_a.self(), record)); - BOOST_REQUIRE_EQUAL(record.value, 0x000000a2_u32); - BOOST_REQUIRE(it_a.advance()); - BOOST_REQUIRE(instance.get(it_a.self(), record)); - BOOST_REQUIRE_EQUAL(record.value, 0x000000a1_u32); - BOOST_REQUIRE(!it_a.advance()); - BOOST_REQUIRE(!instance.get(it_a.self(), record)); - - auto it_b = instance.it(key_b); - - BOOST_REQUIRE(instance.get(it_b.self(), record)); - BOOST_REQUIRE_EQUAL(record.value, 0x000000b3_u32); - BOOST_REQUIRE(it_b.advance()); - BOOST_REQUIRE(instance.get(it_b.self(), record)); - BOOST_REQUIRE_EQUAL(record.value, 0x000000b2_u32); - BOOST_REQUIRE(it_b.advance()); - BOOST_REQUIRE(instance.get(it_b.self(), record)); - BOOST_REQUIRE_EQUAL(record.value, 0x000000b1_u32); - BOOST_REQUIRE(!it_b.advance()); - BOOST_REQUIRE(!instance.get(it_b.self(), record)); - - auto it_c = instance.it(key_c); - - BOOST_REQUIRE(instance.get(it_c.self(), record)); - BOOST_REQUIRE_EQUAL(record.value, 0x000000c3_u32); - BOOST_REQUIRE(it_c.advance()); - BOOST_REQUIRE(instance.get(it_c.self(), record)); - BOOST_REQUIRE_EQUAL(record.value, 0x000000c2_u32); - BOOST_REQUIRE(it_c.advance()); - BOOST_REQUIRE(instance.get(it_c.self(), record)); - BOOST_REQUIRE_EQUAL(record.value, 0x000000c1_u32); - BOOST_REQUIRE(!it_c.advance()); - BOOST_REQUIRE(!instance.get(it_c.self(), record)); - BOOST_REQUIRE(!instance.get_fault()); - - // [0000000000] - //[b] 0500000000 - // ffffffffff - // ffffffffff - //[a] 0200000000 - // ffffffffff - // ffffffffff - // ffffffffff - // ffffffffff - // ffffffffff - // ffffffffff - // ffffffffff - // ffffffffff - // ffffffffff - // ffffffffff - // ffffffffff - // ffffffffff - // ffffffffff - //[c] 0800000000 - // ffffffffff - // ffffffffff - //================== - //[0] ffffffffff - // aa - // 000000a1 - // - //[1] 0000000000 - // aa - // 000000a2 - // - //[2] 0100000000 - // aa - // 000000a3 - // - //[3] ffffffffff - // bb - // 000000b1 - // - //[4] 0300000000 - // bb - // 000000b2 - // - //[5] 0400000000 - // bb - // 000000b3 - // - //[6] ffffffffff - // cc - // 000000c1 - // - //[7] 0600000000 - // cc - // 000000c2 - // - //[8] 0700000000 - // cc - // 000000c3 -} - -// mutiphase commit. -// ---------------------------------------------------------------------------- - -class flex_record -{ -public: - static constexpr size_t size = sizeof(uint32_t); - static constexpr link5 count() NOEXCEPT { return 1; } - - template - bool to_data(Sinker& sink) const NOEXCEPT - { - sink.write_little_endian(value); - return sink; - } - - uint32_t value{ 0 }; -}; - -class flex_slab -{ -public: - static constexpr size_t size = max_size_t; - static constexpr link5 count() NOEXCEPT - { - return link5::size + array_count + sizeof(uint32_t); - } - - template - bool to_data(Sinker& sink) const NOEXCEPT - { - sink.write_little_endian(value); - return sink; - } - - uint32_t value{ 0 }; -}; - -BOOST_AUTO_TEST_CASE(arraymap__set_commit__record__expected) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, 2 }; - BOOST_REQUIRE(instance.create()); - - constexpr auto size = link5::size + array_count + flex_record::size; - const auto link = instance.set_link(flex_record{ 0x01020304_u32 }); - BOOST_REQUIRE(!link.is_terminal()); - BOOST_REQUIRE_EQUAL(link, 0u); - BOOST_REQUIRE_EQUAL(body_store.buffer().size(), size); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0000000000ffffffffffffffffffff")); - BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("00000000000000000000000000000004030201")); - - constexpr key10 key1{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a }; - BOOST_REQUIRE(instance.commit(link, key1)); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("00000000000000000000ffffffffff")); - BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("ffffffffff0102030405060708090a04030201")); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__allocate_set_commit__record__expected) -{ - data_chunk head_file; - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, 2 }; - BOOST_REQUIRE(instance.create()); - - constexpr auto size = link5::size + array_count + flex_record::size; - const auto link = instance.allocate(1); - BOOST_REQUIRE_EQUAL(link, 0u); - BOOST_REQUIRE_EQUAL(body_store.buffer().size(), size); - - BOOST_REQUIRE(instance.set(link, flex_record{ 0x01020304_u32 })); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0000000000ffffffffffffffffffff")); - BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("00000000000000000000000000000004030201")); - - constexpr key10 key1{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a }; - BOOST_REQUIRE(instance.commit(link, key1)); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("00000000000000000000ffffffffff")); - BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("ffffffffff0102030405060708090a04030201")); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__allocate_put1__record__expected) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, 2 }; - BOOST_REQUIRE(instance.create()); - - constexpr auto size = link5::size + array_count + sizeof(uint32_t); - const auto link = instance.allocate(1); - BOOST_REQUIRE_EQUAL(link, 0u); - BOOST_REQUIRE_EQUAL(body_store.buffer().size(), size); - - constexpr key10 key1{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a }; - BOOST_REQUIRE(instance.put(link, key1, flex_record{ 0x01020304_u32 })); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("00000000000000000000ffffffffff")); - BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("ffffffffff0102030405060708090a04030201")); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__allocate_put2__record__expected) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, 2 }; - BOOST_REQUIRE(instance.create()); - - constexpr key10 key1{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a }; - BOOST_REQUIRE(instance.put(key1, flex_record{ 0x01020304_u32 })); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("00000000000000000000ffffffffff")); - BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("ffffffffff0102030405060708090a04030201")); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__set_commit_link__slab__expected) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, 2 }; - BOOST_REQUIRE(instance.create()); - - constexpr auto size = link5::size + array_count + sizeof(uint32_t); - link5 link{}; - BOOST_REQUIRE(instance.set_link(link, flex_slab{ 0x01020304_u32 })); - BOOST_REQUIRE(!link.is_terminal()); - BOOST_REQUIRE_EQUAL(link, 0u); - BOOST_REQUIRE_EQUAL(body_store.buffer().size(), size); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0000000000ffffffffffffffffffff")); - BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("00000000000000000000000000000004030201")); - - constexpr key10 key1{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a }; - BOOST_REQUIRE(!instance.commit_link(link, key1).is_terminal()); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("00000000000000000000ffffffffff")); - BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("ffffffffff0102030405060708090a04030201")); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__allocate_set_commit__slab__expected) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - hashmap instance{ head_store, body_store, 2 }; - BOOST_REQUIRE(instance.create()); - - constexpr auto size = link5::size + array_count + sizeof(uint32_t); - const auto link = instance.allocate(size); - BOOST_REQUIRE_EQUAL(link, 0u); - BOOST_REQUIRE_EQUAL(body_store.buffer().size(), size); - - BOOST_REQUIRE(instance.set(link, flex_slab{ 0x01020304_u32 })); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0000000000ffffffffffffffffffff")); - BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("00000000000000000000000000000004030201")); - - constexpr key10 key1{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a }; - BOOST_REQUIRE(instance.commit(link, key1)); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("00000000000000000000ffffffffff")); - BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("ffffffffff0102030405060708090a04030201")); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__allocate_put1__slab__expected) -{ - data_chunk head_file; - data_chunk body_file; - test::chunk_storage head_store{ head_file }; - test::chunk_storage body_store{ body_file }; - hashmap instance{ head_store, body_store, 2 }; - BOOST_REQUIRE(instance.create()); - - constexpr auto size = link5::size + array_count + sizeof(uint32_t); - const auto link = instance.allocate(size); - BOOST_REQUIRE_EQUAL(link, 0u); - BOOST_REQUIRE_EQUAL(body_file.size(), size); - - constexpr key10 key1{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a }; - BOOST_REQUIRE(instance.put(link, key1, flex_slab{ 0x01020304_u32 })); - BOOST_REQUIRE_EQUAL(head_file, base16_chunk("00000000000000000000ffffffffff")); - BOOST_REQUIRE_EQUAL(body_file, base16_chunk("ffffffffff0102030405060708090a04030201")); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__allocate_put2__slab__expected) -{ - data_chunk head_file; - data_chunk body_file; - test::chunk_storage head_store{ head_file }; - test::chunk_storage body_store{ body_file }; - hashmap instance{ head_store, body_store, 2 }; - BOOST_REQUIRE(instance.create()); - - constexpr key10 key1{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a }; - BOOST_REQUIRE(instance.put(key1, flex_slab{ 0x01020304_u32 })); - BOOST_REQUIRE_EQUAL(head_file, base16_chunk("00000000000000000000ffffffffff")); - BOOST_REQUIRE_EQUAL(body_file, base16_chunk("ffffffffff0102030405060708090a04030201")); + BOOST_REQUIRE(instance.get(0, record)); + BOOST_REQUIRE_EQUAL(record.value, expected); BOOST_REQUIRE(!instance.get_fault()); } @@ -1034,11 +721,11 @@ BOOST_AUTO_TEST_CASE(arraymap__record_verify__empty_files__expected) { test::chunk_storage head_store{}; test::chunk_storage body_store{}; - record_table instance{ head_store, body_store, buckets }; + record_table instance{ head_store, body_store, initial_buckets }; BOOST_REQUIRE(!instance.verify()); BOOST_REQUIRE(instance.create()); BOOST_REQUIRE(instance.verify()); - BOOST_REQUIRE_EQUAL(head_store.buffer().size(), header_size); + BOOST_REQUIRE_EQUAL(head_store.buffer().size(), initial_header_size); BOOST_REQUIRE(body_store.buffer().empty()); BOOST_REQUIRE(!instance.get_fault()); } @@ -1049,7 +736,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_create__non_empty_head_file__failure) data_chunk body_file; test::chunk_storage head_store{ head_file }; test::chunk_storage body_store{ body_file }; - record_table instance{ head_store, body_store, buckets }; + record_table instance{ head_store, body_store, initial_buckets }; BOOST_REQUIRE(!instance.verify()); BOOST_REQUIRE(!instance.create()); BOOST_REQUIRE_EQUAL(head_file.size(), 1u); @@ -1063,57 +750,82 @@ BOOST_AUTO_TEST_CASE(arraymap__record_create__non_empty_body_file__body_zeroed) data_chunk body_file{ 0x42 }; test::chunk_storage head_store{ head_file }; test::chunk_storage body_store{ body_file }; - record_table instance{ head_store, body_store, buckets }; + record_table instance{ head_store, body_store, initial_buckets }; BOOST_REQUIRE(!instance.verify()); BOOST_REQUIRE(instance.create()); BOOST_REQUIRE(instance.verify()); - BOOST_REQUIRE_EQUAL(head_file.size(), header_size); + BOOST_REQUIRE_EQUAL(head_file.size(), initial_header_size); BOOST_REQUIRE(body_file.empty()); + BOOST_REQUIRE(!instance.get_fault()); } BOOST_AUTO_TEST_CASE(arraymap__record_body_count__create__zero) { test::chunk_storage head_store{}; test::chunk_storage body_store{}; - record_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + record_table instance{ head_store, body_store, initial_buckets }; + BOOST_REQUIRE(instance.create()); + BOOST_REQUIRE_EQUAL(head_store.buffer(), + base16_chunk + ( + "000000" + // ----- + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + "ffffff" + )); + BOOST_REQUIRE(body_store.buffer().empty()); BOOST_REQUIRE(!instance.get_fault()); } BOOST_AUTO_TEST_CASE(arraymap__record_body_count__empty_close__zero) { - auto head_file = base16_chunk("1234567890"); + auto head_file = base16_chunk("123456"); data_chunk body_file; test::chunk_storage head_store{ head_file }; test::chunk_storage body_store{ body_file }; - record_table instance{ head_store, body_store, buckets }; + record_table instance{ head_store, body_store, initial_buckets }; BOOST_REQUIRE(instance.close()); - BOOST_REQUIRE_EQUAL(head_file, base16_chunk("0000000000")); + BOOST_REQUIRE_EQUAL(head_file, base16_chunk("000000")); BOOST_REQUIRE(!instance.get_fault()); } -BOOST_AUTO_TEST_CASE(arraymap__record_body_count__two_close__two) +BOOST_AUTO_TEST_CASE(arraymap__record_body_count__nine_close__nine) { test::chunk_storage head_store{}; test::chunk_storage body_store{}; - record_table instance{ head_store, body_store, buckets }; + record_table instance{ head_store, body_store, initial_buckets }; BOOST_REQUIRE(instance.create()); - body_store.buffer() = base16_chunk("1122334455667788990011223344556677889911223344556677889900112233445566778899"); + body_store.buffer() = base16_chunk("112233441122334411223344112233441122334411223344112233441122334411223344"); BOOST_REQUIRE(instance.close()); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0200000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("090000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); BOOST_REQUIRE(!instance.get_fault()); } -BOOST_AUTO_TEST_CASE(arraymap__record_body_count__two_backup__two) +BOOST_AUTO_TEST_CASE(arraymap__record_body_count__nine_backup__nine) { test::chunk_storage head_store{}; test::chunk_storage body_store{}; - record_table instance{ head_store, body_store, buckets }; + record_table instance{ head_store, body_store, initial_buckets }; BOOST_REQUIRE(instance.create()); - body_store.buffer() = base16_chunk("1122334455667788990011223344556677889911223344556677889900112233445566778899"); + body_store.buffer() = base16_chunk("112233441122334411223344112233441122334411223344112233441122334411223344"); BOOST_REQUIRE(instance.backup()); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0200000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("090000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); BOOST_REQUIRE(!instance.get_fault()); } @@ -1121,7 +833,7 @@ BOOST_AUTO_TEST_CASE(arraymap__record_body_count__empty_restore__truncates) { test::chunk_storage head_store{}; test::chunk_storage body_store{}; - record_table instance{ head_store, body_store, buckets }; + record_table instance{ head_store, body_store, initial_buckets }; BOOST_REQUIRE(instance.create()); body_store.buffer() = base16_chunk("1234567812345678"); BOOST_REQUIRE(instance.restore()); @@ -1133,132 +845,13 @@ BOOST_AUTO_TEST_CASE(arraymap__record_body_count__non_empty_restore__truncates) { test::chunk_storage head_store{}; test::chunk_storage body_store{}; - record_table instance{ head_store, body_store, buckets }; + record_table instance{ head_store, body_store, initial_buckets }; BOOST_REQUIRE(instance.create()); - head_store.buffer() = base16_chunk("0100000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - body_store.buffer() = base16_chunk("1122334455667788990011223344556677889911223344556677889900112233445566778899"); + head_store.buffer() = base16_chunk("090000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + body_store.buffer() = base16_chunk("112233441122334411223344112233441122334411223344112233441122334411223344abababababababab"); BOOST_REQUIRE(instance.restore()); - BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("11223344556677889900112233445566778899")); + BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("112233441122334411223344112233441122334411223344112233441122334411223344")); BOOST_REQUIRE(!instance.get_fault()); } -// slab create/close/backup/restore/verify -// ---------------------------------------------------------------------------- - -BOOST_AUTO_TEST_CASE(arraymap__slab_verify__empty_files__expected) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(!instance.verify()); - BOOST_REQUIRE(instance.create()); - BOOST_REQUIRE(instance.verify()); - BOOST_REQUIRE_EQUAL(head_store.buffer().size(), header_size); - BOOST_REQUIRE(body_store.buffer().empty()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_create__non_empty_head_file__failure) -{ - data_chunk head_file{ 0x42 }; - data_chunk body_file; - test::chunk_storage head_store{ head_file }; - test::chunk_storage body_store{ body_file }; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(!instance.verify()); - BOOST_REQUIRE(!instance.create()); - BOOST_REQUIRE_EQUAL(head_file.size(), 1u); - BOOST_REQUIRE(body_file.empty()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_create__non_empty_body_file__body_zeroed) -{ - data_chunk head_file; - data_chunk body_file{ 0x42 }; - test::chunk_storage head_store{ head_file }; - test::chunk_storage body_store{ body_file }; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(!instance.verify()); - BOOST_REQUIRE(instance.create()); - BOOST_REQUIRE(instance.verify()); - BOOST_REQUIRE_EQUAL(head_file.size(), header_size); - BOOST_REQUIRE(body_file.empty()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__create__zero) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__empty_close__zero) -{ - auto head_file = base16_chunk("1234567890"); - data_chunk body_file; - test::chunk_storage head_store{ head_file }; - test::chunk_storage body_store{ body_file }; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.close()); - BOOST_REQUIRE_EQUAL(head_file, base16_chunk("0000000000")); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__two_close__two) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - body_store.buffer() = base16_chunk("1234"); - BOOST_REQUIRE(instance.close()); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0200000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__two_backup__two) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - body_store.buffer() = base16_chunk("1234"); - BOOST_REQUIRE(instance.backup()); - BOOST_REQUIRE_EQUAL(head_store.buffer(), base16_chunk("0200000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__empty_restore__truncates) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - body_store.buffer() = base16_chunk("1234567812345678"); - BOOST_REQUIRE(instance.restore()); - BOOST_REQUIRE(body_store.buffer().empty()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(arraymap__slab_body_count__non_empty_restore__truncates) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - head_store.buffer() = base16_chunk("0300000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - body_store.buffer() = base16_chunk("1234567812345678"); - BOOST_REQUIRE(instance.restore()); - BOOST_REQUIRE_EQUAL(body_store.buffer(), base16_chunk("123456")); - BOOST_REQUIRE(!instance.get_fault()); -} - -////std::cout << head_file << std::endl << std::endl; -////std::cout << body_file << std::endl << std::endl; - BOOST_AUTO_TEST_SUITE_END() diff --git a/test/primitives/hashmap.cpp b/test/primitives/hashmap.cpp index 591eac5a4..2635115f1 100644 --- a/test/primitives/hashmap.cpp +++ b/test/primitives/hashmap.cpp @@ -28,47 +28,6 @@ class hashmap_ public: using base = hashmap; using hashmap::hashmap; - ////using reader_ptr = std::shared_ptr; - ////using finalizer_ptr = std::shared_ptr; - //// - ////reader_ptr getter_(const Key& key) const NOEXCEPT - ////{ - //// return getter_(base::it(key).self()); - ////} - //// - ////reader_ptr getter_(const Link& link) const NOEXCEPT - ////{ - //// const auto ptr = manager_.get(link); - //// if (!ptr) - //// return {}; - //// - //// system::istream stream{ *ptr }; - //// const auto source = std::make_shared(stream); - //// if constexpr (!is_slab) { source->set_limit(Size); } - //// return source; - ////} - //// - ////finalizer_ptr creater_(const Key& key, const Link& size=bc::one) NOEXCEPT - ////{ - //// using namespace system; - //// const auto link = allocate(size); - //// const auto ptr = manager_.get(link); - //// if (!ptr) - //// return {}; - //// - //// iostream stream{ *ptr }; - //// const auto sink = std::make_shared(stream); - //// sink->skip_bytes(Link::size); - //// sink->write_bytes(key); - //// sink->set_finalizer([this, link, index = head_.index(key), ptr]() NOEXCEPT - //// { - //// auto& next = unsafe_array_cast(ptr->begin()); - //// return head_.push(link, next, index); - //// }); - //// - //// if constexpr (!is_slab) { sink->set_limit(Size * size); } - //// return sink; - ////} }; using namespace system; @@ -115,38 +74,6 @@ BOOST_AUTO_TEST_CASE(hashmap__record_construct__non_empty__expected) BOOST_REQUIRE(!instance.get_fault()); } -BOOST_AUTO_TEST_CASE(hashmap__record_getter__terminal__false) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - record_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - ////BOOST_REQUIRE(!instance.getter_(link5::terminal)); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(hashmap__record_getter__empty__exhausted) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - record_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - ////BOOST_REQUIRE(instance.getter_(0)->is_exhausted()); - ////BOOST_REQUIRE(instance.getter_(19)->is_exhausted()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(hashmap__record_getter__empty__false) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - record_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - ////BOOST_REQUIRE(!instance.getter_(key10{ 0x00 })); - ////BOOST_REQUIRE(!instance.getter_(key10{ 0x42 })); - BOOST_REQUIRE(!instance.get_fault()); -} - // slab hashmap // ---------------------------------------------------------------------------- @@ -171,38 +98,6 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_construct__non_empty__expected_enabled) BOOST_REQUIRE(!instance.get_fault()); } -BOOST_AUTO_TEST_CASE(hashmap__slab_getter__terminal__false) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - ////BOOST_REQUIRE(!instance.getter_(link5::terminal)); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(hashmap__slab_getter__empty__exhausted) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - ////BOOST_REQUIRE(instance.getter_(0)->is_exhausted()); - ////BOOST_REQUIRE(instance.getter_(19)->is_exhausted()); - BOOST_REQUIRE(!instance.get_fault()); -} - -BOOST_AUTO_TEST_CASE(hashmap__slab_getter__empty__false) -{ - test::chunk_storage head_store{}; - test::chunk_storage body_store{}; - slab_table instance{ head_store, body_store, buckets }; - BOOST_REQUIRE(instance.create()); - ////BOOST_REQUIRE(!instance.getter_(key10{ 0x00 })); - ////BOOST_REQUIRE(!instance.getter_(key10{ 0x42 })); - BOOST_REQUIRE(!instance.get_fault()); -} - BOOST_AUTO_TEST_CASE(hashmap__enabled__non_empty_slab_zero_buckets__false) { constexpr auto body_size = 12345u; @@ -232,188 +127,6 @@ BOOST_AUTO_TEST_CASE(hashmap__enabled__empty_slab_nonzero_buckets__true) BOOST_REQUIRE(!instance.get_fault()); } -// push/found/at (protected interface positive tests) -// ---------------------------------------------------------------------------- - -////BOOST_AUTO_TEST_CASE(hashmap__record_readers__empty__expected) -////{ -//// test::chunk_storage head_store{}; -//// test::chunk_storage body_store{}; -//// record_table instance{ head_store, body_store, buckets }; -//// BOOST_REQUIRE(instance.create()); -//// -//// constexpr key10 key0{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a }; -//// auto stream0 = instance.creater_(key0); -//// BOOST_REQUIRE_EQUAL(body_store.buffer().size(), element_size); -//// BOOST_REQUIRE(!stream0->is_exhausted()); -//// BOOST_REQUIRE(!instance.getter_(key0)); -//// BOOST_REQUIRE(stream0->finalize()); -//// BOOST_REQUIRE(instance.getter_(key0)); -//// stream0.reset(); -//// -//// constexpr key10 key1{ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a }; -//// auto stream1 = instance.creater_(key1); -//// BOOST_REQUIRE_EQUAL(body_store.buffer().size(), 2u * element_size); -//// BOOST_REQUIRE(!stream1->is_exhausted()); -//// BOOST_REQUIRE(!instance.getter_(key1)); -//// BOOST_REQUIRE(stream1->finalize()); -//// BOOST_REQUIRE(instance.getter_(key1)); -//// stream1.reset(); -//// -//// // Past end is valid pointer but exhausted stream. -//// BOOST_REQUIRE(instance.getter_(0)); -//// BOOST_REQUIRE(!instance.getter_(0)->is_exhausted()); -//// BOOST_REQUIRE(instance.getter_(1)); -//// BOOST_REQUIRE(!instance.getter_(1)->is_exhausted()); -//// BOOST_REQUIRE(instance.getter_(2)); -//// BOOST_REQUIRE(instance.getter_(2)->is_exhausted()); -//// -//// // record -//// // 0000000000 [body logical size] -//// // --------------------------------- -//// // 0000000000 [0] [0->0] -//// // ffffffffff [1] -//// // ffffffffff [2] -//// // ffffffffff [3] -//// // 0100000000 [4] [4->1] -//// // ffffffffff [5] -//// // ffffffffff [6] -//// // ffffffffff [7] -//// // ffffffffff [8] -//// // ffffffffff [9] -//// // ffffffffff [10] -//// // ffffffffff [11] -//// // ffffffffff [12] -//// // ffffffffff [13] -//// // ffffffffff [14] -//// // ffffffffff [15] -//// // ffffffffff [16] -//// // ffffffffff [17] -//// // ffffffffff [18] -//// // ffffffffff [19] -//// // ================================= -//// // ffffffffff [0] [terminator] -//// // 0102030405060708090a [key] -//// // 00000000 [data] -//// // ffffffffff [1] [terminator] -//// // 1112131415161718191a [key] -//// // 00000000 [data] -////} -//// -////BOOST_AUTO_TEST_CASE(hashmap__slab_creater_getter__empty__true) -////{ -//// test::chunk_storage head_store{}; -//// test::chunk_storage body_store{}; -//// slab_table instance{ head_store, body_store, buckets }; -//// BOOST_REQUIRE(instance.create()); -//// -//// constexpr key10 key0{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a }; -//// BOOST_REQUIRE(!instance.getter_(key0)); -//// BOOST_REQUIRE(instance.creater_(key0, element_size)->finalize()); -//// BOOST_REQUIRE_EQUAL(body_store.buffer().size(), element_size); -//// BOOST_REQUIRE(instance.getter_(key0)); -//// -//// constexpr key10 key1{ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a }; -//// BOOST_REQUIRE(!instance.getter_(key1)); -//// BOOST_REQUIRE(instance.creater_(key1, element_size)->finalize()); -//// BOOST_REQUIRE_EQUAL(body_store.buffer().size(), 2u * element_size); -//// BOOST_REQUIRE(instance.getter_(key1)); -//// -//// // Past end is valid pointer but exhausted stream. -//// BOOST_REQUIRE(instance.getter_(0)); -//// BOOST_REQUIRE(!instance.getter_(0)->is_exhausted()); -//// BOOST_REQUIRE(instance.getter_(element_size)); -//// BOOST_REQUIRE(!instance.getter_(element_size)->is_exhausted()); -//// BOOST_REQUIRE(instance.getter_(2u * element_size)); -//// BOOST_REQUIRE(instance.getter_(2u * element_size)->is_exhausted()); -//// -//// // slab (same extents as record above) -//// // 0000000000 [body logical size] -//// // --------------------------------- -//// // 0000000000 [0->0x00] -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // 1300000000 [4->0x13] -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ================================= -//// // ffffffffff [0x00] [terminator] -//// // 0102030405060708090a [key] -//// // 00000000 [data] -//// // ffffffffff [0x13] [terminator] -//// // 1112131415161718191a [key] -//// // 00000000 [data] -////} -//// -////BOOST_AUTO_TEST_CASE(hashmap__record_creater_duplicate_key__getter__true) -////{ -//// test::chunk_storage head_store{}; -//// test::chunk_storage body_store{}; -//// record_table instance{ head_store, body_store, buckets }; -//// BOOST_REQUIRE(instance.create()); -//// -//// constexpr key10 key0{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -//// BOOST_REQUIRE(instance.creater_(key0)->finalize()); -//// BOOST_REQUIRE_EQUAL(body_store.buffer().size(), element_size); -//// BOOST_REQUIRE(instance.getter_(key0)); -//// -//// constexpr key10 key1{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a }; -//// BOOST_REQUIRE(instance.creater_(key1)->finalize()); -//// BOOST_REQUIRE_EQUAL(body_store.buffer().size(), 2u * element_size); -//// BOOST_REQUIRE(instance.getter_(key1)); -//// -//// BOOST_REQUIRE(instance.creater_(key1)->finalize()); -//// BOOST_REQUIRE_EQUAL(body_store.buffer().size(), 3u * element_size); -//// BOOST_REQUIRE(instance.getter_(key1)); -//// -//// // 0000000000 [body logical size] -//// // --------------------------------- -//// // 0200000000 [0->2] -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // 0000000000 [9->0] -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ffffffffff -//// // ================================= -//// // ffffffffff [0] [terminator] -//// // 00000000000000000000 [key] -//// // 00000000 [data] -//// // ffffffffff [1] [terminator] -//// // 0102030405060708090a [key] -//// // 00000000 [data] -//// // 0100000000 [2->1] [next] -//// // 0102030405060708090a [key] -//// // 00000000 [data] -////} - // get/put // ---------------------------------------------------------------------------- @@ -530,7 +243,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_put__multiple__expected) BOOST_REQUIRE(instance.get(1, record2)); BOOST_REQUIRE_EQUAL(record2.value, 0xa1b2c3d4_u32); - // This expecatation relies on the fact of no hash table conflict between 0x41 and 0x42. + // This expectation relies on the fact of no hash table conflict between 0x41 and 0x42. const data_chunk expected_file { 0xff, 0xff, 0xff, 0xff, 0xff, @@ -621,7 +334,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_put__multiple__expected) BOOST_REQUIRE(instance.get(big_slab::count(), slab2)); BOOST_REQUIRE_EQUAL(slab2.value, 0xa1b2c3d4_u32); - // This expecatation relies on the fact of no hash table conflict between 0x41 and 0x42. + // This expectation relies on the fact of no hash table conflict between 0x41 and 0x42. const data_chunk expected_file { 0xff, 0xff, 0xff, 0xff, 0xff, @@ -673,7 +386,7 @@ BOOST_AUTO_TEST_CASE(hashmap__record_get__excess__false) BOOST_REQUIRE(!instance.get_fault()); } -BOOST_AUTO_TEST_CASE(hashmap__record_get_key__excess__expected) +BOOST_AUTO_TEST_CASE(hashmap__record_get_key__exists__expected) { test::chunk_storage head_store{}; test::chunk_storage body_store{}; @@ -760,7 +473,7 @@ BOOST_AUTO_TEST_CASE(hashmap__slab_get__excess__true) BOOST_REQUIRE(!instance.get_fault()); } -BOOST_AUTO_TEST_CASE(hashmap__slab_get_key__excess__expected) +BOOST_AUTO_TEST_CASE(hashmap__slab_get_key__exists__expected) { test::chunk_storage head_store{}; test::chunk_storage body_store{};