summaryrefslogtreecommitdiffstats
path: root/test/trie_map/test_main.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--test/trie_map/test_main.cpp1909
1 files changed, 1909 insertions, 0 deletions
diff --git a/test/trie_map/test_main.cpp b/test/trie_map/test_main.cpp
new file mode 100644
index 0000000..3b7aa19
--- /dev/null
+++ b/test/trie_map/test_main.cpp
@@ -0,0 +1,1909 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * Copyright (c) 2015-2018 Kohei Yoshida
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ************************************************************************/
+
+#define MDDS_TRIE_MAP_DEBUG 1
+#include "test_global.hpp" // This must be the first header to be included.
+//#define MDDS_TRIE_MAP_DEBUG_DUMP_TRIE 1
+//#define MDDS_TRIE_MAP_DEBUG_DUMP_PACKED 1
+
+#include "mdds/trie_map.hpp"
+#include "mdds/global.hpp"
+
+#include <iterator>
+#include <fstream>
+#include <vector>
+#include <list>
+
+using namespace std;
+using namespace mdds;
+
+using packed_int_map_type = packed_trie_map<trie::std_string_traits, int>;
+using packed_str_map_type = packed_trie_map<trie::std_string_traits, std::string>;
+
+bool verify_entries(const packed_int_map_type& db, const packed_int_map_type::entry* entries, size_t entry_size)
+{
+ auto results = db.prefix_search(nullptr, 0);
+ for (auto it = results.begin(), ite = results.end(); it != ite; ++it)
+ cout << it->first << ": " << it->second << endl;
+
+ const packed_int_map_type::entry* p = entries;
+ const packed_int_map_type::entry* p_end = p + entry_size;
+ for (; p != p_end; ++p)
+ {
+ auto it = db.find(p->key, p->keylen);
+ if (it == db.end() || it->second != p->value)
+ return false;
+ }
+
+ return true;
+}
+
+template<typename T>
+bool check_equal(const T& left, const T& right)
+{
+ if (left.first != right.first)
+ {
+ cout << "left: " << left.first << "; right: " << right.first << endl;
+ return false;
+ }
+
+ if (left.second != right.second)
+ {
+ cout << "left: " << left.second << "; right: " << right.second << endl;
+ return false;
+ }
+
+ return true;
+}
+
+void trie_packed_test1()
+{
+ stack_printer __stack_printer__("::trie_packed_test1");
+
+ packed_int_map_type::entry entries[] = {
+ {MDDS_ASCII("a"), 13},
+ {MDDS_ASCII("aa"), 10},
+ {MDDS_ASCII("ab"), 3},
+ {MDDS_ASCII("b"), 7},
+ };
+
+ size_t entry_size = std::size(entries);
+ packed_int_map_type db(entries, entry_size);
+ assert(db.size() == 4);
+ assert(verify_entries(db, entries, entry_size));
+
+ // invalid keys
+ assert(db.find(MDDS_ASCII("ac")) == db.end());
+ assert(db.find(MDDS_ASCII("c")) == db.end());
+
+ {
+ // Get all key-value pairs.
+ auto results = db.prefix_search(nullptr, 0);
+ size_t n = std::distance(results.begin(), results.end());
+ assert(n == 4);
+ auto it = results.begin();
+ assert(it->first == "a");
+ assert(it->second == 13);
+ ++it;
+ assert(it->first == "aa");
+ assert(it->second == 10);
+ ++it;
+ assert(it->first == "ab");
+ assert(it->second == 3);
+ ++it;
+ assert(it->first == "b");
+ assert(it->second == 7);
+ ++it;
+ assert(it == results.end());
+ }
+
+ {
+ auto it = db.find(MDDS_ASCII("a"));
+ assert(it->first == "a");
+ assert(it->second == 13);
+ ++it;
+ assert(it->first == "aa");
+ assert(it->second == 10);
+ ++it;
+ assert(it->first == "ab");
+ assert(it->second == 3);
+ ++it;
+ assert(it->first == "b");
+ assert(it->second == 7);
+ ++it;
+ assert(it == db.end());
+ }
+}
+
+void trie_packed_test2()
+{
+ stack_printer __stack_printer__("::trie_packed_test2");
+
+ packed_int_map_type::entry entries[] = {
+ {MDDS_ASCII("aaron"), 0}, {MDDS_ASCII("al"), 1}, {MDDS_ASCII("aldi"), 2}, {MDDS_ASCII("andy"), 3},
+ {MDDS_ASCII("bison"), 4}, {MDDS_ASCII("bruce"), 5}, {MDDS_ASCII("charlie"), 6}, {MDDS_ASCII("charlotte"), 7},
+ {MDDS_ASCII("david"), 8}, {MDDS_ASCII("dove"), 9}, {MDDS_ASCII("e"), 10}, {MDDS_ASCII("eva"), 11},
+ };
+
+ size_t entry_size = std::size(entries);
+ packed_int_map_type db(entries, entry_size);
+ assert(db.size() == 12);
+ assert(verify_entries(db, entries, entry_size));
+
+ // invalid keys
+ assert(db.find(MDDS_ASCII("aarons")) == db.end());
+ assert(db.find(MDDS_ASCII("a")) == db.end());
+ assert(db.find(MDDS_ASCII("biso")) == db.end());
+ assert(db.find(MDDS_ASCII("dAvid")) == db.end());
+}
+
+void trie_packed_test3()
+{
+ stack_printer __stack_printer__("::trie_packed_test3");
+
+ packed_int_map_type::entry entries[] = {
+ {MDDS_ASCII("NULL"), 1},
+ {MDDS_ASCII("Null"), 2},
+ {MDDS_ASCII("null"), 3},
+ {MDDS_ASCII("~"), 4},
+ };
+
+ size_t entry_size = std::size(entries);
+ packed_int_map_type db(entries, entry_size);
+ assert(db.size() == 4);
+ assert(verify_entries(db, entries, entry_size));
+
+ // invalid keys
+ assert(db.find(MDDS_ASCII("NUll")) == db.end());
+ assert(db.find(MDDS_ASCII("Oull")) == db.end());
+ assert(db.find(MDDS_ASCII("Mull")) == db.end());
+ assert(db.find(MDDS_ASCII("hell")) == db.end());
+}
+
+void trie_packed_test4()
+{
+ stack_printer __stack_printer__("::trie_packed_test4");
+
+ enum name_type
+ {
+ name_none = 0,
+ name_andy,
+ name_bruce,
+ name_charlie,
+ name_david
+ };
+
+ packed_int_map_type::entry entries[] = {
+ {MDDS_ASCII("andy"), name_andy}, {MDDS_ASCII("andy1"), name_andy}, {MDDS_ASCII("andy13"), name_andy},
+ {MDDS_ASCII("bruce"), name_bruce}, {MDDS_ASCII("charlie"), name_charlie}, {MDDS_ASCII("david"), name_david},
+ };
+
+ size_t entry_size = std::size(entries);
+ packed_int_map_type db(entries, entry_size);
+ assert(db.size() == 6);
+ assert(verify_entries(db, entries, entry_size));
+
+ // Try invalid keys.
+ assert(db.find("foo", 3) == db.end());
+ assert(db.find("andy133", 7) == db.end());
+
+ // Test prefix search on 'andy'.
+ auto results = db.prefix_search(MDDS_ASCII("andy"));
+ size_t n = std::distance(results.begin(), results.end());
+ assert(n == 3);
+ auto it = results.begin();
+ assert(it->first == "andy");
+ ++it;
+ assert(it->first == "andy1");
+ ++it;
+ assert(it->first == "andy13");
+ ++it;
+ assert(it == results.end());
+
+ results = db.prefix_search(MDDS_ASCII("andy's toy"));
+ n = std::distance(results.begin(), results.end());
+ assert(n == 0);
+
+ results = db.prefix_search(MDDS_ASCII("e"));
+ n = std::distance(results.begin(), results.end());
+ assert(n == 0);
+
+ results = db.prefix_search(MDDS_ASCII("b"));
+ n = std::distance(results.begin(), results.end());
+ assert(n == 1);
+ it = results.begin();
+ assert(it->first == "bruce");
+ assert(it->second == name_bruce);
+ ++it;
+ assert(it == results.end());
+}
+
+struct value_wrapper
+{
+ int value;
+
+ value_wrapper() : value(0)
+ {}
+ value_wrapper(int _value) : value(_value)
+ {}
+};
+
+std::ostream& operator<<(std::ostream& os, const value_wrapper& vw)
+{
+ os << vw.value;
+ return os;
+}
+
+typedef packed_trie_map<trie::std_string_traits, value_wrapper> packed_value_map_type;
+
+void trie_packed_test_value_life_cycle()
+{
+ stack_printer __stack_printer__("::trie_packed_test_value_life_cycle");
+
+ using entry = packed_value_map_type::entry;
+
+ // Entries must be sorted by the key!
+ std::unique_ptr<vector<entry>> entries(new vector<entry>);
+ entries->push_back(entry(MDDS_ASCII("fifteen"), value_wrapper(15)));
+ entries->push_back(entry(MDDS_ASCII("ten"), value_wrapper(10)));
+ entries->push_back(entry(MDDS_ASCII("twelve"), value_wrapper(12)));
+ entries->push_back(entry(MDDS_ASCII("two"), value_wrapper(2)));
+
+ packed_value_map_type db(entries->data(), entries->size());
+
+ // Delete the original entry store.
+ entries.reset();
+
+ auto results = db.prefix_search(nullptr, 0);
+ std::for_each(results.begin(), results.end(), [](const packed_value_map_type::const_iterator::value_type& v) {
+ cout << v.first << ": " << v.second.value << endl;
+ });
+
+ auto it = db.find(MDDS_ASCII("twelve"));
+ assert(it->second.value == 12);
+
+ it = db.find(MDDS_ASCII("two"));
+ assert(it->second.value == 2);
+
+ it = db.find(MDDS_ASCII("foo"));
+ assert(it == db.end());
+}
+
+struct custom_string
+{
+ std::string data;
+
+ custom_string()
+ {}
+ custom_string(const std::string& _data) : data(_data)
+ {}
+};
+
+struct custom_string_trait
+{
+ typedef uint16_t key_unit_type;
+ typedef custom_string key_type;
+ typedef std::vector<key_unit_type> key_buffer_type;
+
+ static key_buffer_type to_key_buffer(const key_unit_type* str, size_t length)
+ {
+ key_buffer_type buf;
+ const key_unit_type* str_end = str + length;
+ for (; str != str_end; ++str)
+ buf.push_back(*str);
+
+ return buf;
+ }
+
+ static void push_back(key_buffer_type& buffer, key_unit_type c)
+ {
+ buffer.push_back(c);
+ }
+
+ static void pop_back(key_buffer_type& buffer)
+ {
+ buffer.pop_back();
+ }
+
+ static key_type to_key(const key_buffer_type& buf)
+ {
+ // Cast all uint16_t chars to regular chars.
+ key_type s;
+
+ std::for_each(buf.begin(), buf.end(), [&](key_unit_type c) { s.data.push_back(static_cast<char>(c)); });
+ return s;
+ }
+};
+
+typedef packed_trie_map<custom_string_trait, std::string> packed_custom_str_map_type;
+
+void trie_packed_test_custom_string()
+{
+ stack_printer __stack_printer__("::trie_packed_test_custom_string");
+
+ const uint16_t key_alex[] = {0x41, 0x6C, 0x65, 0x78};
+ const uint16_t key_bob[] = {0x42, 0x6F, 0x62};
+ const uint16_t key_max[] = {0x4D, 0x61, 0x78};
+ const uint16_t key_ming[] = {0x4D, 0x69, 0x6E, 0x67};
+
+ const packed_custom_str_map_type::entry entries[] = {
+ {key_alex, 4, "Alex"},
+ {key_bob, 3, "Bob"},
+ {key_max, 3, "Max"},
+ {key_ming, 4, "Ming"},
+ };
+
+ size_t n_entries = std::size(entries);
+ packed_custom_str_map_type db(entries, n_entries);
+ for (size_t i = 0; i < n_entries; ++i)
+ {
+ auto it = db.find(entries[i].key, entries[i].keylen);
+ cout << it->second << endl;
+ assert(it->second == entries[i].value);
+ }
+
+ // Find all keys that start with 'M'.
+ auto results = db.prefix_search(key_max, 1);
+ size_t n = std::distance(results.begin(), results.end());
+ assert(n == 2);
+ auto it = results.begin();
+ assert(it->first.data == it->second);
+ assert(it->second == "Max");
+ ++it;
+ assert(it->first.data == it->second);
+ assert(it->second == "Ming");
+ ++it;
+ assert(it == results.end());
+}
+
+void trie_packed_test_iterator_empty()
+{
+ stack_printer __stack_printer__("::trie_packed_test_iterator_empty");
+ packed_int_map_type db(nullptr, 0);
+
+ // empty container
+ packed_int_map_type::const_iterator it = db.begin();
+ packed_int_map_type::const_iterator ite = db.end();
+
+ assert(it == ite);
+}
+
+void trie_packed_test_iterator()
+{
+ stack_printer __stack_printer__("::trie_packed_test_iterator");
+
+ using trie_map_type = trie_map<trie::std_string_traits, int>;
+ using packed_type = trie_map_type::packed_type;
+ using kv = packed_type::key_value_type;
+
+ trie_map_type db;
+
+ db.insert(MDDS_ASCII("a"), 1);
+ packed_type packed = db.pack();
+ assert(db.size() == packed.size());
+ packed_type::const_iterator it = packed.begin();
+ packed_type::const_iterator ite = packed.end();
+ assert(it != ite);
+ assert(it->first == "a");
+ assert(it->second == 1);
+
+ db.insert(MDDS_ASCII("ab"), 2);
+ packed = db.pack(); // this invalidates the end position.
+ assert(db.size() == packed.size());
+
+ it = packed.begin();
+ ite = packed.end();
+ assert(it != ite);
+ assert(it->first == "a");
+ assert(it->second == 1);
+
+ ++it;
+ bool check_true = check_equal(*it++, kv("ab", 2));
+ assert(check_true);
+ assert(it == ite);
+
+ db.insert(MDDS_ASCII("aba"), 3);
+ db.insert(MDDS_ASCII("abb"), 4);
+ db.insert(MDDS_ASCII("abc"), 5);
+ db.insert(MDDS_ASCII("bc"), 6);
+ db.insert(MDDS_ASCII("bcd"), 7);
+
+ packed = db.pack();
+ assert(db.size() == packed.size());
+
+ it = packed.begin();
+ ite = packed.end();
+
+ assert(*it == kv("a", 1));
+ assert(check_equal(*(++it), kv("ab", 2)));
+ assert(check_equal(*(++it), kv("aba", 3)));
+ assert(check_equal(*(++it), kv("abb", 4)));
+ assert(check_equal(*(++it), kv("abc", 5)));
+ assert(check_equal(*(++it), kv("bc", 6)));
+ assert(check_equal(*(++it), kv("bcd", 7)));
+ assert(it->first == "bcd");
+ assert(it->second == 7);
+ ++it;
+ assert(it == ite);
+
+ --it;
+ assert(it != ite);
+ assert(check_equal(*it, kv("bcd", 7)));
+ --it;
+ assert(check_equal(*it, kv("bc", 6)));
+ --it;
+ assert(check_equal(*it, kv("abc", 5)));
+ --it;
+ assert(check_equal(*it, kv("abb", 4)));
+ --it;
+ assert(check_equal(*it, kv("aba", 3)));
+ --it;
+ assert(check_equal(*it, kv("ab", 2)));
+ assert(check_equal(*(--it), kv("a", 1)));
+ assert(it == packed.begin());
+
+ assert(check_equal(*(++it), kv("ab", 2)));
+ assert(check_equal(*(++it), kv("aba", 3)));
+ --it;
+ assert(check_equal(*it, kv("ab", 2)));
+ --it;
+ assert(check_equal(*it, kv("a", 1)));
+ ++it;
+ assert(check_equal(*it, kv("ab", 2)));
+ ++it;
+ assert(check_equal(*it, kv("aba", 3)));
+
+ // Post-decrement operator.
+ assert(check_equal(*it--, kv("aba", 3)));
+ assert(check_equal(*it, kv("ab", 2)));
+}
+
+void trie_packed_test_prefix_search1()
+{
+ stack_printer __stack_printer__("::trie_packed_test_prefix_search1");
+
+ using trie_map_type = trie_map<trie::std_string_traits, int>;
+ using packed_type = trie_map_type::packed_type;
+
+ trie_map_type db;
+ db.insert(MDDS_ASCII("andy"), 1);
+ db.insert(MDDS_ASCII("andy1"), 2);
+ db.insert(MDDS_ASCII("andy12"), 3);
+
+ {
+ auto results = db.prefix_search(MDDS_ASCII("andy"));
+ auto it = results.begin();
+ assert(it != results.end());
+ assert(it->first == "andy");
+ ++it;
+ assert(it->first == "andy1");
+ ++it;
+ assert(it->first == "andy12");
+ ++it;
+ assert(it == results.end());
+
+ size_t n = std::distance(results.begin(), results.end());
+ assert(n == 3);
+ }
+
+ packed_type packed = db.pack();
+ {
+ auto results = packed.prefix_search(MDDS_ASCII("andy"));
+ auto it = results.begin();
+ assert(it != results.end());
+ assert(it->first == "andy");
+ ++it;
+ assert(it->first == "andy1");
+ ++it;
+ assert(it->first == "andy12");
+ ++it;
+ assert(it == results.end());
+
+ size_t n = std::distance(results.begin(), results.end());
+ assert(n == 3);
+ }
+}
+
+void trie_packed_test_key_as_input()
+{
+ stack_printer __stack_printer__("::trie_packed_test_key_as_input");
+
+ typedef trie_map<trie::std_string_traits, int> trie_map_type;
+ trie_map_type db;
+
+ db.insert(std::string("string as key"), 1);
+ db.insert("literal as key", 2);
+ auto packed = db.pack();
+
+ auto it = packed.find("literal as key");
+ assert(it != packed.end());
+ assert(it->first == "literal as key");
+ assert(it->second == 2);
+
+ auto results = packed.prefix_search("str");
+ auto rit = results.begin();
+ assert(rit != results.end());
+ assert(rit->first == "string as key");
+ assert(rit->second == 1);
+ ++rit;
+ assert(rit == results.end());
+}
+
+void trie_packed_test_copying()
+{
+ stack_printer __stack_printer__("::trie_packed_test_copying");
+ using map_type = packed_trie_map<trie::std_string_traits, int>;
+
+ map_type::entry entries[] = {
+ {MDDS_ASCII("aaron"), 0}, {MDDS_ASCII("al"), 1}, {MDDS_ASCII("aldi"), 2}, {MDDS_ASCII("andy"), 3},
+ {MDDS_ASCII("bison"), 4}, {MDDS_ASCII("bruce"), 5}, {MDDS_ASCII("charlie"), 6}, {MDDS_ASCII("charlotte"), 7},
+ {MDDS_ASCII("david"), 8}, {MDDS_ASCII("dove"), 9}, {MDDS_ASCII("e"), 10}, {MDDS_ASCII("eva"), 11},
+ };
+
+ auto verify_content = [&entries](const map_type& db) {
+ auto it = db.begin();
+ const map_type::entry* p_entries = entries;
+ const map_type::entry* p_entries_end = p_entries + db.size();
+ size_t n = std::distance(p_entries, p_entries_end);
+ assert(db.size() == n);
+ assert(!db.empty());
+
+ for (; p_entries != p_entries_end; ++p_entries, ++it)
+ {
+ std::string key_expected(p_entries->key, p_entries->keylen);
+ assert(key_expected == it->first);
+ assert(p_entries->value == it->second);
+ }
+ };
+
+ auto db = std::make_unique<map_type>(entries, std::size(entries));
+ auto db_copied(*db);
+ assert(*db == db_copied);
+ assert(db->size() == db_copied.size());
+ db.reset();
+
+ auto it = db_copied.find("charlie");
+ assert(it != db_copied.end());
+ assert(it->first == "charlie");
+ assert(it->second == 6);
+
+ verify_content(db_copied);
+
+ auto db_moved(std::move(db_copied));
+ assert(db_copied.empty());
+ assert(!db_moved.empty());
+ assert(db_moved.size() == std::size(entries));
+
+ it = db_copied.find("bison");
+ assert(it == db_copied.end());
+ it = db_moved.find("bison");
+ assert(it != db_moved.end());
+ assert(it->first == "bison");
+ assert(it->second == 4);
+
+ verify_content(db_moved);
+
+ map_type db_copy_assigned;
+ assert(db_copy_assigned.empty());
+ db_copy_assigned = db_moved;
+ assert(db_copy_assigned == db_moved);
+
+ verify_content(db_moved);
+ verify_content(db_copy_assigned);
+
+ map_type db_move_assigned;
+ assert(db_move_assigned.empty());
+ db_move_assigned = std::move(db_moved);
+ assert(db_move_assigned != db_moved);
+
+ verify_content(db_move_assigned);
+ assert(db_moved.empty());
+}
+
+void trie_packed_test_non_equal()
+{
+ stack_printer __stack_printer__("::trie_packed_test_non_equal");
+
+ using map_type = packed_trie_map<trie::std_string_traits, int>;
+
+ map_type::entry entries1[] = {
+ {MDDS_ASCII("aaron"), 0}, {MDDS_ASCII("al"), 1}, {MDDS_ASCII("aldi"), 2}, {MDDS_ASCII("andy"), 3},
+ {MDDS_ASCII("bison"), 4}, {MDDS_ASCII("bruce"), 5}, {MDDS_ASCII("charlie"), 6}, {MDDS_ASCII("charlotte"), 7},
+ {MDDS_ASCII("david"), 8}, {MDDS_ASCII("dove"), 9}, {MDDS_ASCII("e"), 10}, {MDDS_ASCII("eva"), 11},
+ };
+
+ map_type::entry entries2[] = {
+ {MDDS_ASCII("aaron"), 0}, {MDDS_ASCII("al"), 1}, {MDDS_ASCII("aldi"), 2},
+ {MDDS_ASCII("andy"), 3}, {MDDS_ASCII("bison"), 4}, {MDDS_ASCII("bruce"), 2}, // different value
+ {MDDS_ASCII("charlie"), 6}, {MDDS_ASCII("charlotte"), 7}, {MDDS_ASCII("david"), 8},
+ {MDDS_ASCII("dove"), 9}, {MDDS_ASCII("e"), 10}, {MDDS_ASCII("eva"), 11},
+ };
+
+ // fewer entries
+ map_type::entry entries3[] = {
+ {MDDS_ASCII("aaron"), 0}, {MDDS_ASCII("al"), 1}, {MDDS_ASCII("aldi"), 2}, {MDDS_ASCII("andy"), 3},
+ {MDDS_ASCII("bison"), 4}, {MDDS_ASCII("bruce"), 5}, {MDDS_ASCII("charlie"), 6}, {MDDS_ASCII("charlotte"), 7},
+ {MDDS_ASCII("david"), 8}, {MDDS_ASCII("dove"), 9}, {MDDS_ASCII("e"), 10},
+ };
+
+ map_type db1(entries1, std::size(entries1));
+ map_type db2(entries2, std::size(entries2));
+ map_type db3(entries3, std::size(entries3));
+ assert(db1 != db2);
+ assert(db1 != db3);
+ assert(db2 != db3);
+
+ map_type db4(entries1, std::size(entries1));
+ map_type db5(entries2, std::size(entries2));
+ map_type db6(entries3, std::size(entries3));
+
+ assert(db1 == db4);
+ assert(db2 == db5);
+ assert(db3 == db6);
+}
+
+namespace trie_packed_test_save_and_load_state {
+
+struct _custom_variable_value
+{
+ enum class v_type
+ {
+ unknown,
+ fp32,
+ int64
+ };
+
+ v_type type;
+
+ union
+ {
+ float fp32;
+ int64_t int64;
+ } value;
+
+ _custom_variable_value() : type(v_type::unknown)
+ {}
+
+ _custom_variable_value(float v) : type(v_type::fp32)
+ {
+ value.fp32 = v;
+ }
+
+ _custom_variable_value(int v) : type(v_type::int64)
+ {
+ value.int64 = v;
+ }
+
+ _custom_variable_value(const _custom_variable_value& other) : type(other.type)
+ {
+ switch (type)
+ {
+ case v_type::fp32:
+ value.fp32 = other.value.fp32;
+ break;
+ case v_type::int64:
+ value.int64 = other.value.int64;
+ break;
+ default:;
+ }
+ }
+
+ bool operator==(const _custom_variable_value& other) const
+ {
+ if (type != other.type)
+ return false;
+
+ switch (type)
+ {
+ case v_type::fp32:
+ return value.fp32 == other.value.fp32;
+ case v_type::int64:
+ return value.int64 == other.value.int64;
+ default:;
+ }
+
+ return true;
+ }
+
+ bool operator!=(const _custom_variable_value& other) const
+ {
+ return !operator==(other);
+ }
+};
+
+struct _custom_variable_serializer
+{
+ union bin_value
+ {
+ char buffer[8];
+ float fp32;
+ int64_t int64;
+ };
+
+ static constexpr bool variable_size = true;
+
+ static void write(std::ostream& os, const _custom_variable_value& v)
+ {
+ bin_value bv;
+
+ switch (v.type)
+ {
+ case _custom_variable_value::v_type::unknown:
+ {
+ char c = 0;
+ os.write(&c, 1);
+ break;
+ }
+ case _custom_variable_value::v_type::fp32:
+ {
+ char c = 1;
+ os.write(&c, 1);
+ bv.fp32 = v.value.fp32;
+ os.write(bv.buffer, 4);
+ break;
+ }
+ case _custom_variable_value::v_type::int64:
+ {
+ char c = 2;
+ os.write(&c, 1);
+ bv.int64 = v.value.int64;
+ os.write(bv.buffer, 8);
+ break;
+ }
+ }
+ }
+
+ static void read(std::istream& is, size_t n, _custom_variable_value& v)
+ {
+ assert(n > 0);
+ char c;
+ is.read(&c, 1);
+
+ switch (c)
+ {
+ case 0:
+ v.type = _custom_variable_value::v_type::unknown;
+ break;
+ case 1:
+ v.type = _custom_variable_value::v_type::fp32;
+ break;
+ case 2:
+ v.type = _custom_variable_value::v_type::int64;
+ break;
+ default:
+ assert(!"invalid value type");
+ }
+
+ n -= 1;
+ bin_value bv;
+
+ switch (v.type)
+ {
+ case _custom_variable_value::v_type::fp32:
+ assert(n == 4);
+ is.read(bv.buffer, 4);
+ v.value.fp32 = bv.fp32;
+ break;
+ case _custom_variable_value::v_type::int64:
+ assert(n == 8);
+ is.read(bv.buffer, 8);
+ v.value.int64 = bv.int64;
+ break;
+ case _custom_variable_value::v_type::unknown:
+ break;
+ default:
+ assert(!"invalid value type");
+ }
+ }
+};
+
+/**
+ * mock value struct containing one value string that only stores "zero",
+ * "one", "two" or "three". We use a custom serializer to store the value
+ * using only 1 byte each.
+ */
+struct _custom_fixed_value
+{
+ std::string value_string; // only stores "zero", "one", "two" or "three".
+
+ _custom_fixed_value()
+ {}
+
+ _custom_fixed_value(const char* p) : value_string(p, std::strlen(p))
+ {}
+
+ bool operator==(const _custom_fixed_value& other) const
+ {
+ return value_string == other.value_string;
+ }
+
+ bool operator!=(const _custom_fixed_value& other) const
+ {
+ return !operator==(other);
+ }
+};
+
+struct _custom_fixed_serializer
+{
+ static constexpr bool variable_size = false;
+ static constexpr size_t value_size = 1;
+
+ static void write(std::ostream& os, const _custom_fixed_value& v)
+ {
+ char bv = -1;
+
+ if (v.value_string == "zero")
+ bv = 0;
+ else if (v.value_string == "one")
+ bv = 1;
+ else if (v.value_string == "two")
+ bv = 2;
+ else if (v.value_string == "three")
+ bv = 3;
+
+ os.write(&bv, 1);
+ }
+
+ static void read(std::istream& is, size_t n, _custom_fixed_value& v)
+ {
+ assert(n == 1);
+ char bv = -1;
+ is.read(&bv, 1);
+
+ switch (bv)
+ {
+ case 0:
+ v.value_string = "zero";
+ break;
+ case 1:
+ v.value_string = "one";
+ break;
+ case 2:
+ v.value_string = "two";
+ break;
+ case 3:
+ v.value_string = "three";
+ break;
+ default:
+ v.value_string = "???";
+ }
+ }
+};
+
+void test1()
+{
+ stack_printer __stack_printer__("trie_packed_test_save_and_load_state::test1");
+ packed_int_map_type empty_db;
+
+ std::string saved_state;
+
+ {
+ std::ostringstream state;
+ empty_db.save_state(state);
+ saved_state = state.str();
+ }
+
+ packed_int_map_type restored;
+
+ {
+ std::istringstream state(saved_state);
+ restored.load_state(state);
+ }
+
+ assert(restored == empty_db);
+}
+
+void test2()
+{
+ stack_printer __stack_printer__("trie_packed_test_save_and_load_state::test2");
+ packed_int_map_type::entry entries[] = {
+ {MDDS_ASCII("bruce"), 5}, {MDDS_ASCII("charlie"), 6}, {MDDS_ASCII("charlotte"), 7},
+ {MDDS_ASCII("david"), 8}, {MDDS_ASCII("dove"), 9},
+ };
+
+ packed_int_map_type db(entries, std::size(entries));
+
+ std::string saved_state;
+
+ {
+ std::ostringstream state;
+ db.save_state(state);
+ saved_state = state.str();
+ }
+
+ packed_int_map_type restored;
+ assert(restored != db);
+
+ {
+ std::istringstream state(saved_state);
+ restored.load_state(state);
+ }
+
+ assert(restored == db);
+}
+
+void test3()
+{
+ stack_printer __stack_printer__("trie_packed_test_save_and_load_state::test3");
+ std::vector<packed_str_map_type::entry> entries = {
+ {MDDS_ASCII("Abby"), "ABBY"},
+ {MDDS_ASCII("Ashley"), "ASHLEY"},
+ {MDDS_ASCII("Candelaria"), "CANDELARIA"},
+ {MDDS_ASCII("Carita"), "CARITA"},
+ {MDDS_ASCII("Christal"), "CHRISTAL"},
+ {MDDS_ASCII("Cory"), "CORY"},
+ {MDDS_ASCII("Estrella"), "ESTRELLA"},
+ {MDDS_ASCII("Etha"), "ETHA"},
+ {MDDS_ASCII("Harley"), "HARLEY"},
+ {MDDS_ASCII("Irish"), "IRISH"},
+ {MDDS_ASCII("Kiara"), "KIARA"},
+ {MDDS_ASCII("Korey"), "KOREY"},
+ {MDDS_ASCII("Laurene"), "LAURENE"},
+ {MDDS_ASCII("Michiko"), "MICHIKO"},
+ {MDDS_ASCII("Miriam"), "MIRIAM"},
+ {MDDS_ASCII("Mitzi"), "MITZI"},
+ {MDDS_ASCII("Seth"), "SETH"},
+ {MDDS_ASCII("Sindy"), "SINDY"},
+ {MDDS_ASCII("Tawanna"), "TAWANNA"},
+ {MDDS_ASCII("Tyra"), "TYRA"},
+ };
+
+ packed_str_map_type db(entries.data(), entries.size());
+
+ // Run some search.
+ auto results = db.prefix_search("Mi");
+ auto it = results.begin();
+ assert(it != results.end());
+ assert(it->first == "Michiko");
+ assert(it->second == "MICHIKO");
+ ++it;
+ assert(it != results.end());
+ assert(it->first == "Miriam");
+ assert(it->second == "MIRIAM");
+ ++it;
+ assert(it != results.end());
+ assert(it->first == "Mitzi");
+ assert(it->second == "MITZI");
+ ++it;
+ assert(it == results.end());
+
+ std::string saved_state;
+
+ {
+ std::ostringstream state;
+ db.save_state(state);
+ saved_state = state.str();
+ }
+
+ packed_str_map_type restored;
+
+ {
+ std::istringstream state(saved_state);
+ restored.load_state(state);
+ }
+
+ assert(db == restored);
+}
+
+void test4()
+{
+ stack_printer __stack_printer__("trie_packed_test_save_and_load_state::test4");
+ using map_type = packed_trie_map<trie::std_string_traits, std::vector<int64_t>>;
+
+ std::vector<map_type::entry> entries = {
+ {MDDS_ASCII("Abby"), {65, 98, 98, 121}},
+ {MDDS_ASCII("Ashley"), {65, 115, 104, 108, 101, 121}},
+ {MDDS_ASCII("Christal"), {67, 104, 114, 105, 115, 116, 97, 108}},
+ {MDDS_ASCII("Cory"), {67, 111, 114, 121}},
+ {MDDS_ASCII("Harley"), {72, 97, 114, 108, 101, 121}},
+ {MDDS_ASCII("Kiara"), {75, 105, 97, 114, 97}},
+ {MDDS_ASCII("Mitzi"), {77, 105, 116, 122, 105}},
+ };
+
+ map_type db(entries.data(), entries.size());
+ assert(db.size() == entries.size());
+
+ std::string saved_state;
+ {
+ std::ostringstream state;
+ db.save_state(state);
+ saved_state = state.str();
+ }
+
+ map_type restored;
+
+ {
+ std::istringstream state(saved_state);
+ restored.load_state(state);
+ }
+
+ assert(db == restored);
+}
+
+void test5()
+{
+ stack_printer __stack_printer__("trie_packed_test_save_and_load_state::test5");
+ using map_type = packed_trie_map<trie::std_string_traits, float>;
+
+ std::vector<map_type::entry> entries = {
+ {MDDS_ASCII("Abby"), 1.0f}, {MDDS_ASCII("Ashley"), 1.1f}, {MDDS_ASCII("Christal"), 1.2f},
+ {MDDS_ASCII("Cory"), 1.3f}, {MDDS_ASCII("Harley"), 1.4f}, {MDDS_ASCII("Kiara"), 1.5f},
+ {MDDS_ASCII("Mitzi"), 1.6f},
+ };
+
+ map_type db(entries.data(), entries.size());
+ assert(db.size() == entries.size());
+
+ std::string saved_state;
+ {
+ std::ostringstream state;
+ db.save_state(state);
+ saved_state = state.str();
+ }
+
+ map_type restored;
+
+ {
+ std::istringstream state(saved_state);
+ restored.load_state(state);
+ }
+
+ assert(db == restored);
+}
+
+template<typename SeqT>
+void test6()
+{
+ stack_printer __stack_printer__("trie_packed_test_save_and_load_state::test6");
+ using map_type = packed_trie_map<trie::std_string_traits, SeqT>;
+
+ std::vector<typename map_type::entry> entries = {
+ {MDDS_ASCII("Abby"), {65.0, 98.1, 98.2, 121.3}},
+ {MDDS_ASCII("Ashley"), {65.0, 11.5, 1.04, 1.08, .101, .12586}},
+ {MDDS_ASCII("Christal"), {67.0, -10.4, -114.236}},
+ {MDDS_ASCII("Cory"), {67.0, 122.111}},
+ {MDDS_ASCII("Harley"), {72.0, 97.12, -1.114}},
+ {MDDS_ASCII("Kiara"), {75.0, 1.05, 9.7, 1.14, -97.5}},
+ {MDDS_ASCII("Mitzi"), {77.0, 10.5, 11.6, 1.22, 10.5}},
+ };
+
+ map_type db(entries.data(), entries.size());
+ assert(db.size() == entries.size());
+
+ std::string saved_state;
+ {
+ std::ostringstream state;
+ db.save_state(state);
+ saved_state = state.str();
+ }
+
+ map_type restored;
+
+ {
+ std::istringstream state(saved_state);
+ restored.load_state(state);
+ }
+
+ assert(db == restored);
+}
+
+void test7()
+{
+ stack_printer __stack_printer__("trie_packed_test_save_and_load_state::test7");
+ using map_type = packed_trie_map<trie::std_string_traits, _custom_variable_value>;
+
+ std::vector<map_type::entry> entries = {
+ {MDDS_ASCII("Alan"), 1.2f}, {MDDS_ASCII("Cory"), -125}, {MDDS_ASCII("Eleni"), 966},
+ {MDDS_ASCII("Evia"), -0.987f}, {MDDS_ASCII("Nathaniel"), 0}, {MDDS_ASCII("Rebbecca"), 1.234f},
+ {MDDS_ASCII("Rodrick"), 34253536}, {MDDS_ASCII("Stuart"), 12}, {MDDS_ASCII("Verline"), 56},
+ };
+
+ map_type db(entries.data(), entries.size());
+ assert(db.size() == entries.size());
+
+ std::string saved_state;
+ {
+ std::ostringstream state;
+ db.save_state<_custom_variable_serializer>(state);
+ saved_state = state.str();
+ }
+
+ map_type restored;
+
+ {
+ std::istringstream state(saved_state);
+ restored.load_state<_custom_variable_serializer>(state);
+ }
+
+ assert(db == restored);
+}
+
+void test8()
+{
+ stack_printer __stack_printer__("trie_packed_test_save_and_load_state::test8");
+ using map_type = packed_trie_map<trie::std_string_traits, _custom_fixed_value>;
+
+ std::vector<map_type::entry> entries = {
+ {MDDS_ASCII("Bernardine"), "zero"}, {MDDS_ASCII("Donny"), "two"}, {MDDS_ASCII("Julia"), "one"},
+ {MDDS_ASCII("Lindsy"), "three"}, {MDDS_ASCII("Martine"), "three"}, {MDDS_ASCII("Shana"), "two"},
+ {MDDS_ASCII("Sonia"), "zero"}, {MDDS_ASCII("Tracie"), "one"}, {MDDS_ASCII("Vanita"), "two"},
+ {MDDS_ASCII("Yung"), "zero"},
+ };
+
+ map_type db(entries.data(), entries.size());
+ assert(db.size() == entries.size());
+
+ std::string saved_state;
+ {
+ std::ostringstream state;
+ db.save_state<_custom_fixed_serializer>(state);
+ saved_state = state.str();
+ }
+
+ map_type restored;
+
+ {
+ std::istringstream state(saved_state);
+ restored.load_state<_custom_fixed_serializer>(state);
+ }
+
+ assert(db == restored);
+
+ // Run some query to make sure it is still functional.
+ auto it = restored.find("Tracie");
+ assert(it->first == "Tracie");
+ assert(it->second.value_string == "one");
+}
+
+void run()
+{
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6<std::vector<double>>();
+ test6<std::deque<double>>();
+ test6<std::list<double>>();
+ test7();
+ test8();
+}
+
+} // namespace trie_packed_test_save_and_load_state
+
+void trie_test1()
+{
+ stack_printer __stack_printer__("::trie_test1");
+
+ typedef trie_map<trie::std_string_traits, custom_string> trie_map_type;
+ typedef packed_trie_map<trie::std_string_traits, custom_string> packed_trie_map_type;
+
+ trie_map_type db;
+ const trie_map_type& dbc = db;
+
+ assert(db.size() == 0);
+ db.insert(MDDS_ASCII("Barak"), custom_string("Obama"));
+ assert(db.size() == 1);
+ db.insert(MDDS_ASCII("Bob"), custom_string("Marley"));
+ assert(db.size() == 2);
+ db.insert(MDDS_ASCII("Hideki"), custom_string("Matsui"));
+ assert(db.size() == 3);
+
+ auto it = dbc.find(MDDS_ASCII("Barak"));
+ assert(it->first == "Barak");
+ custom_string res = it->second;
+ assert(res.data == "Obama");
+
+ res = dbc.find(MDDS_ASCII("Bob"))->second;
+ assert(res.data == "Marley");
+ res = dbc.find(MDDS_ASCII("Hideki"))->second;
+ assert(res.data == "Matsui");
+
+ // Non-existent key.
+ it = dbc.find(MDDS_ASCII("Von"));
+ assert(it == dbc.end());
+ it = dbc.find(MDDS_ASCII("Bar"));
+ assert(it == dbc.end());
+
+ // Perform prefix search on "B", which should return both "Barak" and "Bob".
+ // The results should be sorted.
+ {
+ auto matches = dbc.prefix_search(MDDS_ASCII("B"));
+ size_t n = std::distance(matches.begin(), matches.end());
+ assert(n == 2);
+ auto it2 = matches.begin();
+ assert(it2->first == "Barak");
+ assert(it2->second.data == "Obama");
+ ++it2;
+ assert(it2->first == "Bob");
+ assert(it2->second.data == "Marley");
+
+ matches = dbc.prefix_search(MDDS_ASCII("Hi"));
+ n = std::distance(matches.begin(), matches.end());
+ assert(n == 1);
+ it2 = matches.begin();
+ assert(it2->first == "Hideki");
+ assert(it2->second.data == "Matsui");
+
+ // Invalid prefix searches.
+ matches = dbc.prefix_search(MDDS_ASCII("Bad"));
+ assert(matches.begin() == matches.end());
+ matches = dbc.prefix_search(MDDS_ASCII("Foo"));
+ assert(matches.begin() == matches.end());
+ }
+
+ {
+ // Create a packed version from it, and make sure it still generates the
+ // same results.
+ packed_trie_map_type packed(dbc);
+ assert(packed.size() == dbc.size());
+
+ {
+ auto results = packed.prefix_search(MDDS_ASCII("B"));
+ size_t n = std::distance(results.begin(), results.end());
+ assert(n == 2);
+ auto it2 = results.begin();
+ assert(it2->first == "Barak");
+ assert(it2->second.data == "Obama");
+ ++it2;
+ assert(it2->first == "Bob");
+ assert(it2->second.data == "Marley");
+ ++it2;
+ assert(it2 == results.end());
+ }
+
+ {
+ auto results = dbc.prefix_search(MDDS_ASCII("Hi"));
+ size_t n = std::distance(results.begin(), results.end());
+ assert(n == 1);
+ auto it2 = results.begin();
+ assert(it2->first == "Hideki");
+ assert(it2->second.data == "Matsui");
+ }
+
+ // Invalid prefix searches.
+ auto results = dbc.prefix_search(MDDS_ASCII("Bad"));
+ assert(results.begin() == results.end());
+ results = dbc.prefix_search(MDDS_ASCII("Foo"));
+ assert(results.begin() == results.end());
+ }
+
+ {
+ auto packed = dbc.pack();
+ auto results = packed.prefix_search(MDDS_ASCII("B"));
+ size_t n = std::distance(results.begin(), results.end());
+ assert(n == 2);
+ auto it2 = results.begin();
+ assert(it2->first == "Barak");
+ assert(it2->second.data == "Obama");
+ ++it2;
+ assert(it2->first == "Bob");
+ assert(it2->second.data == "Marley");
+ }
+
+ // Erase an existing key.
+ bool erased = db.erase(MDDS_ASCII("Hideki"));
+ assert(erased);
+ assert(db.size() == 2);
+
+ it = dbc.find(MDDS_ASCII("Hideki"));
+ assert(it == dbc.end());
+
+ // Try to erase a key that doesn't exist.
+ erased = db.erase(MDDS_ASCII("Foo"));
+ assert(!erased);
+ assert(db.size() == 2);
+
+ // Clear the whole thing.
+ db.clear();
+ assert(db.size() == 0);
+}
+
+void trie_test2()
+{
+ stack_printer __stack_printer__("::trie_test2");
+ using key_trait = trie::std_container_traits<std::vector<uint16_t>>;
+ using map_type = trie_map<key_trait, int>;
+ using key_type = map_type::key_type;
+
+ auto print_key = [](const std::vector<uint16_t>& key, const char* msg) {
+ cout << msg << ": ";
+ std::copy(key.begin(), key.end(), std::ostream_iterator<uint16_t>(std::cout, " "));
+ cout << endl;
+ };
+
+ map_type db;
+ key_type key = {2393, 99, 32589, 107, 0, 65535};
+ print_key(key, "original");
+ int value = 1;
+ db.insert(key, value);
+ assert(db.size() == 1);
+ {
+ auto it = db.begin();
+ assert(it != db.end());
+ assert(it->first == key);
+
+ print_key(it->first, "from trie_map");
+ }
+
+ auto packed = db.pack();
+ assert(packed.size() == 1);
+
+ {
+ auto it = packed.begin();
+ assert(it != packed.end());
+ print_key(it->first, "from packed_trie_map");
+ assert(it->first == key);
+ }
+}
+
+void trie_test_iterator_empty()
+{
+ stack_printer __stack_printer__("::trie_test_iterator_empty");
+ typedef trie_map<trie::std_string_traits, int> trie_map_type;
+ trie_map_type db;
+ const trie_map_type& dbc = db;
+
+ // empty container
+ trie_map_type::const_iterator it = dbc.begin();
+ trie_map_type::const_iterator ite = dbc.end();
+
+ assert(it == ite);
+ assert(db.begin() == dbc.begin()); // non-const vs const iterators
+ assert(dbc.end() == db.end()); // const vs non-const iterators
+}
+
+void trie_test_iterator()
+{
+ stack_printer __stack_printer__("::trie_test_iterator");
+ typedef trie_map<trie::std_string_traits, int> trie_map_type;
+ using kv = trie_map_type::key_value_type;
+ trie_map_type db;
+ const trie_map_type& dbc = db;
+
+ cout << "empty container" << endl;
+
+ // empty container
+ trie_map_type::const_iterator it = dbc.begin();
+ trie_map_type::const_iterator ite = dbc.end();
+
+ // The end iterator will never get invalidated since it only references
+ // the root node which will never get modified as long as the parent
+ // container is alive.
+
+ assert(it == ite);
+
+ cout << "one element" << endl;
+
+ db.insert(MDDS_ASCII("a"), 1);
+ it = dbc.begin();
+ assert(it != ite);
+ assert(*it == kv("a", 1));
+ ++it;
+ assert(it == ite);
+
+ cout << "two elements" << endl;
+
+ db.insert(MDDS_ASCII("ab"), 2);
+ it = dbc.begin();
+ assert(it != ite);
+ assert(*it == kv("a", 1));
+ ++it;
+ assert(it != ite);
+ assert(*it == kv("ab", 2));
+ ++it;
+ assert(it == ite);
+
+ cout << "more than two elements" << endl;
+
+ db.insert(MDDS_ASCII("aba"), 3);
+ db.insert(MDDS_ASCII("abb"), 4);
+ db.insert(MDDS_ASCII("abc"), 5);
+ db.insert(MDDS_ASCII("bc"), 6);
+ db.insert(MDDS_ASCII("bcd"), 7);
+
+ it = dbc.begin();
+ assert(*it == kv("a", 1));
+ ++it;
+ assert(*it == kv("ab", 2));
+ ++it;
+ assert(*it == kv("aba", 3));
+ ++it;
+ assert(*it == kv("abb", 4));
+ ++it;
+ assert(*it == kv("abc", 5));
+ ++it;
+ assert(*it == kv("bc", 6));
+ ++it;
+ assert(*it == kv("bcd", 7));
+ assert(it->first == "bcd");
+ assert(it->second == 7);
+ ++it;
+ assert(it == ite);
+
+ --it;
+ assert(it != ite);
+ assert(*it == kv("bcd", 7));
+ --it;
+ assert(*it == kv("bc", 6));
+ --it;
+ assert(*it == kv("abc", 5));
+ --it;
+ assert(*it == kv("abb", 4));
+ --it;
+ assert(*it == kv("aba", 3));
+ --it;
+ assert(*it == kv("ab", 2));
+ --it;
+ assert(*it == kv("a", 1));
+ assert(it == dbc.begin());
+ ++it;
+ assert(*it == kv("ab", 2));
+ ++it;
+ assert(*it == kv("aba", 3));
+ --it;
+ assert(*it == kv("ab", 2));
+ --it;
+ assert(*it == kv("a", 1));
+ ++it;
+ assert(*it == kv("ab", 2));
+ ++it;
+ assert(*it == kv("aba", 3));
+
+ assert(db.begin() != dbc.end()); // non-const vs const iterators
+ assert(dbc.begin() != db.end()); // const vs non-const iterators
+}
+
+void trie_test_iterator_with_erase()
+{
+ stack_printer __stack_printer__("::trie_test_iterator_with_erase");
+ typedef trie_map<trie::std_string_traits, int> trie_map_type;
+ using kv = trie_map_type::key_value_type;
+ trie_map_type db;
+ const trie_map_type& dbc = db;
+ bool check_true = false;
+
+ db.insert(MDDS_ASCII("Python"), 1);
+ db.insert(MDDS_ASCII("C++"), 2);
+
+ auto it = dbc.begin(), ite = dbc.end();
+ check_true = (*it++ == kv("C++", 2));
+ assert(check_true);
+ check_true = (*it++ == kv("Python", 1));
+ assert(check_true);
+ assert(it == ite);
+
+ db.erase(MDDS_ASCII("C++"));
+ it = dbc.begin();
+ check_true = (*it++ == kv("Python", 1));
+ assert(check_true);
+ assert(it == ite);
+ check_true = (*(--it) == kv("Python", 1));
+ assert(check_true);
+ assert(it == dbc.begin());
+
+ db.clear();
+ assert(dbc.begin() == dbc.end());
+
+ db.insert(MDDS_ASCII("A"), 1);
+ db.insert(MDDS_ASCII("AB"), 2);
+ db.insert(MDDS_ASCII("ABC"), 3);
+ db.erase(MDDS_ASCII("AB"));
+
+ it = dbc.begin();
+ check_true = (*it++ == kv("A", 1));
+ assert(check_true);
+ check_true = (*it++ == kv("ABC", 3));
+ assert(check_true);
+ assert(it == ite);
+
+ check_true = (*(--it) == kv("ABC", 3));
+ assert(check_true);
+ check_true = (*(--it) == kv("A", 1));
+ assert(check_true);
+ assert(it == dbc.begin());
+
+ db.clear();
+ db.insert(MDDS_ASCII("A"), 1);
+ db.insert(MDDS_ASCII("AB"), 2);
+ db.insert(MDDS_ASCII("ABC"), 3);
+ db.erase(MDDS_ASCII("ABC"));
+
+ it = dbc.begin();
+ check_true = (*it++ == kv("A", 1));
+ assert(check_true);
+ check_true = (*it++ == kv("AB", 2));
+ assert(check_true);
+ assert(it == ite);
+
+ check_true = (*(--it) == kv("AB", 2));
+ assert(check_true);
+ check_true = (*(--it) == kv("A", 1));
+ assert(check_true);
+ assert(it == dbc.begin());
+
+ it = ite;
+ --it;
+ assert(*it-- == kv("AB", 2)); // test post-decrement operator.
+ assert(*it == kv("A", 1));
+}
+
+void trie_test_find_iterator()
+{
+ stack_printer __stack_printer__("::trie_test_find_iterator");
+ typedef trie_map<trie::std_string_traits, int> trie_map_type;
+ trie_map_type db;
+ const trie_map_type& dbc = db;
+
+ db.insert(MDDS_ASCII("a"), 1);
+ db.insert(MDDS_ASCII("aa"), 2);
+ db.insert(MDDS_ASCII("ab"), 3);
+ db.insert(MDDS_ASCII("b"), 4);
+ {
+ auto it = dbc.find(MDDS_ASCII("a"));
+ assert(it->first == "a");
+ assert(it->second == 1);
+ ++it;
+ assert(it->first == "aa");
+ assert(it->second == 2);
+ ++it;
+ assert(it->first == "ab");
+ assert(it->second == 3);
+ ++it;
+ assert(it->first == "b");
+ assert(it->second == 4);
+ ++it;
+ assert(it == dbc.end());
+
+ it = dbc.find(MDDS_ASCII("aa"));
+ assert(it->first == "aa");
+ assert(it->second == 2);
+ ++it;
+ assert(it->first == "ab");
+ assert(it->second == 3);
+ ++it;
+ assert(it->first == "b");
+ assert(it->second == 4);
+ ++it;
+ assert(it == dbc.end());
+
+ it = dbc.find(MDDS_ASCII("ab"));
+ assert(it->first == "ab");
+ assert(it->second == 3);
+ ++it;
+ assert(it->first == "b");
+ assert(it->second == 4);
+ ++it;
+ assert(it == dbc.end());
+
+ it = dbc.find(MDDS_ASCII("b"));
+ assert(it->first == "b");
+ assert(it->second == 4);
+ ++it;
+ assert(it == dbc.end());
+ }
+
+ trie_map_type::packed_type packed = db.pack();
+ {
+ auto it = packed.find(MDDS_ASCII("a"));
+ assert(it->first == "a");
+ assert(it->second == 1);
+ ++it;
+ assert(it->first == "aa");
+ assert(it->second == 2);
+ ++it;
+ assert(it->first == "ab");
+ assert(it->second == 3);
+ ++it;
+ assert(it->first == "b");
+ assert(it->second == 4);
+ ++it;
+ assert(it == packed.end());
+
+ it = packed.find(MDDS_ASCII("aa"));
+ assert(it->first == "aa");
+ assert(it->second == 2);
+ ++it;
+ assert(it->first == "ab");
+ assert(it->second == 3);
+ ++it;
+ assert(it->first == "b");
+ assert(it->second == 4);
+ ++it;
+ assert(it == packed.end());
+
+ it = packed.find(MDDS_ASCII("ab"));
+ assert(it->first == "ab");
+ assert(it->second == 3);
+ ++it;
+ assert(it->first == "b");
+ assert(it->second == 4);
+ ++it;
+ assert(it == packed.end());
+
+ it = packed.find(MDDS_ASCII("b"));
+ assert(it->first == "b");
+ assert(it->second == 4);
+ ++it;
+ assert(it == packed.end());
+ }
+}
+
+void trie_test_prefix_search()
+{
+ stack_printer __stack_printer__("::trie_test_prefix_search");
+
+ typedef trie_map<trie::std_string_traits, int> trie_map_type;
+ trie_map_type db;
+ const trie_map_type& dbc = db;
+
+ db.insert(MDDS_ASCII("a"), 1);
+ db.insert(MDDS_ASCII("aa"), 2);
+ db.insert(MDDS_ASCII("ab"), 3);
+ db.insert(MDDS_ASCII("b"), 4);
+
+ cout << "Performing prefix search on 'a'..." << endl;
+
+ trie_map_type::search_results results = dbc.prefix_search(MDDS_ASCII("a"));
+ auto it = results.begin();
+ auto ite = results.end();
+ assert(it != ite);
+ assert(it->first == "a");
+ assert(it->second == 1);
+ ++it;
+ assert(it->first == "aa");
+ assert(it->second == 2);
+ ++it;
+ assert(it->first == "ab");
+ assert(it->second == 3);
+ ++it;
+ assert(it == ite);
+ size_t n = std::distance(results.begin(), results.end());
+ assert(n == 3);
+
+ cout << "Performing prefix search on 'b'..." << endl;
+
+ results = dbc.prefix_search(MDDS_ASCII("b"));
+ it = results.begin();
+ ite = results.end();
+ assert(it != ite);
+ assert(it->first == "b");
+ assert(it->second == 4);
+ ++it;
+ assert(it == ite);
+ --it;
+ assert(it->first == "b");
+ assert(it->second == 4);
+ n = std::distance(results.begin(), results.end());
+ assert(n == 1);
+
+ // Only one element.
+ db.clear();
+ db.insert(MDDS_ASCII("dust"), 10);
+
+ cout << "Performing prefix search on 'du'..." << endl;
+
+ results = dbc.prefix_search(MDDS_ASCII("du"));
+ it = results.begin();
+ assert(it->first == "dust");
+ assert(it->second == 10);
+ bool check_true = (++it == results.end());
+ assert(check_true);
+ --it;
+ assert(it->first == "dust");
+ assert(it->second == 10);
+ n = std::distance(results.begin(), results.end());
+ assert(n == 1);
+}
+
+void trie_test_key_as_input()
+{
+ stack_printer __stack_printer__("::trie_test_key_as_input");
+
+ typedef trie_map<trie::std_string_traits, int> trie_map_type;
+ trie_map_type db;
+ const trie_map_type& dbc = db;
+
+ db.insert(std::string("string as key"), 1);
+ db.insert("literal as key", 2);
+
+ auto it = dbc.find("literal as key");
+ assert(it != dbc.end());
+ assert(it->first == "literal as key");
+ assert(it->second == 2);
+
+ auto results = dbc.prefix_search("str");
+ auto rit = results.begin();
+ assert(rit != results.end());
+ assert(rit->first == "string as key");
+ assert(rit->second == 1);
+ ++rit;
+ assert(rit == results.end());
+}
+
+void trie_test_copying()
+{
+ stack_printer __stack_printer__("::trie_test_copying");
+
+ typedef trie_map<trie::std_string_traits, int> trie_map_type;
+ trie_map_type db;
+ assert(db.empty());
+
+ {
+ auto db_copied(db);
+ assert(db_copied.empty());
+ }
+
+ db.insert("twenty", 20);
+ db.insert("twelve", 12);
+ assert(db.size() == 2);
+
+ {
+ // copy constructor
+ auto db_copied(db);
+ const trie_map_type& dbc_copied = db_copied;
+ assert(db_copied.size() == 2);
+
+ auto it = dbc_copied.find("twenty");
+ assert(it != dbc_copied.end());
+ assert(it->first == "twenty");
+ assert(it->second == 20);
+
+ it = dbc_copied.find("twelve");
+ assert(it != dbc_copied.end());
+ assert(it->first == "twelve");
+ assert(it->second == 12);
+ }
+
+ {
+ // copy assignment
+ trie_map_type db_copied;
+ db_copied = db;
+ const trie_map_type& dbc_copied = db_copied;
+ assert(db_copied.size() == 2);
+
+ auto it = dbc_copied.find("twenty");
+ assert(it != dbc_copied.end());
+ assert(it->first == "twenty");
+ assert(it->second == 20);
+
+ it = dbc_copied.find("twelve");
+ assert(it != dbc_copied.end());
+ assert(it->first == "twelve");
+ assert(it->second == 12);
+ }
+
+ {
+ // move constructor
+ auto db_copied(db);
+ auto db_moved(std::move(db_copied));
+ const trie_map_type& dbc_moved = db_moved;
+ assert(db_moved.size() == 2);
+ assert(db_copied.empty());
+
+ auto it = dbc_moved.find("twenty");
+ assert(it != dbc_moved.end());
+ assert(it->first == "twenty");
+ assert(it->second == 20);
+
+ it = dbc_moved.find("twelve");
+ assert(it != dbc_moved.end());
+ assert(it->first == "twelve");
+ assert(it->second == 12);
+ }
+
+ {
+ // move assignment
+ auto db_copied(db);
+ trie_map_type db_moved;
+ db_moved = std::move(db_copied);
+ const trie_map_type& dbc_moved = db_moved;
+ assert(db_moved.size() == 2);
+ assert(db_copied.empty());
+
+ auto it = dbc_moved.find("twenty");
+ assert(it != dbc_moved.end());
+ assert(it->first == "twenty");
+ assert(it->second == 20);
+
+ it = dbc_moved.find("twelve");
+ assert(it != dbc_moved.end());
+ assert(it->first == "twelve");
+ assert(it->second == 12);
+ }
+}
+
+void trie_test_value_update_from_iterator()
+{
+ stack_printer __stack_printer__("::trie_test_value_update_from_iterator");
+
+ typedef trie_map<trie::std_string_traits, int> trie_map_type;
+ trie_map_type db;
+ db.insert("one", 1);
+ db.insert("two", 2);
+ db.insert("three", 3);
+
+ trie_map_type::iterator it = db.begin();
+ assert(it->first == "one");
+ assert(it->second == 1);
+ it->second = 10; // update the value.
+ it = db.begin();
+ assert(it->first == "one");
+ assert(it->second == 10);
+
+ it = db.find("three");
+ assert(it->first == "three");
+ assert(it->second == 3);
+ it->second = 345; // update the value again.
+ it = db.find("three");
+ assert(it->first == "three");
+ assert(it->second == 345);
+}
+
+int main()
+{
+ try
+ {
+ trie_packed_test1();
+ trie_packed_test2();
+ trie_packed_test3();
+ trie_packed_test4();
+ trie_packed_test_value_life_cycle();
+ trie_packed_test_custom_string();
+ trie_packed_test_iterator_empty();
+ trie_packed_test_iterator();
+ trie_packed_test_prefix_search1();
+ trie_packed_test_key_as_input();
+ trie_packed_test_copying();
+ trie_packed_test_non_equal();
+ trie_packed_test_save_and_load_state::run();
+
+ trie_test1();
+ trie_test2();
+
+ trie_test_iterator_empty();
+ trie_test_iterator();
+ trie_test_iterator_with_erase();
+ trie_test_find_iterator();
+ trie_test_prefix_search();
+ trie_test_key_as_input();
+ trie_test_copying();
+ trie_test_value_update_from_iterator();
+ }
+ catch (const std::exception& e)
+ {
+ cout << "Test failed: " << e.what() << endl;
+ return EXIT_FAILURE;
+ }
+
+ cout << "Test finished successfully!" << endl;
+
+ return EXIT_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */