From 1a14ff58c9c40b13df8cda431fd2b35ca079b5d4 Mon Sep 17 00:00:00 2001 From: Josh MacDonald Date: Mon, 12 Dec 2011 13:46:12 -0800 Subject: [PATCH] Initial checkin of C++ btree code, has some useless google3-specific bits to be removed and needs a Makefile. --- Makefile | 75 ++ benchmarks.awk | 27 + btree.h | 2420 +++++++++++++++++++++++++++++++++++++++++ btree_bench.cc | 483 ++++++++ btree_container.h | 325 ++++++ btree_map.h | 122 +++ btree_nc.cc | 50 + btree_nc_test.py | 83 ++ btree_printer.py | 125 +++ btree_printer_test.py | 92 ++ btree_set.h | 113 ++ btree_test.cc | 167 +++ btree_test.h | 930 ++++++++++++++++ btree_test_flags.cc | 9 + btree_test_program.cc | 64 ++ safe_btree.h | 379 +++++++ safe_btree_map.h | 70 ++ safe_btree_set.h | 68 ++ safe_btree_test.cc | 67 ++ 19 files changed, 5669 insertions(+) create mode 100644 Makefile create mode 100755 benchmarks.awk create mode 100644 btree.h create mode 100644 btree_bench.cc create mode 100644 btree_container.h create mode 100644 btree_map.h create mode 100644 btree_nc.cc create mode 100755 btree_nc_test.py create mode 100755 btree_printer.py create mode 100755 btree_printer_test.py create mode 100644 btree_set.h create mode 100644 btree_test.cc create mode 100644 btree_test.h create mode 100644 btree_test_flags.cc create mode 100644 btree_test_program.cc create mode 100644 safe_btree.h create mode 100644 safe_btree_map.h create mode 100644 safe_btree_set.h create mode 100644 safe_btree_test.cc diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..c58946f9 --- /dev/null +++ b/Makefile @@ -0,0 +1,75 @@ +# -*- mode: python; -*- + +# This should be a Makefile, but it's not. + +# cc_library(name = "btree", +# srcs = [ "btree.h", +# "btree_container.h", +# "btree_map.h", +# "btree_set.h", +# "safe_btree.h", +# "safe_btree_map.h", +# "safe_btree_set.h" ], +# deps = [ "//strings", +# "//strings:cord" ]) + +# cc_library(name = "btree_test_flags", +# srcs = [ "btree_test_flags.cc" ], +# deps = [ "//base" ]) + +# cc_binary(name = "btree_bench", +# srcs = [ "btree_bench.cc" ], +# deps = [ ":btree", +# ":btree_test_flags", +# "//testing/base", +# "//util/random" ]) + +# cc_test(name = "btree_test", +# srcs = [ "btree_test.cc", ], +# deps = [ ":btree", +# ":btree_test_flags", +# "//base:heapcheck", +# "//testing/base", +# "//util/random", +# ], +# size = "large") + +# cc_test(name = "safe_btree_test", +# srcs = [ "safe_btree_test.cc", ], +# deps = [ ":btree", +# ":btree_test_flags", +# "//base:heapcheck", +# "//testing/base", +# "//util/random", +# ], +# size = "large") + +# cc_fake_binary(name = "btree_nc", +# srcs = [ "btree_nc.cc" ], +# deps = [ ":btree" ], +# legacy = 0) + +# py_test(name = "btree_nc_test", +# srcs = [ "btree_nc_test.py" ], +# deps = [ "//pyglib", +# "//testing/pybase" ], +# data = [ "btree_nc" ], +# size = "large") + +# cc_binary(name = "btree_test_program", +# srcs = [ "btree_test_program.cc" ], +# deps = [ ":btree", +# "//devtools/gdb/component:gdb_test_utils" ], +# testonly = 1) + +# # This test will only actually test the pretty-printing code if it's +# # compiled with debug information (blaze build -c dbg). The default +# # mode, fastbuild, will pass but will not catch any regressions! +# py_test(name = "btree_printer_test", +# size = "large", +# srcs = [ "btree_printer_test.py", +# "btree_printer.py" ], +# deps = [ "//devtools/gdb/component:gdbpy", +# "//testing/pybase", +# "//testing/gdb:gdb_script_test_util", +# ":btree_test_program" ]) diff --git a/benchmarks.awk b/benchmarks.awk new file mode 100755 index 00000000..d5d4aeac --- /dev/null +++ b/benchmarks.awk @@ -0,0 +1,27 @@ +#!/usr/bin/gawk -f + +/^Run on/ { + print $0; + printf "%-25s %5s %-20s\n", + "Benchmark", "STL(ns)", "B-Tree(ns) @ " + printf "--------------------------------------------------------\n"; +} + +/^BM_/ { + split($1, name, "_"); + if (name[2] == "stl") { + stl = $3; + stl_bytes = $5 + printf "%-25s %5d ", name[1] "_" name[3] "_" name[4] "_" name[5], stl; + fflush(); + } else if (name[2] == "btree") { + btree = $3 + btree_size = name[3] + btree_bytes = $5 + printf "%5d %+7.2f%% <%3d>", btree, 100.0 * (stl - btree) / stl, btree_size; + printf " [%4.1f, %4.1f]\n", stl_bytes, btree_bytes; + fflush(); + } else { + printf "ERROR: %s unrecognized\n", $1 + } +} diff --git a/btree.h b/btree.h new file mode 100644 index 00000000..b9fd04e2 --- /dev/null +++ b/btree.h @@ -0,0 +1,2420 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: jmacd@google.com (Josh MacDonald) +// Author: pmattis@google.com (Peter Mattis) +// +// A btree implementation of the STL set and map interfaces. A btree is both +// smaller and faster than STL set/map. The red-black tree implementation of +// STL set/map has an overhead of 3 pointers (left, right and parent) plus the +// node color information for each stored value. So a set consumes 20 +// bytes for each value stored. This btree implementation stores multiple +// values on fixed size nodes (usually 256 bytes) and doesn't store child +// pointers for leaf nodes. The result is that a btree_set may use much +// less memory per stored value. For the random insertion benchmark in +// btree_test.cc, a btree_set with node-size of 256 uses 4.9 bytes per +// stored value. +// +// The packing of multiple values on to each node of a btree has another effect +// besides better space utilization: better cache locality due to fewer cache +// lines being accessed. Better cache locality translates into faster +// operations. +// +// CAVEATS +// +// Insertions and deletions on a btree can cause splitting, merging or +// rebalancing of btree nodes. And even without these operations, insertions +// and deletions on a btree will move values around within a node. In both +// cases, the result is that insertions and deletions can invalidate iterators +// pointing to values other than the one being inserted/deleted. This is +// notably different from STL set/map which takes care to not invalidate +// iterators on insert/erase except, of course, for iterators pointing to the +// value being erased. A partial workaround when erasing is available: +// erase() returns an iterator pointing to the item just after the one that was +// erased (or end() if none exists). See also safe_btree. + +// PERFORMANCE +// +// btree_bench --benchmarks=. 2>&1 | ./benchmarks.awk +// +// NOTE(pmattis): My warpstation (pmattis-warp.nyc) often produces slower +// results when running the benchmarks on CPUs 0 and 1 vs CPUs 2 and 3. To get +// consistent benchmark results, I run "taskset 0xc " to run the +// benchmark on CPUs 2 and 3. +// +// Run on pmattis-warp.nyc (4 X 2200 MHz CPUs); 2010/03/04-15:23:06 +// Benchmark STL(ns) B-Tree(ns) @ +// -------------------------------------------------------- +// BM_set_int32_insert 1516 608 +59.89% <256> [40.0, 5.2] +// BM_set_int32_lookup 1160 414 +64.31% <256> [40.0, 5.2] +// BM_set_int32_fulllookup 960 410 +57.29% <256> [40.0, 4.4] +// BM_set_int32_delete 1741 528 +69.67% <256> [40.0, 5.2] +// BM_set_int32_queueaddrem 3078 1046 +66.02% <256> [40.0, 5.5] +// BM_set_int32_mixedaddrem 3600 1384 +61.56% <256> [40.0, 5.3] +// BM_set_int32_fifo 227 113 +50.22% <256> [40.0, 4.4] +// BM_set_int32_fwditer 158 26 +83.54% <256> [40.0, 5.2] +// BM_map_int32_insert 1551 636 +58.99% <256> [48.0, 10.5] +// BM_map_int32_lookup 1200 508 +57.67% <256> [48.0, 10.5] +// BM_map_int32_fulllookup 989 487 +50.76% <256> [48.0, 8.8] +// BM_map_int32_delete 1794 628 +64.99% <256> [48.0, 10.5] +// BM_map_int32_queueaddrem 3189 1266 +60.30% <256> [48.0, 11.6] +// BM_map_int32_mixedaddrem 3822 1623 +57.54% <256> [48.0, 10.9] +// BM_map_int32_fifo 151 134 +11.26% <256> [48.0, 8.8] +// BM_map_int32_fwditer 161 32 +80.12% <256> [48.0, 10.5] +// BM_set_int64_insert 1546 636 +58.86% <256> [40.0, 10.5] +// BM_set_int64_lookup 1200 512 +57.33% <256> [40.0, 10.5] +// BM_set_int64_fulllookup 971 487 +49.85% <256> [40.0, 8.8] +// BM_set_int64_delete 1745 616 +64.70% <256> [40.0, 10.5] +// BM_set_int64_queueaddrem 3163 1195 +62.22% <256> [40.0, 11.6] +// BM_set_int64_mixedaddrem 3760 1564 +58.40% <256> [40.0, 10.9] +// BM_set_int64_fifo 146 103 +29.45% <256> [40.0, 8.8] +// BM_set_int64_fwditer 162 31 +80.86% <256> [40.0, 10.5] +// BM_map_int64_insert 1551 720 +53.58% <256> [48.0, 20.7] +// BM_map_int64_lookup 1214 612 +49.59% <256> [48.0, 20.7] +// BM_map_int64_fulllookup 994 592 +40.44% <256> [48.0, 17.2] +// BM_map_int64_delete 1778 764 +57.03% <256> [48.0, 20.7] +// BM_map_int64_queueaddrem 3189 1547 +51.49% <256> [48.0, 20.9] +// BM_map_int64_mixedaddrem 3779 1887 +50.07% <256> [48.0, 21.6] +// BM_map_int64_fifo 147 145 +1.36% <256> [48.0, 17.2] +// BM_map_int64_fwditer 162 41 +74.69% <256> [48.0, 20.7] +// BM_set_string_insert 1989 1966 +1.16% <256> [64.0, 44.5] +// BM_set_string_lookup 1709 1600 +6.38% <256> [64.0, 44.5] +// BM_set_string_fulllookup 1573 1529 +2.80% <256> [64.0, 35.4] +// BM_set_string_delete 2520 1920 +23.81% <256> [64.0, 44.5] +// BM_set_string_queueaddrem 4706 4309 +8.44% <256> [64.0, 48.3] +// BM_set_string_mixedaddrem 5080 4654 +8.39% <256> [64.0, 46.7] +// BM_set_string_fifo 318 512 -61.01% <256> [64.0, 35.4] +// BM_set_string_fwditer 182 93 +48.90% <256> [64.0, 44.5] +// BM_map_string_insert 2600 2227 +14.35% <256> [72.0, 55.8] +// BM_map_string_lookup 2068 1730 +16.34% <256> [72.0, 55.8] +// BM_map_string_fulllookup 1859 1618 +12.96% <256> [72.0, 44.0] +// BM_map_string_delete 3168 2080 +34.34% <256> [72.0, 55.8] +// BM_map_string_queueaddrem 5840 4701 +19.50% <256> [72.0, 59.4] +// BM_map_string_mixedaddrem 6400 5200 +18.75% <256> [72.0, 57.8] +// BM_map_string_fifo 398 596 -49.75% <256> [72.0, 44.0] +// BM_map_string_fwditer 243 113 +53.50% <256> [72.0, 55.8] +// BM_set_cord_insert 3661 2680 +26.80% <256> [40.0, 10.5] +// BM_set_cord_lookup 2920 2293 +21.47% <256> [40.0, 10.5] +// BM_set_cord_fulllookup 2960 2267 +23.41% <256> [40.0, 8.8] +// BM_set_cord_delete 4679 2535 +45.82% <256> [40.0, 10.5] +// BM_set_cord_queueaddrem 8230 5600 +31.96% <256> [40.0, 11.3] +// BM_set_cord_mixedaddrem 8497 6080 +28.45% <256> [40.0, 10.7] +// BM_set_cord_fifo 358 370 -3.35% <256> [40.0, 8.8] +// BM_set_cord_fwditer 352 193 +45.17% <256> [40.0, 10.5] +// BM_map_cord_insert 3680 2927 +20.46% <256> [48.0, 20.7] +// BM_map_cord_lookup 3018 2466 +18.29% <256> [48.0, 20.7] +// BM_map_cord_fulllookup 2943 2466 +16.21% <256> [48.0, 17.2] +// BM_map_cord_delete 4675 2775 +40.64% <256> [48.0, 20.7] +// BM_map_cord_queueaddrem 8383 6360 +24.13% <256> [48.0, 22.2] +// BM_map_cord_mixedaddrem 8952 6760 +24.49% <256> [48.0, 21.2] +// BM_map_cord_fifo 444 463 -4.28% <256> [48.0, 17.2] +// BM_map_cord_fwditer 391 225 +42.46% <256> [48.0, 20.7] + +#ifndef UTIL_BTREE_BTREE_H__ +#define UTIL_BTREE_BTREE_H__ + +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export // Clients can rely on this. +#include +#include +#include // IWYU pragma: export // Clients can rely on this. +#include +#include + +#include "base/gdb-scripting.h" +#include "base/integral_types.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/template_util.h" +#include "base/type_traits.h" +#include "strings/cord.h" +#include "strings/stringpiece.h" + +// iwyu.py found std::swap in , but we include . +// IWYU pragma: no_include +// +// iwyu.py doesn't think we need , so wants . +// IWYU pragma: no_include +// +// iwyu.py sure wants to bring in a lot of private STL headers. +// IWYU pragma: no_include +// IWYU pragma: no_include +// IWYU pragma: no_include +// IWYU pragma: no_include + +namespace util { +namespace btree { + +// Inside a btree method, if we just call swap(), it will choose the +// btree::swap method, which we don't want. And we can't say ::swap because +// then MSVC won't pickup any std::swap() implementations. We can't just use +// std::swap() directly because then we don't get the specialization for string +// and Cord (which are not defined in the std namespace!). So the solution is +// to have a special swap helper function whose name doesn't collide with other +// swap functions defined by the btree classes. +template +inline void btree_swap_helper(T &a, T &b) { + using std::swap; + swap(a, b); +} + +// A helper type used to indicate that a key-compare-to functor has been +// provided. A user can specify a key-compare-to functor by doing: +// +// struct MyStringComparer +// : public util::btree::btree_key_compare_to_tag { +// int operator()(const string &a, const string &b) const { +// return a.compare(b); +// } +// }; +// +// Note that the return type is an int and not a bool. There is a +// COMPILE_ASSERT which enforces this return type. +struct btree_key_compare_to_tag { +}; + +// A helper class that indicates if the Compare parameter is derived from +// btree_key_compare_to_tag. +template +struct btree_is_key_compare_to + : public base::is_convertible { +}; + +// A helper class to convert a boolean comparison into a three-way "compare-to" +// comparison that returns a negative value to indicate less-than, zero to +// indicate equality and a positive value to indicate greater-than. This helper +// class is specialized for less, greater, less, +// greater, less and greater. The +// btree_key_compare_to_adapter class is provided so that btree users +// automatically get the more efficient compare-to code when using common +// google string types with common comparison functors. +template +struct btree_key_compare_to_adapter : Compare { + btree_key_compare_to_adapter() { } + btree_key_compare_to_adapter(const Compare &c) : Compare(c) { } + btree_key_compare_to_adapter(const btree_key_compare_to_adapter &c) + : Compare(c) { + } +}; + +template <> +struct btree_key_compare_to_adapter > + : public btree_key_compare_to_tag { + btree_key_compare_to_adapter() {} + btree_key_compare_to_adapter(const less&) {} + btree_key_compare_to_adapter( + const btree_key_compare_to_adapter >&) {} + int operator()(const string &a, const string &b) const { + return a.compare(b); + } +}; + +template <> +struct btree_key_compare_to_adapter > + : public btree_key_compare_to_tag { + btree_key_compare_to_adapter() {} + btree_key_compare_to_adapter(const greater&) {} + btree_key_compare_to_adapter( + const btree_key_compare_to_adapter >&) {} + int operator()(const string &a, const string &b) const { + return b.compare(a); + } +}; + +template <> +struct btree_key_compare_to_adapter > + : public btree_key_compare_to_tag { + btree_key_compare_to_adapter() {} + btree_key_compare_to_adapter(const less&) {} + btree_key_compare_to_adapter( + const btree_key_compare_to_adapter >&) {} + int operator()(const StringPiece &a, const StringPiece &b) const { + return a.compare(b); + } +}; + +template <> +struct btree_key_compare_to_adapter > + : public btree_key_compare_to_tag { + btree_key_compare_to_adapter() {} + btree_key_compare_to_adapter(const greater&) {} + btree_key_compare_to_adapter( + const btree_key_compare_to_adapter >&) {} + int operator()(const StringPiece &a, const StringPiece &b) const { + return b.compare(a); + } +}; + +template <> +struct btree_key_compare_to_adapter > + : public btree_key_compare_to_tag { + btree_key_compare_to_adapter() {} + btree_key_compare_to_adapter(const less&) {} + btree_key_compare_to_adapter( + const btree_key_compare_to_adapter >&) {} + int operator()(const Cord &a, const Cord &b) const { + return a.CompareTo(b); + } +}; + +template <> +struct btree_key_compare_to_adapter > + : public btree_key_compare_to_tag { + btree_key_compare_to_adapter() {} + btree_key_compare_to_adapter(const greater&) {} + btree_key_compare_to_adapter( + const btree_key_compare_to_adapter >&) {} + int operator()(const Cord &a, const Cord &b) const { + return b.CompareTo(a); + } +}; + +// A helper class that allows a compare-to functor to behave like a plain +// compare functor. This specialization is used when we do not have a +// compare-to functor. +template +struct btree_key_comparer { + btree_key_comparer() {} + btree_key_comparer(Compare c) : comp(c) {} + static bool bool_compare(const Compare &comp, const Key &x, const Key &y) { + return comp(x, y); + } + bool operator()(const Key &x, const Key &y) const { + return bool_compare(comp, x, y); + } + Compare comp; +}; + +// A specialization of btree_key_comparer when a compare-to functor is +// present. We need a plain (boolean) comparison in some parts of the btree +// code, such as insert-with-hint. +template +struct btree_key_comparer { + btree_key_comparer() {} + btree_key_comparer(Compare c) : comp(c) {} + static bool bool_compare(const Compare &comp, const Key &x, const Key &y) { + return comp(x, y) < 0; + } + bool operator()(const Key &x, const Key &y) const { + return bool_compare(comp, x, y); + } + Compare comp; +}; + +// A helper function to compare to keys using the specified compare +// functor. This dispatches to the appropriate btree_key_comparer comparison, +// depending on whether we have a compare-to functor or not (which depends on +// whether Compare is derived from btree_key_compare_to_tag). +template +static bool btree_compare_keys( + const Compare &comp, const Key &x, const Key &y) { + typedef btree_key_comparer::value> key_comparer; + return key_comparer::bool_compare(comp, x, y); +} + +template +struct btree_common_params { + // If Compare is derived from btree_key_compare_to_tag then use it as the + // key_compare type. Otherwise, use btree_key_compare_to_adapter<> which will + // fall-back to Compare if we don't have an appropriate specialization. + typedef typename base::if_< + btree_is_key_compare_to::value, + Compare, btree_key_compare_to_adapter >::type key_compare; + // A type which indicates if we have a key-compare-to functor or a plain old + // key-compare functor. + typedef btree_is_key_compare_to is_key_compare_to; + + typedef Alloc allocator_type; + typedef Key key_type; + typedef ssize_t size_type; + typedef ptrdiff_t difference_type; + + enum { + kTargetNodeSize = TargetNodeSize, + }; +}; + +// A parameters structure for holding the type parameters for a btree_map. +template +struct btree_map_params + : public btree_common_params { + typedef Data data_type; + typedef Data mapped_type; + typedef pair value_type; + typedef pair mutable_value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + + enum { + kValueSize = sizeof(Key) + sizeof(data_type), + }; + + static const Key& key(const value_type &x) { return x.first; } + static const Key& key(const mutable_value_type &x) { return x.first; } + static void swap(mutable_value_type *a, mutable_value_type *b) { + btree_swap_helper(a->first, b->first); + btree_swap_helper(a->second, b->second); + } +}; + +// A parameters structure for holding the type parameters for a btree_set. +template +struct btree_set_params + : public btree_common_params { + typedef base::false_type data_type; + typedef base::false_type mapped_type; + typedef Key value_type; + typedef value_type mutable_value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + + enum { + kValueSize = sizeof(Key), + }; + + static const Key& key(const value_type &x) { return x; } + static void swap(mutable_value_type *a, mutable_value_type *b) { + btree_swap_helper(*a, *b); + } +}; + +// An adapter class that converts a lower-bound compare into an upper-bound +// compare. +template +struct btree_upper_bound_adapter : public Compare { + btree_upper_bound_adapter(Compare c) : Compare(c) {} + bool operator()(const Key &a, const Key &b) const { + return !static_cast(*this)(b, a); + } +}; + +template +struct btree_upper_bound_compare_to_adapter : public CompareTo { + btree_upper_bound_compare_to_adapter(CompareTo c) : CompareTo(c) {} + int operator()(const Key &a, const Key &b) const { + return static_cast(*this)(b, a); + } +}; + +// Dispatch helper class for using linear search with plain compare. +template +struct btree_linear_search_plain_compare { + static int lower_bound(const K &k, const N &n, Compare comp) { + return n.linear_search_plain_compare(k, 0, n.count(), comp); + } + static int upper_bound(const K &k, const N &n, Compare comp) { + typedef btree_upper_bound_adapter upper_compare; + return n.linear_search_plain_compare(k, 0, n.count(), upper_compare(comp)); + } +}; + +// Dispatch helper class for using linear search with compare-to +template +struct btree_linear_search_compare_to { + static int lower_bound(const K &k, const N &n, CompareTo comp) { + return n.linear_search_compare_to(k, 0, n.count(), comp); + } + static int upper_bound(const K &k, const N &n, CompareTo comp) { + typedef btree_upper_bound_adapter > upper_compare; + return n.linear_search_plain_compare(k, 0, n.count(), upper_compare(comp)); + } +}; + +// Dispatch helper class for using binary search with plain compare. +template +struct btree_binary_search_plain_compare { + static int lower_bound(const K &k, const N &n, Compare comp) { + return n.binary_search_plain_compare(k, 0, n.count(), comp); + } + static int upper_bound(const K &k, const N &n, Compare comp) { + typedef btree_upper_bound_adapter upper_compare; + return n.binary_search_plain_compare(k, 0, n.count(), upper_compare(comp)); + } +}; + +// Dispatch helper class for using binary search with compare-to. +template +struct btree_binary_search_compare_to { + static int lower_bound(const K &k, const N &n, CompareTo comp) { + return n.binary_search_compare_to(k, 0, n.count(), CompareTo()); + } + static int upper_bound(const K &k, const N &n, CompareTo comp) { + typedef btree_upper_bound_adapter > upper_compare; + return n.linear_search_plain_compare(k, 0, n.count(), upper_compare(comp)); + } +}; + +// A node in the btree holding. The same node type is used for both internal +// and leaf nodes in the btree, though the nodes are allocated in such a way +// that the children array is only valid in internal nodes. +template +class btree_node { + public: + typedef Params params_type; + typedef btree_node self_type; + typedef typename Params::key_type key_type; + typedef typename Params::data_type data_type; + typedef typename Params::value_type value_type; + typedef typename Params::mutable_value_type mutable_value_type; + typedef typename Params::pointer pointer; + typedef typename Params::const_pointer const_pointer; + typedef typename Params::reference reference; + typedef typename Params::const_reference const_reference; + typedef typename Params::key_compare key_compare; + typedef typename Params::size_type size_type; + typedef typename Params::difference_type difference_type; + // Typedefs for the various types of node searches. + typedef btree_linear_search_plain_compare< + key_type, self_type, key_compare> linear_search_plain_compare_type; + typedef btree_linear_search_compare_to< + key_type, self_type, key_compare> linear_search_compare_to_type; + typedef btree_binary_search_plain_compare< + key_type, self_type, key_compare> binary_search_plain_compare_type; + typedef btree_binary_search_compare_to< + key_type, self_type, key_compare> binary_search_compare_to_type; + // If we have a valid key-compare-to type, use linear_search_compare_to, + // otherwise use linear_search_plain_compare. + typedef typename base::if_< + Params::is_key_compare_to::value, + linear_search_compare_to_type, + linear_search_plain_compare_type>::type linear_search_type; + // If we have a valid key-compare-to type, use binary_search_compare_to, + // otherwise use binary_search_plain_compare. + typedef typename base::if_< + Params::is_key_compare_to::value, + binary_search_compare_to_type, + binary_search_plain_compare_type>::type binary_search_type; + // If the key is an integral or floating point type, use linear search which + // is faster than binary search for such types. Might be wise to also + // configure linear search based on node-size. + typedef typename base::if_< + base::is_integral::value || + base::is_floating_point::value, + linear_search_type, binary_search_type>::type search_type; + + struct base_fields { + // A boolean indicating whether the node is a leaf or not. + uint8 leaf; + // The position of the node in the node's parent. + uint8 position; + // The maximum number of values the node can hold. + uint8 max_count; + // The count of the number of values in the node. + uint8 count; + // A pointer to the node's parent. + btree_node *parent; + }; + + enum { + kValueSize = params_type::kValueSize, + kTargetNodeSize = params_type::kTargetNodeSize, + + // Compute how many values we can fit onto a leaf node. + kNodeTargetValues = (kTargetNodeSize - sizeof(base_fields)) / kValueSize, + // We need a minimum of 3 values per internal node in order to perform + // splitting (1 value for the two nodes involved in the split and 1 value + // propagated to the parent as the delimiter for the split). + kNodeValues = kNodeTargetValues >= 3 ? kNodeTargetValues : 3, + + kExactMatch = 1 << 30, + kMatchMask = kExactMatch - 1, + }; + + struct leaf_fields : public base_fields { + // The array of values. Only the first count of these values have been + // constructed and are valid. + mutable_value_type values[kNodeValues]; + }; + + struct internal_fields : public leaf_fields { + // The array of child pointers. The keys in children_[i] are all less than + // key(i). The keys in children_[i + 1] are all greater than key(i). There + // are always count + 1 children. + btree_node *children[kNodeValues + 1]; + }; + + struct root_fields : public internal_fields { + btree_node *rightmost; + size_type size; + }; + + public: + // Getter/setter for whether this is a leaf node or not. This value doesn't + // change after the node is created. + bool leaf() const { return fields_.leaf; } + + // Getter for the position of this node in its parent. + int position() const { return fields_.position; } + void set_position(int v) { fields_.position = v; } + + // Getter/setter for the number of values stored in this node. + int count() const { return fields_.count; } + void set_count(int v) { fields_.count = v; } + int max_count() const { return fields_.max_count; } + + // Getter for the parent of this node. + btree_node* parent() const { return fields_.parent; } + // Getter for whether the node is the root of the tree. The parent of the + // root of the tree is the leftmost node in the tree which is guaranteed to + // be a leaf. + bool is_root() const { return parent()->leaf(); } + void make_root() { + DCHECK(parent()->is_root()); + fields_.parent = fields_.parent->parent(); + } + + // Getter for the rightmost root node field. Only valid on the root node. + btree_node* rightmost() const { return fields_.rightmost; } + btree_node** mutable_rightmost() { return &fields_.rightmost; } + + // Getter for the size root node field. Only valid on the root node. + size_type size() const { return fields_.size; } + size_type* mutable_size() { return &fields_.size; } + + // Getters for the key/value at position i in the node. + const key_type& key(int i) const { + return params_type::key(fields_.values[i]); + } + reference value(int i) { + return reinterpret_cast(fields_.values[i]); + } + const_reference value(int i) const { + return reinterpret_cast(fields_.values[i]); + } + mutable_value_type* mutable_value(int i) { + return &fields_.values[i]; + } + + // Swap value i in this node with value j in node x. + void value_swap(int i, btree_node *x, int j) { + params_type::swap(mutable_value(i), x->mutable_value(j)); + } + + // Getters/setter for the child at position i in the node. + btree_node* child(int i) const { return fields_.children[i]; } + btree_node** mutable_child(int i) { return &fields_.children[i]; } + void set_child(int i, btree_node *c) { + *mutable_child(i) = c; + c->fields_.parent = this; + c->fields_.position = i; + } + + // Returns the position of the first value whose key is not less than k. + template + int lower_bound(const key_type &k, const Compare &comp) const { + return search_type::lower_bound(k, *this, comp); + } + // Returns the position of the first value whose key is greater than k. + template + int upper_bound(const key_type &k, const Compare &comp) const { + return search_type::upper_bound(k, *this, comp); + } + + // Returns the position of the first value whose key is not less than k using + // linear search performed using plain compare. + template + int linear_search_plain_compare( + const key_type &k, int s, int e, const Compare &comp) const { + while (s < e) { + if (!btree_compare_keys(comp, key(s), k)) { + break; + } + ++s; + } + return s; + } + + // Returns the position of the first value whose key is not less than k using + // linear search performed using compare-to. + template + int linear_search_compare_to( + const key_type &k, int s, int e, const Compare &comp) const { + while (s < e) { + int c = comp(key(s), k); + if (c == 0) { + return s | kExactMatch; + } else if (c > 0) { + break; + } + ++s; + } + return s; + } + + // Returns the position of the first value whose key is not less than k using + // binary search performed using plain compare. + template + int binary_search_plain_compare( + const key_type &k, int s, int e, const Compare &comp) const { + while (s != e) { + int mid = (s + e) / 2; + if (btree_compare_keys(comp, key(mid), k)) { + s = mid + 1; + } else { + e = mid; + } + } + return s; + } + + // Returns the position of the first value whose key is not less than k using + // binary search performed using compare-to. + template + int binary_search_compare_to( + const key_type &k, int s, int e, const CompareTo &comp) const { + while (s != e) { + int mid = (s + e) / 2; + int c = comp(key(mid), k); + if (c < 0) { + s = mid + 1; + } else if (c > 0) { + e = mid; + } else { + // Need to return the first value whose key is not less than k, which + // requires continuing the binary search. Note that we are guaranteed + // that the result is an exact match because if "key(mid-1) < k" the + // call to binary_search_compare_to() will return "mid". + s = binary_search_compare_to(k, s, mid, comp); + return s | kExactMatch; + } + } + return s; + } + + // Inserts the value x at position i, shifting all existing values and + // children at positions >= i to the right by 1. + void insert_value(int i, const value_type &x); + + // Removes the value at position i, shifting all existing values and children + // at positions > i to the left by 1. + void remove_value(int i); + + // Rebalances a node with its right sibling. + void rebalance_right_to_left(btree_node *sibling, int to_move); + void rebalance_left_to_right(btree_node *sibling, int to_move); + + // Splits a node, moving a portion of the node's values to its right sibling. + void split(btree_node *sibling, int insert_position); + + // Merges a node with its right sibling, moving all of the values and the + // delimiting key in the parent node onto itself. + void merge(btree_node *sibling); + + // Swap the contents of "this" and "src". + void swap(btree_node *src); + + // Node allocation/deletion routines. + static btree_node* init_leaf( + leaf_fields *f, btree_node *parent, int max_count) { + btree_node *n = reinterpret_cast(f); + f->leaf = 1; + f->position = 0; + f->max_count = max_count; + f->count = 0; + f->parent = parent; + if (DEBUG_MODE) { + memset(&f->values, 0, max_count * sizeof(value_type)); + } + return n; + } + static btree_node* init_internal(internal_fields *f, btree_node *parent) { + btree_node *n = init_leaf(f, parent, kNodeValues); + f->leaf = 0; + if (DEBUG_MODE) { + memset(f->children, 0, sizeof(f->children)); + } + return n; + } + static btree_node* init_root(root_fields *f, btree_node *parent) { + btree_node *n = init_internal(f, parent); + f->rightmost = parent; + f->size = parent->count(); + return n; + } + void destroy() { + for (int i = 0; i < count(); ++i) { + value_destroy(i); + } + } + + private: + void value_init(int i) { + new (&fields_.values[i]) mutable_value_type; + } + void value_init(int i, const value_type &x) { + new (&fields_.values[i]) mutable_value_type(x); + } + void value_destroy(int i) { + fields_.values[i].~mutable_value_type(); + } + + private: + root_fields fields_; + + private: + DISALLOW_EVIL_CONSTRUCTORS(btree_node); +}; + +template +struct btree_iterator { + typedef typename Node::key_type key_type; + typedef typename Node::size_type size_type; + typedef typename Node::difference_type difference_type; + typedef typename Node::params_type params_type; + + typedef Node node_type; + typedef typename base::remove_const::type normal_node; + typedef const Node const_node; + typedef typename params_type::value_type value_type; + typedef typename params_type::pointer normal_pointer; + typedef typename params_type::reference normal_reference; + typedef typename params_type::const_pointer const_pointer; + typedef typename params_type::const_reference const_reference; + + typedef Pointer pointer; + typedef Reference reference; + typedef bidirectional_iterator_tag iterator_category; + + typedef btree_iterator< + normal_node, normal_reference, normal_pointer> iterator; + typedef btree_iterator< + const_node, const_reference, const_pointer> const_iterator; + typedef btree_iterator self_type; + + btree_iterator() + : node(NULL), + position(-1) { + } + btree_iterator(Node *n, int p) + : node(n), + position(p) { + } + btree_iterator(const iterator &x) + : node(x.node), + position(x.position) { + } + + // Increment/decrement the iterator. + void increment() { + if (node->leaf() && ++position < node->count()) { + return; + } + increment_slow(); + } + void increment_by(int count); + void increment_slow(); + + void decrement() { + if (node->leaf() && --position >= 0) { + return; + } + decrement_slow(); + } + void decrement_slow(); + + bool operator==(const const_iterator &x) const { + return node == x.node && position == x.position; + } + bool operator!=(const const_iterator &x) const { + return node != x.node || position != x.position; + } + + // Accessors for the key/value the iterator is pointing at. + const key_type& key() const { + return node->key(position); + } + reference operator*() const { + return node->value(position); + } + pointer operator->() const { + return &node->value(position); + } + + self_type& operator++() { + increment(); + return *this; + } + self_type& operator--() { + decrement(); + return *this; + } + self_type operator++(int) { + self_type tmp = *this; + ++*this; + return tmp; + } + self_type operator--(int) { + self_type tmp = *this; + --*this; + return tmp; + } + + // The node in the tree the iterator is pointing at. + Node *node; + // The position within the node of the tree the iterator is pointing at. + int position; +}; + +// Dispatch helper class for using btree::internal_locate with plain compare. +struct btree_internal_locate_plain_compare { + template + static pair dispatch(const K &k, const T &t, Iter iter) { + return t.internal_locate_plain_compare(k, iter); + } +}; + +// Dispatch helper class for using btree::internal_locate with compare-to. +struct btree_internal_locate_compare_to { + template + static pair dispatch(const K &k, const T &t, Iter iter) { + return t.internal_locate_compare_to(k, iter); + } +}; + +template +class btree : public Params::key_compare { + typedef btree self_type; + typedef btree_node node_type; + typedef typename node_type::base_fields base_fields; + typedef typename node_type::leaf_fields leaf_fields; + typedef typename node_type::internal_fields internal_fields; + typedef typename node_type::root_fields root_fields; + typedef typename Params::is_key_compare_to is_key_compare_to; + + friend class btree_internal_locate_plain_compare; + friend class btree_internal_locate_compare_to; + typedef typename base::if_< + is_key_compare_to::value, + btree_internal_locate_compare_to, + btree_internal_locate_plain_compare>::type internal_locate_type; + + enum { + kNodeValues = node_type::kNodeValues, + kMinNodeValues = kNodeValues / 2, + kValueSize = node_type::kValueSize, + kExactMatch = node_type::kExactMatch, + kMatchMask = node_type::kMatchMask, + }; + + // A helper class to get the empty base class optimization for 0-size + // allocators. Base is internal_allocator_type. + // (e.g. empty_base_handle). If Base is + // 0-size, the compiler doesn't have to reserve any space for it and + // sizeof(empty_base_handle) will simply be sizeof(Data). Google [empty base + // class optimization] for more details. + template + struct empty_base_handle : public Base { + empty_base_handle(const Base &b, const Data &d) + : Base(b), + data(d) { + } + Data data; + }; + + struct node_stats { + node_stats(ssize_t l, ssize_t i) + : leaf_nodes(l), + internal_nodes(i) { + } + + node_stats& operator+=(const node_stats &x) { + leaf_nodes += x.leaf_nodes; + internal_nodes += x.internal_nodes; + return *this; + } + + ssize_t leaf_nodes; + ssize_t internal_nodes; + }; + + public: + typedef Params params_type; + typedef typename Params::key_type key_type; + typedef typename Params::data_type data_type; + typedef typename Params::mapped_type mapped_type; + typedef typename Params::value_type value_type; + typedef typename Params::key_compare key_compare; + typedef typename Params::pointer pointer; + typedef typename Params::const_pointer const_pointer; + typedef typename Params::reference reference; + typedef typename Params::const_reference const_reference; + typedef typename Params::size_type size_type; + typedef typename Params::difference_type difference_type; + typedef btree_iterator iterator; + typedef typename iterator::const_iterator const_iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef std::reverse_iterator reverse_iterator; + + typedef typename Params::allocator_type allocator_type; + typedef typename allocator_type::template rebind::other + internal_allocator_type; + + public: + // Default constructor. + btree(const key_compare &comp, const allocator_type &alloc); + + // Copy constructor. + btree(const self_type &x); + + // Destructor. + ~btree() { + clear(); + } + + // Iterator routines. + iterator begin() { + return iterator(leftmost(), 0); + } + const_iterator begin() const { + return const_iterator(leftmost(), 0); + } + iterator end() { + return iterator(rightmost(), rightmost() ? rightmost()->count() : 0); + } + const_iterator end() const { + return const_iterator(rightmost(), rightmost() ? rightmost()->count() : 0); + } + reverse_iterator rbegin() { + return reverse_iterator(end()); + } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + reverse_iterator rend() { + return reverse_iterator(begin()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + // Finds the first element whose key is not less than key. + iterator lower_bound(const key_type &key) { + return internal_end( + internal_lower_bound(key, iterator(root(), 0))); + } + const_iterator lower_bound(const key_type &key) const { + return internal_end( + internal_lower_bound(key, const_iterator(root(), 0))); + } + + // Finds the first element whose key is greater than key. + iterator upper_bound(const key_type &key) { + return internal_end( + internal_upper_bound(key, iterator(root(), 0))); + } + const_iterator upper_bound(const key_type &key) const { + return internal_end( + internal_upper_bound(key, const_iterator(root(), 0))); + } + + // Finds the range of values which compare equal to key. The first member of + // the returned pair is equal to lower_bound(key). The second member pair of + // the pair is equal to upper_bound(key). + pair equal_range(const key_type &key) { + return make_pair(lower_bound(key), upper_bound(key)); + } + pair equal_range(const key_type &key) const { + return make_pair(lower_bound(key), upper_bound(key)); + } + + // Inserts a value into the btree only if it does not already exist. The + // boolean return value indicates whether insertion succeeded or failed. The + // ValuePointer type is used to avoid instatiating the value unless the key + // is being inserted. Value is not dereferenced if the key already exists in + // the btree. See btree_map::operator[]. + template + pair insert_unique(const key_type &key, ValuePointer value); + + // Inserts a value into the btree only if it does not already exist. The + // boolean return value indicates whether insertion succeeded or failed. + pair insert_unique(const value_type &v) { + return insert_unique(params_type::key(v), &v); + } + + // Insert with hint. Check to see if the value should be placed immediately + // before position in the tree. If it does, then the insertion will take + // amortized constant time. If not, the insertion will take amortized + // logarithmic time as if a call to insert_unique(v) were made. + iterator insert_unique(iterator position, const value_type &v); + + // Insert a range of values into the btree. + template + void insert_unique(InputIterator b, InputIterator e); + + // Inserts a value into the btree. The ValuePointer type is used to avoid + // instatiating the value unless the key is being inserted. Value is not + // dereferenced if the key already exists in the btree. See + // btree_map::operator[]. + template + iterator insert_multi(const key_type &key, ValuePointer value); + + // Inserts a value into the btree. + iterator insert_multi(const value_type &v) { + return insert_multi(params_type::key(v), &v); + } + + // Insert with hint. Check to see if the value should be placed immediately + // before position in the tree. If it does, then the insertion will take + // amortized constant time. If not, the insertion will take amortized + // logarithmic time as if a call to insert_multi(v) were made. + iterator insert_multi(iterator position, const value_type &v); + + // Insert a range of values into the btree. + template + void insert_multi(InputIterator b, InputIterator e); + + void assign(const self_type &x); + + // Erase the specified iterator from the btree. The iterator must be valid + // (i.e. not equal to end()). Return an iterator pointing to the node after + // the one that was erased (or end() if none exists). + iterator erase(iterator iter); + + // Erases range. Returns the number of keys erased. + int erase(iterator begin, iterator end); + + // Erases the specified key from the btree. Returns 1 if an element was + // erased and 0 otherwise. + int erase_unique(const key_type &key); + + // Erases all of the entries matching the specified key from the + // btree. Returns the number of elements erased. + int erase_multi(const key_type &key); + + // Finds the iterator corresponding to a key or returns end() if the key is + // not present. + iterator find_unique(const key_type &key) { + return internal_end( + internal_find_unique(key, iterator(root(), 0))); + } + const_iterator find_unique(const key_type &key) const { + return internal_end( + internal_find_unique(key, const_iterator(root(), 0))); + } + iterator find_multi(const key_type &key) { + return internal_end( + internal_find_multi(key, iterator(root(), 0))); + } + const_iterator find_multi(const key_type &key) const { + return internal_end( + internal_find_multi(key, const_iterator(root(), 0))); + } + + // Returns a count of the number of times the key appears in the btree. + size_type count_unique(const key_type &key) const { + const_iterator begin = internal_find_unique( + key, const_iterator(root(), 0)); + if (!begin.node) { + // The key doesn't exist in the tree. + return 0; + } + return 1; + } + // Returns a count of the number of times the key appears in the btree. + size_type count_multi(const key_type &key) const { + return distance(lower_bound(key), upper_bound(key)); + } + + // Clear the btree, deleting all of the values it contains. + void clear(); + + // Swap the contents of *this and x. + void swap(self_type &x); + + // Assign the contents of x to *this. + self_type& operator=(const self_type &x) { + if (&x == this) { + // Don't copy onto ourselves. + return *this; + } + assign(x); + return *this; + } + + key_compare* mutable_key_comp() { + return this; + } + const key_compare& key_comp() const { + return *this; + } + bool compare_keys(const key_type &x, const key_type &y) const { + return btree_compare_keys(key_comp(), x, y); + } + + // Dump the btree to the specified ostream. Requires that operator<< is + // defined for Key and Value. + void dump(ostream &os) const { + if (root() != NULL) { + internal_dump(os, root(), 0); + } + } + + // Verifies the structure of the btree. + void verify() const; + + // Size routines. Note that empty() is slightly faster than doing size()==0. + size_type size() const { + if (empty()) return 0; + if (root()->leaf()) return root()->count(); + return root()->size(); + } + size_type max_size() const { return numeric_limits::max(); } + bool empty() const { return root() == NULL; } + + // The height of the btree. An empty tree will have height 0. + size_type height() const { + size_type h = 0; + if (root()) { + // Count the length of the chain from the leftmost node up to the + // root. We actually count from the root back around to the level below + // the root, but the calculation is the same because of the circularity + // of that traversal. + const node_type *n = root(); + do { + ++h; + n = n->parent(); + } while (n != root()); + } + return h; + } + + // The number of internal, leaf and total nodes used by the btree. + size_type leaf_nodes() const { + return internal_stats(root()).leaf_nodes; + } + size_type internal_nodes() const { + return internal_stats(root()).internal_nodes; + } + size_type nodes() const { + node_stats stats = internal_stats(root()); + return stats.leaf_nodes + stats.internal_nodes; + } + + // The total number of bytes used by the btree. + size_type bytes_used() const { + node_stats stats = internal_stats(root()); + if (stats.leaf_nodes == 1 && stats.internal_nodes == 0) { + return sizeof(*this) + + sizeof(base_fields) + root()->max_count() * sizeof(value_type); + } else { + return sizeof(*this) + + sizeof(root_fields) - sizeof(internal_fields) + + stats.leaf_nodes * sizeof(leaf_fields) + + stats.internal_nodes * sizeof(internal_fields); + } + } + + // The average number of bytes used per value stored in the btree. + static double average_bytes_per_value() { + // Returns the number of bytes per value on a leaf node that is 75% + // full. Experimentally, this matches up nicely with the computed number of + // bytes per value in trees that had their values inserted in random order. + return sizeof(leaf_fields) / (kNodeValues * 0.75); + } + + // The fullness of the btree. Computed as the number of elements in the btree + // divided by the maximum number of elements a tree with the current number + // of nodes could hold. A value of 1 indicates perfect space + // utilization. Smaller values indicate space wastage. + double fullness() const { + return double(size()) / (nodes() * kNodeValues); + } + // The overhead of the btree structure in bytes per node. Computed as the + // total number of bytes used by the btree minus the number of bytes used for + // storing elements divided by the number of elements. + double overhead() const { + if (empty()) { + return 0.0; + } + return (bytes_used() - size() * kValueSize) / double(size()); + } + + private: + // Internal accessor routines. + node_type* root() { return root_.data; } + const node_type* root() const { return root_.data; } + node_type** mutable_root() { return &root_.data; } + + // The rightmost node is stored in the root node. + node_type* rightmost() { + return (!root() || root()->leaf()) ? root() : root()->rightmost(); + } + const node_type* rightmost() const { + return (!root() || root()->leaf()) ? root() : root()->rightmost(); + } + node_type** mutable_rightmost() { return root()->mutable_rightmost(); } + + // The leftmost node is stored as the parent of the root node. + node_type* leftmost() { return root() ? root()->parent() : NULL; } + const node_type* leftmost() const { return root() ? root()->parent() : NULL; } + + // The size of the tree is stored in the root node. + size_type* mutable_size() { return root()->mutable_size(); } + + // Allocator routines. + internal_allocator_type* mutable_internal_allocator() { + return static_cast(&root_); + } + const internal_allocator_type& internal_allocator() const { + return *static_cast(&root_); + } + + // Node creation/deletion routines. + node_type* new_internal_node(node_type *parent) { + internal_fields *p = reinterpret_cast( + mutable_internal_allocator()->allocate(sizeof(internal_fields))); + return node_type::init_internal(p, parent); + } + node_type* new_internal_root_node() { + root_fields *p = reinterpret_cast( + mutable_internal_allocator()->allocate(sizeof(root_fields))); + return node_type::init_root(p, root()->parent()); + } + node_type* new_leaf_node(node_type *parent) { + leaf_fields *p = reinterpret_cast( + mutable_internal_allocator()->allocate(sizeof(leaf_fields))); + return node_type::init_leaf(p, parent, kNodeValues); + } + node_type* new_leaf_root_node(int max_count) { + leaf_fields *p = reinterpret_cast( + mutable_internal_allocator()->allocate( + sizeof(base_fields) + max_count * sizeof(value_type))); + return node_type::init_leaf(p, reinterpret_cast(p), max_count); + } + void delete_internal_node(node_type *node) { + node->destroy(); + DCHECK(node != root()); + mutable_internal_allocator()->deallocate( + reinterpret_cast(node), sizeof(internal_fields)); + } + void delete_internal_root_node() { + root()->destroy(); + mutable_internal_allocator()->deallocate( + reinterpret_cast(root()), sizeof(root_fields)); + } + void delete_leaf_node(node_type *node) { + node->destroy(); + mutable_internal_allocator()->deallocate( + reinterpret_cast(node), + sizeof(base_fields) + node->max_count() * sizeof(value_type)); + } + + // Rebalances or splits the node iter points to. + void rebalance_or_split(iterator *iter); + + // Merges the values of left, right and the delimiting key on their parent + // onto left, removing the delimiting key and deleting right. + void merge_nodes(node_type *left, node_type *right); + + // Tries to merge node with its left or right sibling, and failing that, + // rebalance with its left or right sibling. Returns true if a merge + // occurred, at which point it is no longer valid to access node. Returns + // false if no merging took place. + bool try_merge_or_rebalance(iterator *iter); + + // Tries to shrink the height of the tree by 1. + void try_shrink(); + + iterator internal_end(iterator iter) { + return iter.node ? iter : end(); + } + const_iterator internal_end(const_iterator iter) const { + return iter.node ? iter : end(); + } + + // Inserts a value into the btree immediately before iter. Requires that + // key(v) <= iter.key() and (--iter).key() <= key(v). + iterator internal_insert(iterator iter, const value_type &v); + + // Returns an iterator pointing to the first value >= the value "iter" is + // pointing at. Note that "iter" might be pointing to an invalid location as + // iter.position == iter.node->count(). This routine simply moves iter up in + // the tree to a valid location. + template + static IterType internal_last(IterType iter); + + // Returns an iterator pointing to the leaf position at which key would + // reside in the tree. We provide 2 versions of internal_locate. The first + // version (internal_locate_plain_compare) always returns 0 for the second + // field of the pair. The second version (internal_locate_compare_to) is for + // the key-compare-to specialization and returns either kExactMatch (if the + // key was found in the tree) or -kExactMatch (if it wasn't) in the second + // field of the pair. The compare_to specialization allows the caller to + // avoid a subsequent comparison to determine if an exact match was made, + // speeding up string, cord and StringPiece keys. + template + pair internal_locate( + const key_type &key, IterType iter) const; + template + pair internal_locate_plain_compare( + const key_type &key, IterType iter) const; + template + pair internal_locate_compare_to( + const key_type &key, IterType iter) const; + + // Internal routine which implements lower_bound(). + template + IterType internal_lower_bound( + const key_type &key, IterType iter) const; + + // Internal routine which implements upper_bound(). + template + IterType internal_upper_bound( + const key_type &key, IterType iter) const; + + // Internal routine which implements find_unique(). + template + IterType internal_find_unique( + const key_type &key, IterType iter) const; + + // Internal routine which implements find_multi(). + template + IterType internal_find_multi( + const key_type &key, IterType iter) const; + + // Deletes a node and all of its children. + void internal_clear(node_type *node); + + // Dumps a node and all of its children to the specified ostream. + void internal_dump(ostream &os, const node_type *node, int level) const; + + // Verifies the tree structure of node. + int internal_verify(const node_type *node, + const key_type *lo, const key_type *hi) const; + + node_stats internal_stats(const node_type *node) const { + if (!node) { + return node_stats(0, 0); + } + if (node->leaf()) { + return node_stats(1, 0); + } + node_stats res(0, 1); + for (int i = 0; i <= node->count(); ++i) { + res += internal_stats(node->child(i)); + } + return res; + } + + private: + empty_base_handle root_; + + private: + // A never instantiated helper function that returns base::big_ if we have a + // key-compare-to functor or if R is bool and base::small_ otherwise. + template + static typename base::if_< + base::if_, + base::type_equals_ >::type::value, + base::big_, base::small_>::type key_compare_checker(R); + + // A never instantiated helper function that returns the key comparison + // functor. + static key_compare key_compare_helper(); + + // Verify that key_compare returns a bool. This is similar to the way + // is_convertible in base/type_traits.h works. Note that key_compare_checker + // is never actually invoked. The compiler will select which + // key_compare_checker() to instantiate and then figure out the size of the + // return type of key_compare_checker() at compile time which we then check + // against the sizeof of base::big_. + COMPILE_ASSERT( + sizeof(key_compare_checker(key_compare_helper()(key_type(), key_type()))) == + sizeof(base::big_), + key_comparison_function_must_return_bool); +}; + +//// +// btree_node methods +template +inline void btree_node

::insert_value(int i, const value_type &x) { + DCHECK_LE(i, count()); + value_init(count(), x); + for (int j = count(); j > i; --j) { + value_swap(j, this, j - 1); + } + set_count(count() + 1); + + if (!leaf()) { + ++i; + for (int j = count(); j > i; --j) { + *mutable_child(j) = child(j - 1); + child(j)->set_position(j); + } + *mutable_child(i) = NULL; + } +} + +template +inline void btree_node

::remove_value(int i) { + if (!leaf()) { + DCHECK_EQ(child(i + 1)->count(), 0); + for (int j = i + 1; j < count(); ++j) { + *mutable_child(j) = child(j + 1); + child(j)->set_position(j); + } + *mutable_child(count()) = NULL; + } + + set_count(count() - 1); + for (; i < count(); ++i) { + value_swap(i, this, i + 1); + } + value_destroy(i); +} + +template +void btree_node

::rebalance_right_to_left(btree_node *src, int to_move) { + DCHECK_EQ(parent(), src->parent()); + DCHECK_EQ(position() + 1, src->position()); + DCHECK_GE(src->count(), count()); + DCHECK_GE(to_move, 1); + DCHECK_LE(to_move, src->count()); + + // Make room in the left node for the new values. + for (int i = 0; i < to_move; ++i) { + value_init(i + count()); + } + + // Move the delimiting value to the left node and the new delimiting value + // from the right node. + value_swap(count(), parent(), position()); + parent()->value_swap(position(), src, to_move - 1); + + // Move the values from the right to the left node. + for (int i = 1; i < to_move; ++i) { + value_swap(count() + i, src, i - 1); + } + // Shift the values in the right node to their correct position. + for (int i = to_move; i < src->count(); ++i) { + src->value_swap(i - to_move, src, i); + } + for (int i = 1; i <= to_move; ++i) { + src->value_destroy(src->count() - i); + } + + if (!leaf()) { + // Move the child pointers from the right to the left node. + for (int i = 0; i < to_move; ++i) { + set_child(1 + count() + i, src->child(i)); + } + for (int i = 0; i <= src->count() - to_move; ++i) { + DCHECK_LE(i + to_move, src->max_count()); + src->set_child(i, src->child(i + to_move)); + *src->mutable_child(i + to_move) = NULL; + } + } + + // Fixup the counts on the src and dest nodes. + set_count(count() + to_move); + src->set_count(src->count() - to_move); +} + +template +void btree_node

::rebalance_left_to_right(btree_node *dest, int to_move) { + DCHECK_EQ(parent(), dest->parent()); + DCHECK_EQ(position() + 1, dest->position()); + DCHECK_GE(count(), dest->count()); + DCHECK_GE(to_move, 1); + DCHECK_LE(to_move, count()); + + // Make room in the right node for the new values. + for (int i = 0; i < to_move; ++i) { + dest->value_init(i + dest->count()); + } + for (int i = dest->count() - 1; i >= 0; --i) { + dest->value_swap(i, dest, i + to_move); + } + + // Move the delimiting value to the right node and the new delimiting value + // from the left node. + dest->value_swap(to_move - 1, parent(), position()); + parent()->value_swap(position(), this, count() - to_move); + value_destroy(count() - to_move); + + // Move the values from the left to the right node. + for (int i = 1; i < to_move; ++i) { + value_swap(count() - to_move + i, dest, i - 1); + value_destroy(count() - to_move + i); + } + + if (!leaf()) { + // Move the child pointers from the left to the right node. + for (int i = dest->count(); i >= 0; --i) { + dest->set_child(i + to_move, dest->child(i)); + *dest->mutable_child(i) = NULL; + } + for (int i = 1; i <= to_move; ++i) { + dest->set_child(i - 1, child(count() - to_move + i)); + *mutable_child(count() - to_move + i) = NULL; + } + } + + // Fixup the counts on the src and dest nodes. + set_count(count() - to_move); + dest->set_count(dest->count() + to_move); +} + +template +void btree_node

::split(btree_node *dest, int insert_position) { + DCHECK_EQ(dest->count(), 0); + + // We bias the split based on the position being inserted. If we're + // inserting at the beginning of the left node then bias the split to put + // more values on the right node. If we're inserting at the end of the + // right node then bias the split to put more values on the left node. + if (insert_position == 0) { + dest->set_count(count() - 1); + } else if (insert_position == max_count()) { + dest->set_count(0); + } else { + dest->set_count(count() / 2); + } + set_count(count() - dest->count()); + DCHECK_GE(count(), 1); + + // Move values from the left sibling to the right sibling. + for (int i = 0; i < dest->count(); ++i) { + dest->value_init(i); + value_swap(count() + i, dest, i); + value_destroy(count() + i); + } + + // The split key is the largest value in the left sibling. + set_count(count() - 1); + parent()->insert_value(position(), value_type()); + value_swap(count(), parent(), position()); + value_destroy(count()); + parent()->set_child(position() + 1, dest); + + if (!leaf()) { + for (int i = 0; i <= dest->count(); ++i) { + DCHECK(child(count() + i + 1) != NULL); + dest->set_child(i, child(count() + i + 1)); + *mutable_child(count() + i + 1) = NULL; + } + } +} + +template +void btree_node

::merge(btree_node *src) { + DCHECK_EQ(parent(), src->parent()); + DCHECK_EQ(position() + 1, src->position()); + + // Move the delimiting value to the left node. + value_init(count()); + value_swap(count(), parent(), position()); + + // Move the values from the right to the left node. + for (int i = 0; i < src->count(); ++i) { + value_init(1 + count() + i); + value_swap(1 + count() + i, src, i); + src->value_destroy(i); + } + + if (!leaf()) { + // Move the child pointers from the right to the left node. + for (int i = 0; i <= src->count(); ++i) { + set_child(1 + count() + i, src->child(i)); + *src->mutable_child(i) = NULL; + } + } + + // Fixup the counts on the src and dest nodes. + set_count(1 + count() + src->count()); + src->set_count(0); + + // Remove the value on the parent node. + parent()->remove_value(position()); +} + +template +void btree_node

::swap(btree_node *x) { + DCHECK_EQ(leaf(), x->leaf()); + + // Swap the values. + for (int i = count(); i < x->count(); ++i) { + value_init(i); + } + for (int i = x->count(); i < count(); ++i) { + x->value_init(i); + } + int n = max(count(), x->count()); + for (int i = 0; i < n; ++i) { + value_swap(i, x, i); + } + for (int i = count(); i < x->count(); ++i) { + x->value_destroy(i); + } + for (int i = x->count(); i < count(); ++i) { + value_destroy(i); + } + + if (!leaf()) { + // Swap the child pointers. + for (int i = 0; i <= n; ++i) { + btree_swap_helper(*mutable_child(i), *x->mutable_child(i)); + } + for (int i = 0; i <= count(); ++i) { + x->child(i)->fields_.parent = x; + } + for (int i = 0; i <= x->count(); ++i) { + child(i)->fields_.parent = this; + } + } + + // Swap the counts. + btree_swap_helper(fields_.count, x->fields_.count); +} + +//// +// btree_iterator methods +template +void btree_iterator::increment_slow() { + if (node->leaf()) { + DCHECK_GE(position, node->count()); + self_type save(*this); + while (position == node->count() && !node->is_root()) { + DCHECK_EQ(node->parent()->child(node->position()), node); + position = node->position(); + node = node->parent(); + } + if (position == node->count()) { + *this = save; + } + } else { + DCHECK_LT(position, node->count()); + node = node->child(position + 1); + while (!node->leaf()) { + node = node->child(0); + } + position = 0; + } +} + +template +void btree_iterator::increment_by(int count) { + while (count > 0) { + if (node->leaf()) { + int rest = node->count() - position; + position += min(rest, count); + count = count - rest; + if (position < node->count()) { + return; + } + } else { + --count; + } + increment_slow(); + } +} + +template +void btree_iterator::decrement_slow() { + if (node->leaf()) { + DCHECK_LE(position, -1); + self_type save(*this); + while (position < 0 && !node->is_root()) { + DCHECK_EQ(node->parent()->child(node->position()), node); + position = node->position() - 1; + node = node->parent(); + } + if (position < 0) { + *this = save; + } + } else { + DCHECK_GE(position, 0); + node = node->child(position); + while (!node->leaf()) { + node = node->child(node->count()); + } + position = node->count() - 1; + } +} + +//// +// btree methods +template +btree

::btree(const key_compare &comp, const allocator_type &alloc) + : key_compare(comp), + root_(alloc, NULL) { +} + +template +btree

::btree(const self_type &x) + : key_compare(x.key_comp()), + root_(x.internal_allocator(), NULL) { + assign(x); +} + +template template +pair::iterator, bool> +btree

::insert_unique(const key_type &key, ValuePointer value) { + if (empty()) { + *mutable_root() = new_leaf_root_node(1); + } + + pair res = internal_locate(key, iterator(root(), 0)); + iterator &iter = res.first; + if (res.second == kExactMatch) { + // The key already exists in the tree, do nothing. + return make_pair(internal_last(iter), false); + } else if (!res.second) { + iterator last = internal_last(iter); + if (last.node && !compare_keys(key, last.key())) { + // The key already exists in the tree, do nothing. + return make_pair(last, false); + } + } + + return make_pair(internal_insert(iter, *value), true); +} + +template +inline typename btree

::iterator +btree

::insert_unique(iterator position, const value_type &v) { + if (!empty()) { + const key_type &key = params_type::key(v); + if (position == end() || compare_keys(key, position.key())) { + iterator prev = position; + if (position == begin() || compare_keys((--prev).key(), key)) { + // prev.key() < key < position.key() + return internal_insert(position, v); + } + } else if (compare_keys(position.key(), key)) { + iterator next = position; + ++next; + if (next == end() || compare_keys(key, next.key())) { + // position.key() < key < next.key() + return internal_insert(next, v); + } + } else { + // position.key() == key + return position; + } + } + return insert_unique(v).first; +} + +template template +void btree

::insert_unique(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert_unique(end(), *b); + } +} + +template template +typename btree

::iterator +btree

::insert_multi(const key_type &key, ValuePointer value) { + if (empty()) { + *mutable_root() = new_leaf_root_node(1); + } + + iterator iter = internal_upper_bound(key, iterator(root(), 0)); + if (!iter.node) { + iter = end(); + } + return internal_insert(iter, *value); +} + +template +typename btree

::iterator +btree

::insert_multi(iterator position, const value_type &v) { + if (!empty()) { + const key_type &key = params_type::key(v); + if (position == end() || !compare_keys(position.key(), key)) { + iterator prev = position; + if (position == begin() || !compare_keys(key, (--prev).key())) { + // prev.key() <= key <= position.key() + return internal_insert(position, v); + } + } else { + iterator next = position; + ++next; + if (next == end() || !compare_keys(next.key(), key)) { + // position.key() < key <= next.key() + return internal_insert(next, v); + } + } + } + return insert_multi(v); +} + +template template +void btree

::insert_multi(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert_multi(end(), *b); + } +} + +template +void btree

::assign(const self_type &x) { + clear(); + + *mutable_key_comp() = x.key_comp(); + *mutable_internal_allocator() = x.internal_allocator(); + + // Assignment can avoid key comparisons because we know the order of the + // values is the same order we'll store them in. + for (const_iterator iter = x.begin(); iter != x.end(); ++iter) { + if (empty()) { + insert_multi(*iter); + } else { + // If the btree is not empty, we can just insert the new value at the end + // of the tree! + internal_insert(end(), *iter); + } + } +} + +template +typename btree

::iterator btree

::erase(iterator iter) { + bool internal_delete = false; + if (!iter.node->leaf()) { + // Deletion of a value on an internal node. Swap the key with the largest + // value of our left child. This is easy, we just decrement iter. + iterator tmp_iter(iter--); + DCHECK(iter.node->leaf()); + DCHECK(!compare_keys(tmp_iter.key(), iter.key())); + iter.node->value_swap(iter.position, tmp_iter.node, tmp_iter.position); + internal_delete = true; + --*mutable_size(); + } else if (!root()->leaf()) { + --*mutable_size(); + } + + // Delete the key from the leaf. + iter.node->remove_value(iter.position); + + // We want to return the next value after the one we just erased. If we + // erased from an internal node (internal_delete == true), then the next + // value is ++(++iter). If we erased from a leaf node (internal_delete == + // false) then the next value is ++iter. Note that ++iter may point to an + // internal node and the value in the internal node may move to a leaf node + // (iter.node) when rebalancing is performed at the leaf level. + + // Merge/rebalance as we walk back up the tree. + iterator res(iter); + for (;;) { + if (iter.node == root()) { + try_shrink(); + if (empty()) { + return end(); + } + break; + } + if (iter.node->count() >= kMinNodeValues) { + break; + } + bool merged = try_merge_or_rebalance(&iter); + if (iter.node->leaf()) { + res = iter; + } + if (!merged) { + break; + } + iter.node = iter.node->parent(); + } + + // Adjust our return value. If we're pointing at the end of a node, advance + // the iterator. + if (res.position == res.node->count()) { + res.position = res.node->count() - 1; + ++res; + } + // If we erased from an internal node, advance the iterator. + if (internal_delete) { + ++res; + } + return res; +} + +template +int btree

::erase(iterator begin, iterator end) { + int count = distance(begin, end); + for (int i = 0; i < count; i++) { + begin = erase(begin); + } + return count; +} + +template +int btree

::erase_unique(const key_type &key) { + iterator iter = internal_find_unique(key, iterator(root(), 0)); + if (!iter.node) { + // The key doesn't exist in the tree, return nothing done. + return 0; + } + erase(iter); + return 1; +} + +template +int btree

::erase_multi(const key_type &key) { + iterator begin = internal_lower_bound(key, iterator(root(), 0)); + if (!begin.node) { + // The key doesn't exist in the tree, return nothing done. + return 0; + } + // Delete all of the keys between begin and upper_bound(key). + iterator end = internal_end( + internal_upper_bound(key, iterator(root(), 0))); + return erase(begin, end); +} + +template +void btree

::clear() { + if (root() != NULL) { + internal_clear(root()); + } + *mutable_root() = NULL; +} + +template +void btree

::swap(self_type &x) { + ::swap(static_cast(*this), static_cast(x)); + ::swap(root_, x.root_); +} + +template +void btree

::verify() const { + if (root() != NULL) { + CHECK_EQ(size(), internal_verify(root(), NULL, NULL)); + CHECK_EQ(leftmost(), (++const_iterator(root(), -1)).node); + CHECK_EQ(rightmost(), (--const_iterator(root(), root()->count())).node); + CHECK(leftmost()->leaf()); + CHECK(rightmost()->leaf()); + } else { + CHECK_EQ(size(), 0); + CHECK(leftmost() == NULL); + CHECK(rightmost() == NULL); + } +} + +template +void btree

::rebalance_or_split(iterator *iter) { + node_type *&node = iter->node; + int &insert_position = iter->position; + DCHECK_EQ(node->count(), node->max_count()); + + // First try to make room on the node by rebalancing. + node_type *parent = node->parent(); + if (node != root()) { + if (node->position() > 0) { + // Try rebalancing with our left sibling. + node_type *left = parent->child(node->position() - 1); + if (left->count() < left->max_count()) { + // We bias rebalancing based on the position being inserted. If we're + // inserting at the end of the right node then we bias rebalancing to + // fill up the left node. + int to_move = (left->max_count() - left->count()) / + (1 + (insert_position < left->max_count())); + to_move = max(1, to_move); + + if (((insert_position - to_move) >= 0) || + ((left->count() + to_move) < left->max_count())) { + left->rebalance_right_to_left(node, to_move); + + DCHECK_EQ(node->max_count() - node->count(), to_move); + insert_position = insert_position - to_move; + if (insert_position < 0) { + insert_position = insert_position + left->count() + 1; + node = left; + } + + DCHECK_LT(node->count(), node->max_count()); + return; + } + } + } + + if (node->position() < parent->count()) { + // Try rebalancing with our right sibling. + node_type *right = parent->child(node->position() + 1); + if (right->count() < right->max_count()) { + // We bias rebalancing based on the position being inserted. If we're + // inserting at the beginning of the left node then we bias rebalancing + // to fill up the right node. + int to_move = (right->max_count() - right->count()) / + (1 + (insert_position > 0)); + to_move = max(1, to_move); + + if ((insert_position <= (node->count() - to_move)) || + ((right->count() + to_move) < right->max_count())) { + node->rebalance_left_to_right(right, to_move); + + if (insert_position > node->count()) { + insert_position = insert_position - node->count() - 1; + node = right; + } + + DCHECK_LT(node->count(), node->max_count()); + return; + } + } + } + + // Rebalancing failed, make sure there is room on the parent node for a new + // value. + if (parent->count() == parent->max_count()) { + iterator parent_iter(node->parent(), node->position()); + rebalance_or_split(&parent_iter); + } + } else { + // Rebalancing not possible because this is the root node. + if (root()->leaf()) { + // The root node is currently a leaf node: create a new root node and set + // the current root node as the child of the new root. + parent = new_internal_root_node(); + parent->set_child(0, root()); + *mutable_root() = parent; + DCHECK(*mutable_rightmost() == parent->child(0)); + } else { + // The root node is an internal node. We do not want to create a new root + // node because the root node is special and holds the size of the tree + // and a pointer to the rightmost node. So we create a new internal node + // and move all of the items on the current root into the new node. + parent = new_internal_node(parent); + parent->set_child(0, parent); + parent->swap(root()); + node = parent; + } + } + + // Split the node. + node_type *split_node; + if (node->leaf()) { + split_node = new_leaf_node(parent); + node->split(split_node, insert_position); + if (rightmost() == node) { + *mutable_rightmost() = split_node; + } + } else { + split_node = new_internal_node(parent); + node->split(split_node, insert_position); + } + + if (insert_position > node->count()) { + insert_position = insert_position - node->count() - 1; + node = split_node; + } +} + +template +void btree

::merge_nodes(node_type *left, node_type *right) { + left->merge(right); + if (right->leaf()) { + if (rightmost() == right) { + *mutable_rightmost() = left; + } + delete_leaf_node(right); + } else { + delete_internal_node(right); + } +} + +template +bool btree

::try_merge_or_rebalance(iterator *iter) { + node_type *parent = iter->node->parent(); + if (iter->node->position() > 0) { + // Try merging with our left sibling. + node_type *left = parent->child(iter->node->position() - 1); + if ((1 + left->count() + iter->node->count()) <= left->max_count()) { + iter->position += 1 + left->count(); + merge_nodes(left, iter->node); + iter->node = left; + return true; + } + } + if (iter->node->position() < parent->count()) { + // Try merging with our right sibling. + node_type *right = parent->child(iter->node->position() + 1); + if ((1 + iter->node->count() + right->count()) <= right->max_count()) { + merge_nodes(iter->node, right); + return true; + } + // Try rebalancing with our right sibling. We don't perform rebalancing if + // we deleted the first element from iter->node and the node is not + // empty. This is a small optimization for the common pattern of deleting + // from the front of the tree. + if ((right->count() > kMinNodeValues) && + ((iter->node->count() == 0) || + (iter->position > 0))) { + int to_move = (right->count() - iter->node->count()) / 2; + to_move = min(to_move, right->count() - 1); + iter->node->rebalance_right_to_left(right, to_move); + return false; + } + } + if (iter->node->position() > 0) { + // Try rebalancing with our left sibling. We don't perform rebalancing if + // we deleted the last element from iter->node and the node is not + // empty. This is a small optimization for the common pattern of deleting + // from the back of the tree. + node_type *left = parent->child(iter->node->position() - 1); + if ((left->count() > kMinNodeValues) && + ((iter->node->count() == 0) || + (iter->position < iter->node->count()))) { + int to_move = (left->count() - iter->node->count()) / 2; + to_move = min(to_move, left->count() - 1); + left->rebalance_left_to_right(iter->node, to_move); + iter->position += to_move; + return false; + } + } + return false; +} + +template +void btree

::try_shrink() { + if (root()->count() > 0) { + return; + } + // Deleted the last item on the root node, shrink the height of the tree. + if (root()->leaf()) { + DCHECK_EQ(size(), 0); + delete_leaf_node(root()); + *mutable_root() = NULL; + } else { + node_type *child = root()->child(0); + if (child->leaf()) { + // The child is a leaf node so simply make it the root node in the tree. + child->make_root(); + delete_internal_root_node(); + *mutable_root() = child; + } else { + // The child is an internal node. We want to keep the existing root node + // so we move all of the values from the child node into the existing + // (empty) root node. + child->swap(root()); + delete_internal_node(child); + } + } +} + +template template +inline IterType btree

::internal_last(IterType iter) { + while (iter.node && iter.position == iter.node->count()) { + iter.position = iter.node->position(); + iter.node = iter.node->parent(); + if (iter.node->leaf()) { + iter.node = NULL; + } + } + return iter; +} + +template +inline typename btree

::iterator +btree

::internal_insert(iterator iter, const value_type &v) { + if (!iter.node->leaf()) { + // We can't insert on an internal node. Instead, we'll insert after the + // previous value which is guaranteed to be on a leaf node. + --iter; + ++iter.position; + } + if (iter.node->count() == iter.node->max_count()) { + // Make room in the leaf for the new item. + if (iter.node->max_count() < kNodeValues) { + // Insertion into the root where the root is smaller that the full node + // size. Simply grow the size of the root node. + DCHECK(iter.node == root()); + iter.node = new_leaf_root_node( + min(kNodeValues, 2 * iter.node->max_count())); + iter.node->swap(root()); + delete_leaf_node(root()); + *mutable_root() = iter.node; + } else { + rebalance_or_split(&iter); + ++*mutable_size(); + } + } else if (!root()->leaf()) { + ++*mutable_size(); + } + iter.node->insert_value(iter.position, v); + return iter; +} + +template template +inline pair btree

::internal_locate( + const key_type &key, IterType iter) const { + return internal_locate_type::dispatch(key, *this, iter); +} + +template template +inline pair btree

::internal_locate_plain_compare( + const key_type &key, IterType iter) const { + for (;;) { + iter.position = iter.node->lower_bound(key, key_comp()); + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return make_pair(iter, 0); +} + +template template +inline pair btree

::internal_locate_compare_to( + const key_type &key, IterType iter) const { + for (;;) { + int res = iter.node->lower_bound(key, key_comp()); + iter.position = res & kMatchMask; + if (res & kExactMatch) { + return make_pair(iter, static_cast(kExactMatch)); + } + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + return make_pair(iter, -kExactMatch); +} + +template template +IterType btree

::internal_lower_bound( + const key_type &key, IterType iter) const { + if (iter.node) { + for (;;) { + iter.position = + iter.node->lower_bound(key, key_comp()) & kMatchMask; + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + iter = internal_last(iter); + } + return iter; +} + +template template +IterType btree

::internal_upper_bound( + const key_type &key, IterType iter) const { + if (iter.node) { + for (;;) { + iter.position = iter.node->upper_bound(key, key_comp()); + if (iter.node->leaf()) { + break; + } + iter.node = iter.node->child(iter.position); + } + iter = internal_last(iter); + } + return iter; +} + +template template +IterType btree

::internal_find_unique( + const key_type &key, IterType iter) const { + if (iter.node) { + pair res = internal_locate(key, iter); + if (res.second == kExactMatch) { + return res.first; + } + if (!res.second) { + iter = internal_last(res.first); + if (iter.node && !compare_keys(key, iter.key())) { + return iter; + } + } + } + return IterType(NULL, 0); +} + +template template +IterType btree

::internal_find_multi( + const key_type &key, IterType iter) const { + if (iter.node) { + iter = internal_lower_bound(key, iter); + if (iter.node) { + iter = internal_last(iter); + if (iter.node && !compare_keys(key, iter.key())) { + return iter; + } + } + } + return IterType(NULL, 0); +} + +template +void btree

::internal_clear(node_type *node) { + if (!node->leaf()) { + for (int i = 0; i <= node->count(); ++i) { + internal_clear(node->child(i)); + } + if (node == root()) { + delete_internal_root_node(); + } else { + delete_internal_node(node); + } + } else { + delete_leaf_node(node); + } +} + +template +void btree

::internal_dump( + ostream &os, const node_type *node, int level) const { + for (int i = 0; i < node->count(); ++i) { + if (!node->leaf()) { + internal_dump(os, node->child(i), level + 1); + } + for (int j = 0; j < level; ++j) { + os << " "; + } + os << node->key(i) << " [" << level << "]\n"; + } + if (!node->leaf()) { + internal_dump(os, node->child(node->count()), level + 1); + } +} + +template +int btree

::internal_verify( + const node_type *node, const key_type *lo, const key_type *hi) const { + CHECK_GT(node->count(), 0); + CHECK_LE(node->count(), node->max_count()); + if (lo) { + CHECK(!compare_keys(node->key(0), *lo)); + } + if (hi) { + CHECK(!compare_keys(*hi, node->key(node->count() - 1))); + } + for (int i = 1; i < node->count(); ++i) { + CHECK(!compare_keys(node->key(i), node->key(i - 1))); + } + int count = node->count(); + if (!node->leaf()) { + for (int i = 0; i <= node->count(); ++i) { + CHECK(node->child(i) != NULL); + CHECK_EQ(node->child(i)->parent(), node); + CHECK_EQ(node->child(i)->position(), i); + count += internal_verify( + node->child(i), + (i == 0) ? lo : &node->key(i - 1), + (i == node->count()) ? hi : &node->key(i)); + } + } + return count; +} + +} // namespace btree +} // namespace util + +DEFINE_GDB_AUTO_SCRIPT("util/btree/btree_printer.py") + +#endif // UTIL_BTREE_BTREE_H__ diff --git a/btree_bench.cc b/btree_bench.cc new file mode 100644 index 00000000..c67dd664 --- /dev/null +++ b/btree_bench.cc @@ -0,0 +1,483 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: jmacd@google.com (Josh MacDonald) +// Author: pmattis@google.com (Peter Mattis) + +#include +#include +#include +#include +#include +#include +#include + +#include "base/commandlineflags.h" +#include "base/init_google.h" +#include "base/integral_types.h" +#include "base/logging.h" +#include "base/stringprintf.h" +#include "base/type_traits.h" +#include "strings/cord.h" +#include "testing/base/public/benchmark.h" +#include "testing/base/public/googletest.h" +#include "util/btree/btree_map.h" +#include "util/btree/btree_set.h" +#include "util/btree/btree_test.h" +#include "util/random/acmrandom.h" + +DECLARE_int32(benchmark_max_iters); + +namespace util { +namespace btree { +namespace { + +// Benchmark insertion of values into a container. +template +void BM_Insert(int n) { + typedef typename base::remove_const::type V; + typename KeyOfValue::type key_of_value; + + // Disable timing while we perform some initialization. + StopBenchmarkTiming(); + + T container; + vector values = GenerateValues(FLAGS_benchmark_values); + for (int i = 0; i < values.size(); i++) { + container.insert(values[i]); + } + SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); + + for (int i = 0; i < n; ) { + // Remove and re-insert 10% of the keys + int m = min(n - i, FLAGS_benchmark_values / 10); + + for (int j = i; j < i + m; j++) { + int x = j % FLAGS_benchmark_values; + container.erase(key_of_value(values[x])); + } + + StartBenchmarkTiming(); + + for (int j = i; j < i + m; j++) { + int x = j % FLAGS_benchmark_values; + container.insert(values[x]); + } + + StopBenchmarkTiming(); + + i += m; + } +} + +// Benchmark lookup of values in a container. +template +void BM_Lookup(int n) { + typedef typename base::remove_const::type V; + typename KeyOfValue::type key_of_value; + + // Disable timing while we perform some initialization. + StopBenchmarkTiming(); + + T container; + vector values = GenerateValues(FLAGS_benchmark_values); + + for (int i = 0; i < values.size(); i++) { + container.insert(values[i]); + } + SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); + + V r = V(); + + StartBenchmarkTiming(); + + for (int i = 0; i < n; i++) { + int m = i % values.size(); + r = *container.find(key_of_value(values[m])); + } + + StopBenchmarkTiming(); + + VLOG(4) << r; // Keep compiler from optimizing away r. +} + +// Benchmark lookup of values in a full container, meaning that values +// are inserted in-order to take advantage of biased insertion, which +// yields a full tree. +template +void BM_FullLookup(int n) { + typedef typename base::remove_const::type V; + typename KeyOfValue::type key_of_value; + + // Disable timing while we perform some initialization. + StopBenchmarkTiming(); + + T container; + vector values = GenerateValues(FLAGS_benchmark_values); + vector sorted(values); + sort(sorted.begin(), sorted.end()); + + for (int i = 0; i < sorted.size(); i++) { + container.insert(sorted[i]); + } + SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); + + V r = V(); + + StartBenchmarkTiming(); + + for (int i = 0; i < n; i++) { + int m = i % values.size(); + r = *container.find(key_of_value(values[m])); + } + + StopBenchmarkTiming(); + + VLOG(4) << r; // Keep compiler from optimizing away r. +} + +// Benchmark deletion of values from a container. +template +void BM_Delete(int n) { + typedef typename base::remove_const::type V; + typename KeyOfValue::type key_of_value; + + // Disable timing while we perform some initialization. + StopBenchmarkTiming(); + + T container; + vector values = GenerateValues(FLAGS_benchmark_values); + for (int i = 0; i < values.size(); i++) { + container.insert(values[i]); + } + SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); + + for (int i = 0; i < n; ) { + // Remove and re-insert 10% of the keys + int m = min(n - i, FLAGS_benchmark_values / 10); + + StartBenchmarkTiming(); + + for (int j = i; j < i + m; j++) { + int x = j % FLAGS_benchmark_values; + container.erase(key_of_value(values[x])); + } + + StopBenchmarkTiming(); + + for (int j = i; j < i + m; j++) { + int x = j % FLAGS_benchmark_values; + container.insert(values[x]); + } + + i += m; + } +} + +// Benchmark steady-state insert (into first half of range) and remove +// (from second second half of range), treating the container +// approximately like a queue with log-time access for all elements. +// This benchmark does not test the case where insertion and removal +// happen in the same region of the tree. This benchmark counts two +// value constructors. +template +void BM_QueueAddRem(int n) { + typedef typename base::remove_const::type V; + typename KeyOfValue::type key_of_value; + + // Disable timing while we perform some initialization. + StopBenchmarkTiming(); + CHECK(FLAGS_benchmark_values % 2 == 0); + + T container; + + const int half = FLAGS_benchmark_values / 2; + vector remove_keys(half); + vector add_keys(half); + + for (int i = 0; i < half; i++) { + remove_keys[i] = i; + add_keys[i] = i; + } + + ACMRandom rand(FLAGS_test_random_seed); + + random_shuffle(remove_keys.begin(), remove_keys.end(), rand); + random_shuffle(add_keys.begin(), add_keys.end(), rand); + + Generator g(FLAGS_benchmark_values + FLAGS_benchmark_max_iters); + + for (int i = 0; i < half; i++) { + container.insert(g(add_keys[i])); + container.insert(g(half + remove_keys[i])); + } + + // There are three parts each of size "half": + // 1 is being deleted from [offset - half, offset) + // 2 is standing [offset, offset + half) + // 3 is being inserted into [offset + half, offset + 2 * half) + int offset = 0; + + StartBenchmarkTiming(); + + for (int i = 0; i < n; i++) { + int idx = i % half; + + if (idx == 0) { + StopBenchmarkTiming(); + random_shuffle(remove_keys.begin(), remove_keys.end(), rand); + random_shuffle(add_keys.begin(), add_keys.end(), rand); + offset += half; + StartBenchmarkTiming(); + } + + int e = container.erase(key_of_value(g(offset - half + remove_keys[idx]))); + DCHECK(e == 1); + container.insert(g(offset + half + add_keys[idx])); + } + + StopBenchmarkTiming(); + + SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); +} + +// Mixed insertion and deletion in the same range using pre-constructed values. +template +void BM_MixedAddRem(int n) { + typedef typename base::remove_const::type V; + typename KeyOfValue::type key_of_value; + + // Disable timing while we perform some initialization. + StopBenchmarkTiming(); + CHECK(FLAGS_benchmark_values % 2 == 0); + + T container; + ACMRandom rand(FLAGS_test_random_seed); + + vector values = GenerateValues(FLAGS_benchmark_values * 2); + + // Create two random shuffles + vector remove_keys(FLAGS_benchmark_values); + vector add_keys(FLAGS_benchmark_values); + + // Insert the first half of the values (already in random order) + for (int i = 0; i < FLAGS_benchmark_values; i++) { + container.insert(values[i]); + + // remove_keys and add_keys will be swapped before each round, + // therefore fill add_keys here w/ the keys being inserted, so + // they'll be the first to be removed. + remove_keys[i] = i + FLAGS_benchmark_values; + add_keys[i] = i; + } + + StartBenchmarkTiming(); + + for (int i = 0; i < n; i++) { + int idx = i % FLAGS_benchmark_values; + + if (idx == 0) { + StopBenchmarkTiming(); + remove_keys.swap(add_keys); + random_shuffle(remove_keys.begin(), remove_keys.end(), rand); + random_shuffle(add_keys.begin(), add_keys.end(), rand); + StartBenchmarkTiming(); + } + + int e = container.erase(key_of_value(values[remove_keys[idx]])); + DCHECK(e == 1); + container.insert(values[add_keys[idx]]); + } + + StopBenchmarkTiming(); + + SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); +} + +// Insertion at end, removal from the beginning. This benchmark +// counts two value constructors. +template +void BM_Fifo(int n) { + typedef typename base::remove_const::type V; + + // Disable timing while we perform some initialization. + StopBenchmarkTiming(); + + T container; + Generator g(FLAGS_benchmark_values + FLAGS_benchmark_max_iters); + + for (int i = 0; i < FLAGS_benchmark_values; i++) { + container.insert(g(i)); + } + + StartBenchmarkTiming(); + + for (int i = 0; i < n; i++) { + container.erase(container.begin()); + container.insert(container.end(), g(i + FLAGS_benchmark_values)); + } + + StopBenchmarkTiming(); + + SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); +} + +// Iteration (forward) through the tree +template +void BM_FwdIter(int n) { + typedef typename base::remove_const::type V; + + // Disable timing while we perform some initialization. + StopBenchmarkTiming(); + + T container; + vector values = GenerateValues(FLAGS_benchmark_values); + + for (int i = 0; i < FLAGS_benchmark_values; i++) { + container.insert(values[i]); + } + + typename T::iterator iter; + + V r = V(); + + StartBenchmarkTiming(); + + for (int i = 0; i < n; i++) { + int idx = i % FLAGS_benchmark_values; + + if (idx == 0) { + iter = container.begin(); + } + r = *iter; + ++iter; + } + + StopBenchmarkTiming(); + + VLOG(4) << r; // Keep compiler from optimizing away r. + + SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); +} + +typedef set stl_set_int32; +typedef set stl_set_int64; +typedef set stl_set_string; +typedef set stl_set_cord; + +typedef map stl_map_int32; +typedef map stl_map_int64; +typedef map stl_map_string; +typedef map stl_map_cord; + +typedef multiset stl_multiset_int32; +typedef multiset stl_multiset_int64; +typedef multiset stl_multiset_string; +typedef multiset stl_multiset_cord; + +typedef multimap stl_multimap_int32; +typedef multimap stl_multimap_int64; +typedef multimap stl_multimap_string; +typedef multimap stl_multimap_cord; + +#define MY_BENCHMARK_TYPES2(value, name, size) \ + typedef btree ## _set, allocator, size> \ + btree ## _ ## size ## _set_ ## name; \ + typedef btree ## _map, allocator, size> \ + btree ## _ ## size ## _map_ ## name; \ + typedef btree ## _multiset, allocator, size> \ + btree ## _ ## size ## _multiset_ ## name; \ + typedef btree ## _multimap, allocator, size> \ + btree ## _ ## size ## _multimap_ ## name + +#define MY_BENCHMARK_TYPES(value, name) \ + MY_BENCHMARK_TYPES2(value, name, 128); \ + MY_BENCHMARK_TYPES2(value, name, 160); \ + MY_BENCHMARK_TYPES2(value, name, 192); \ + MY_BENCHMARK_TYPES2(value, name, 224); \ + MY_BENCHMARK_TYPES2(value, name, 256); \ + MY_BENCHMARK_TYPES2(value, name, 288); \ + MY_BENCHMARK_TYPES2(value, name, 320); \ + MY_BENCHMARK_TYPES2(value, name, 352); \ + MY_BENCHMARK_TYPES2(value, name, 384); \ + MY_BENCHMARK_TYPES2(value, name, 416); \ + MY_BENCHMARK_TYPES2(value, name, 448); \ + MY_BENCHMARK_TYPES2(value, name, 480); \ + MY_BENCHMARK_TYPES2(value, name, 512) + +MY_BENCHMARK_TYPES(int32, int32); +MY_BENCHMARK_TYPES(int64, int64); +MY_BENCHMARK_TYPES(string, string); +MY_BENCHMARK_TYPES(Cord, cord); + +#define MY_BENCHMARK4(type, name, func) \ + void BM_ ## type ## _ ## name(int n) { BM_ ## func (n); } \ + BENCHMARK(BM_ ## type ## _ ## name) + +// Define NODESIZE_TESTING when running btree_perf.py. You need to do +// a local build or raise the distcc timeout, it takes about 5 minutes +// to build: +// +// blaze build --copts=-DNODESIZE_TESTING --cc_strategy=local +// --compilation_mode=opt util/btree/btree_test + +#ifdef NODESIZE_TESTING +#define MY_BENCHMARK3(tree, type, name, func) \ + MY_BENCHMARK4(tree ## _128_ ## type, name, func); \ + MY_BENCHMARK4(tree ## _160_ ## type, name, func); \ + MY_BENCHMARK4(tree ## _192_ ## type, name, func); \ + MY_BENCHMARK4(tree ## _224_ ## type, name, func); \ + MY_BENCHMARK4(tree ## _256_ ## type, name, func); \ + MY_BENCHMARK4(tree ## _288_ ## type, name, func); \ + MY_BENCHMARK4(tree ## _320_ ## type, name, func); \ + MY_BENCHMARK4(tree ## _352_ ## type, name, func); \ + MY_BENCHMARK4(tree ## _384_ ## type, name, func); \ + MY_BENCHMARK4(tree ## _416_ ## type, name, func); \ + MY_BENCHMARK4(tree ## _448_ ## type, name, func); \ + MY_BENCHMARK4(tree ## _480_ ## type, name, func); \ + MY_BENCHMARK4(tree ## _512_ ## type, name, func) +#else +#define MY_BENCHMARK3(tree, type, name, func) \ + MY_BENCHMARK4(tree ## _256_ ## type, name, func) +#endif + +#define MY_BENCHMARK2(type, name, func) \ + MY_BENCHMARK4(stl_ ## type, name, func); \ + MY_BENCHMARK3(btree, type, name, func) + +#define MY_BENCHMARK(type) \ + MY_BENCHMARK2(type, insert, Insert); \ + MY_BENCHMARK2(type, lookup, Lookup); \ + MY_BENCHMARK2(type, fulllookup, FullLookup); \ + MY_BENCHMARK2(type, delete, Delete); \ + MY_BENCHMARK2(type, queueaddrem, QueueAddRem); \ + MY_BENCHMARK2(type, mixedaddrem, MixedAddRem); \ + MY_BENCHMARK2(type, fifo, Fifo); \ + MY_BENCHMARK2(type, fwditer, FwdIter) + +MY_BENCHMARK(set_int32); +MY_BENCHMARK(map_int32); +MY_BENCHMARK(set_int64); +MY_BENCHMARK(map_int64); +MY_BENCHMARK(set_string); +MY_BENCHMARK(map_string); +MY_BENCHMARK(set_cord); +MY_BENCHMARK(map_cord); + +MY_BENCHMARK(multiset_int32); +MY_BENCHMARK(multimap_int32); +MY_BENCHMARK(multiset_int64); +MY_BENCHMARK(multimap_int64); +MY_BENCHMARK(multiset_string); +MY_BENCHMARK(multimap_string); +MY_BENCHMARK(multiset_cord); +MY_BENCHMARK(multimap_cord); + +} // namespace +} // namespace btree +} // namespace util + +int main(int argc, char **argv) { + FLAGS_logtostderr = true; + InitGoogle(argv[0], &argc, &argv, true); + RunSpecifiedBenchmarks(); + return 0; +} diff --git a/btree_container.h b/btree_container.h new file mode 100644 index 00000000..6703d1bf --- /dev/null +++ b/btree_container.h @@ -0,0 +1,325 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: jmacd@google.com (Josh MacDonald) +// Author: pmattis@google.com (Peter Mattis) + +#ifndef UTIL_BTREE_BTREE_CONTAINER_H__ +#define UTIL_BTREE_BTREE_CONTAINER_H__ + +#include +#include + +#include "util/btree/btree.h" // IWYU pragma: export + +namespace util { +namespace btree { + +// A common base class for btree_set, btree_map, btree_multiset and +// btree_multimap. +template +class btree_container { + typedef btree_container self_type; + + public: + typedef typename Tree::params_type params_type; + typedef typename Tree::key_type key_type; + typedef typename Tree::value_type value_type; + typedef typename Tree::key_compare key_compare; + typedef typename Tree::allocator_type allocator_type; + typedef typename Tree::pointer pointer; + typedef typename Tree::const_pointer const_pointer; + typedef typename Tree::reference reference; + typedef typename Tree::const_reference const_reference; + typedef typename Tree::size_type size_type; + typedef typename Tree::difference_type difference_type; + typedef typename Tree::iterator iterator; + typedef typename Tree::const_iterator const_iterator; + typedef typename Tree::reverse_iterator reverse_iterator; + typedef typename Tree::const_reverse_iterator const_reverse_iterator; + + public: + // Default constructor. + btree_container(const key_compare &comp, const allocator_type &alloc) + : tree_(comp, alloc) { + } + + // Copy constructor. + btree_container(const self_type &x) + : tree_(x.tree_) { + } + + // Iterator routines. + iterator begin() { return tree_.begin(); } + const_iterator begin() const { return tree_.begin(); } + iterator end() { return tree_.end(); } + const_iterator end() const { return tree_.end(); } + reverse_iterator rbegin() { return tree_.rbegin(); } + const_reverse_iterator rbegin() const { return tree_.rbegin(); } + reverse_iterator rend() { return tree_.rend(); } + const_reverse_iterator rend() const { return tree_.rend(); } + + // Lookup routines. + iterator lower_bound(const key_type &key) { + return tree_.lower_bound(key); + } + const_iterator lower_bound(const key_type &key) const { + return tree_.lower_bound(key); + } + iterator upper_bound(const key_type &key) { + return tree_.upper_bound(key); + } + const_iterator upper_bound(const key_type &key) const { + return tree_.upper_bound(key); + } + pair equal_range(const key_type &key) { + return tree_.equal_range(key); + } + pair equal_range(const key_type &key) const { + return tree_.equal_range(key); + } + + // Utility routines. + void clear() { + tree_.clear(); + } + void swap(self_type &x) { + tree_.swap(x.tree_); + } + void dump(ostream &os) const { + tree_.dump(os); + } + void verify() const { + tree_.verify(); + } + + // Size routines. + size_type size() const { return tree_.size(); } + size_type max_size() const { return tree_.max_size(); } + bool empty() const { return tree_.empty(); } + size_type height() const { return tree_.height(); } + size_type internal_nodes() const { return tree_.internal_nodes(); } + size_type leaf_nodes() const { return tree_.leaf_nodes(); } + size_type nodes() const { return tree_.nodes(); } + size_type bytes_used() const { return tree_.bytes_used(); } + static double average_bytes_per_value() { + return Tree::average_bytes_per_value(); + } + double fullness() const { return tree_.fullness(); } + double overhead() const { return tree_.overhead(); } + + protected: + Tree tree_; +}; + +template +inline ostream& operator<<(ostream &os, const btree_container &b) { + b.dump(os); + return os; +} + +// A common base class for btree_set and safe_btree_set. +template +class btree_unique_container : public btree_container { + typedef btree_unique_container self_type; + typedef btree_container super_type; + + public: + typedef typename Tree::key_type key_type; + typedef typename Tree::value_type value_type; + typedef typename Tree::size_type size_type; + typedef typename Tree::key_compare key_compare; + typedef typename Tree::allocator_type allocator_type; + typedef typename Tree::iterator iterator; + typedef typename Tree::const_iterator const_iterator; + + public: + // Default constructor. + btree_unique_container(const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + } + + // Copy constructor. + btree_unique_container(const self_type &x) + : super_type(x) { + } + + // Range constructor. + template + btree_unique_container(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + insert(b, e); + } + + // Lookup routines. + iterator find(const key_type &key) { + return this->tree_.find_unique(key); + } + const_iterator find(const key_type &key) const { + return this->tree_.find_unique(key); + } + size_type count(const key_type &key) const { + return this->tree_.count_unique(key); + } + + // Insertion routines. + pair insert(const value_type &x) { + return this->tree_.insert_unique(x); + } + iterator insert(iterator position, const value_type &x) { + return this->tree_.insert_unique(position, x); + } + template + void insert(InputIterator b, InputIterator e) { + this->tree_.insert_unique(b, e); + } + + // Deletion routines. + int erase(const key_type &key) { + return this->tree_.erase_unique(key); + } + // Erase the specified iterator from the btree. The iterator must be valid + // (i.e. not equal to end()). Return an iterator pointing to the node after + // the one that was erased (or end() if none exists). + iterator erase(const iterator &iter) { + return this->tree_.erase(iter); + } + void erase(const iterator &first, const iterator &last) { + this->tree_.erase(first, last); + } +}; + +// A common base class for btree_map and safe_btree_map. +template +class btree_map_container : public btree_unique_container { + typedef btree_map_container self_type; + typedef btree_unique_container super_type; + + public: + typedef typename Tree::key_type key_type; + typedef typename Tree::data_type data_type; + typedef typename Tree::value_type value_type; + typedef typename Tree::mapped_type mapped_type; + typedef typename Tree::key_compare key_compare; + typedef typename Tree::allocator_type allocator_type; + + private: + // A pointer-like object which only generates its value when + // dereferenced. Used by operator[] to avoid constructing an empty data_type + // if the key already exists in the map. + struct generate_value { + generate_value(const key_type &k) + : key(k) { + } + value_type operator*() const { + return make_pair(key, data_type()); + } + const key_type &key; + }; + + public: + // Default constructor. + btree_map_container(const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + } + + // Copy constructor. + btree_map_container(const self_type &x) + : super_type(x) { + } + + // Range constructor. + template + btree_map_container(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + insert(b, e); + } + + // Insertion routines. + data_type& operator[](const key_type &key) { + return this->tree_.insert_unique(key, generate_value(key)).first->second; + } +}; + +// A common base class for btree_multiset and btree_multimap. +template +class btree_multi_container : public btree_container { + typedef btree_multi_container self_type; + typedef btree_container super_type; + + public: + typedef typename Tree::key_type key_type; + typedef typename Tree::value_type value_type; + typedef typename Tree::size_type size_type; + typedef typename Tree::key_compare key_compare; + typedef typename Tree::allocator_type allocator_type; + typedef typename Tree::iterator iterator; + typedef typename Tree::const_iterator const_iterator; + + public: + // Default constructor. + btree_multi_container(const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + } + + // Copy constructor. + btree_multi_container(const self_type &x) + : super_type(x) { + } + + // Range constructor. + template + btree_multi_container(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(b, e, comp, alloc) { + insert(b, e); + } + + // Lookup routines. + iterator find(const key_type &key) { + return this->tree_.find_multi(key); + } + const_iterator find(const key_type &key) const { + return this->tree_.find_multi(key); + } + size_type count(const key_type &key) const { + return this->tree_.count_multi(key); + } + + // Insertion routines. + iterator insert(const value_type &x) { + return this->tree_.insert_multi(x); + } + iterator insert(iterator position, const value_type &x) { + return this->tree_.insert_multi(position, x); + } + template + void insert(InputIterator b, InputIterator e) { + this->tree_.insert_multi(b, e); + } + + // Deletion routines. + int erase(const key_type &key) { + return this->tree_.erase_multi(key); + } + // Erase the specified iterator from the btree. The iterator must be valid + // (i.e. not equal to end()). Return an iterator pointing to the node after + // the one that was erased (or end() if none exists). + iterator erase(const iterator &iter) { + return this->tree_.erase(iter); + } + void erase(const iterator &first, const iterator &last) { + this->tree_.erase(first, last); + } +}; + +} // namespace btree +} // namespace util + +#endif // UTIL_BTREE_BTREE_CONTAINER_H__ diff --git a/btree_map.h b/btree_map.h new file mode 100644 index 00000000..3c61a7f2 --- /dev/null +++ b/btree_map.h @@ -0,0 +1,122 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: jmacd@google.com (Josh MacDonald) +// Author: pmattis@google.com (Peter Mattis) +// +// A btree_map<> implements the STL unique sorted associative container +// interface and the pair associative container interface (a.k.a map<>) using a +// btree. A btree_multimap<> implements the STL multiple sorted associative +// container interface and the pair associtive container interface (a.k.a +// multimap<>) using a btree. See btree.h for details of the btree +// implementation and caveats. + +#ifndef UTIL_BTREE_BTREE_MAP_H__ +#define UTIL_BTREE_BTREE_MAP_H__ + +#include +#include +#include +#include +#include + +#include "util/btree/btree.h" // IWYU pragma: export +#include "util/btree/btree_container.h" // IWYU pragma: export + +namespace util { +namespace btree { + +// The btree_map class is needed mainly for it's constructors. +template , + typename Alloc = std::allocator >, + int TargetNodeSize = 256> +class btree_map : public btree_map_container< + btree > > { + + typedef btree_map self_type; + typedef btree_map_params< + Key, Value, Compare, Alloc, TargetNodeSize> params_type; + typedef btree btree_type; + typedef btree_map_container super_type; + + public: + typedef typename btree_type::key_compare key_compare; + typedef typename btree_type::allocator_type allocator_type; + + public: + // Default constructor. + btree_map(const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + } + + // Copy constructor. + btree_map(const self_type &x) + : super_type(x) { + } + + // Range constructor. + template + btree_map(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + } +}; + +template +inline void swap(btree_map &x, + btree_map &y) { + x.swap(y); +} + +// The btree_multimap class is needed mainly for it's constructors. +template , + typename Alloc = std::allocator >, + int TargetNodeSize = 256> +class btree_multimap : public btree_multi_container< + btree > > { + + typedef btree_multimap self_type; + typedef btree_map_params< + Key, Value, Compare, Alloc, TargetNodeSize> params_type; + typedef btree btree_type; + typedef btree_multi_container super_type; + + public: + typedef typename btree_type::key_compare key_compare; + typedef typename btree_type::allocator_type allocator_type; + typedef typename btree_type::data_type data_type; + typedef typename btree_type::mapped_type mapped_type; + + public: + // Default constructor. + btree_multimap(const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + } + + // Copy constructor. + btree_multimap(const self_type &x) + : super_type(x) { + } + + // Range constructor. + template + btree_multimap(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(b, e, comp, alloc) { + } +}; + +template +inline void swap(btree_multimap &x, + btree_multimap &y) { + x.swap(y); +} + +} // namespace btree +} // namespace util + +#endif // UTIL_BTREE_BTREE_MAP_H__ diff --git a/btree_nc.cc b/btree_nc.cc new file mode 100644 index 00000000..b24c312d --- /dev/null +++ b/btree_nc.cc @@ -0,0 +1,50 @@ +// Copyright 2009 Google Inc. All Rights Reserved. +// Author: jmacd@google.com (Josh MacDonald) + +#include "util/btree/btree_set.h" + +namespace { + +template +struct Compare { + R operator()(int a, int b) const { return reinterpret_cast(a < b); } +}; + +template +struct CompareTo : public util::btree::btree_key_compare_to_tag { + R operator()(int a, int b) const { return reinterpret_cast(a < b); } +}; + +#define TEST_COMPARE(r) \ + void TestCompile() { \ + util::btree::btree_set > s; \ + } + +#define TEST_COMPARE_TO(r) \ + void TestCompile() { \ + util::btree::btree_set > s; \ + } + +#if defined(TEST_bool) +TEST_COMPARE(bool); +#elif defined(TEST_int) +TEST_COMPARE(int); +#elif defined(TEST_float) +TEST_COMPARE(float); +#elif defined(TEST_pointer) +TEST_COMPARE(void*); +#elif defined(TEST_compare_to_bool) +TEST_COMPARE_TO(bool); +#elif defined(TEST_compare_to_int) +TEST_COMPARE_TO(int); +#elif defined(TEST_compare_to_float) +TEST_COMPARE_TO(float); +#elif defined(TEST_compare_to_pointer) +TEST_COMPARE_TO(pointer); +#endif + +} // namespace + +int main() { + return 1; +} diff --git a/btree_nc_test.py b/btree_nc_test.py new file mode 100755 index 00000000..056a75d2 --- /dev/null +++ b/btree_nc_test.py @@ -0,0 +1,83 @@ +#!/usr/bin/python2.4 +# +# Copyright 2006 Google Inc. All Rights Reserved. + +"""Negative compilation unit test for btree.h. +""" + +__author__ = 'pmattis@google.com (Peter Mattis)' + +import os +from google3.testing.pybase import googletest +from google3.testing.pybase import fake_target_util +from google3.pyglib import flags + +_FLAGS = flags.FLAGS + + +class BtreeNegativeUnitTest(googletest.TestCase): + """Negative compilation tests for btree.h""" + + def testCompilerErrors(self): + """Runs a list of tests to verify that erroneous code leads to + expected compiler messages.""" + + # Defines a list of test specs, where each element is a tuple + # (test name, list of regexes for matching the compiler errors). + test_specs = [ + # Test that bool works as a return type for key comparison. + ('bool', None), # None means compilation should succeed. + + # Test that int does not work as a return type for key comparison. + ('int', + [r'error: creating array with negative size', # for gcc + r'', # for icc + ]), + + # Test that float does not work as a return type for key comparison. + ('float', + [r'error: creating array with negative size', # for gcc + r'', # for icc + ]), + + # Test that void* does not work as a return type for key comparison. + ('pointer', + [r'error: creating array with negative size', # for gcc + r'', # for icc + ]), + + # Test that bool does not work as a return type for compare-to + # comparison. + ('compare_to_bool', + [r'error: creating array with negative size', # for gcc + r'', # for icc + ]), + + # Test that int works as a return type for compare-to comparison. + ('compare_to_int', None), # None means compilation should succeed. + + # Test that float does not work as a return type for compare-to + # comparison. + ('compare_to_float', + [r'error: creating array with negative size', # for gcc + r'', # for icc + ]), + + # Test that void* does not work as a return type for compare-to + # comparison. + ('compare_to_pointer', + [r'error: creating array with negative size', # for gcc + r'', # for icc + ]), + ] + + # Runs the list of tests. + fake_target_util.AssertCcCompilerErrors( + self, + 'google3/util/btree/btree_nc', # path to the fake target file. + 'btree_nc.o', # name of the target to build. + test_specs) + + +if __name__ == '__main__': + googletest.main() diff --git a/btree_printer.py b/btree_printer.py new file mode 100755 index 00000000..8f233401 --- /dev/null +++ b/btree_printer.py @@ -0,0 +1,125 @@ +#!/usr/bin/python2.4 +# Copyright 2011 Google Inc. All Rights Reserved. +# GDB support for pretty printing StringPiece. + +"""GDB pretty-printer for btrees.""" + +# This is a module provided by GDB. +# Ref: http://wiki/Main/Google3GDBScripts +import gdb +from google3.devtools.gdb.component import printing + + +class BaseBtreePrinter(object): + """btree pretty-printer, for util/btree.""" + + def __init__(self, typename, val): + self.typename = typename + self.val = val + + def display_hint(self): + return 'array' + + def _my_iter(self, node): + count = node['fields_']['count'] + if node['fields_']['leaf']: + for i in range(count): + key = node['fields_']['values'][i] + yield ('[item]', key) + else: + # recursive generators are annoying: we can't just recurse, we need + # to expand and yield the values. + for i in range(count+1): + child = node['fields_']['children'][i] + for v in self._my_iter(child.dereference()): + yield v + + def children(self): + if self.nelements() != 0: + return self._my_iter(self.val['root_']['data'].dereference()) + else: + return iter([]) + + def nelements(self): + if self.val['root_']['data'] != 0: + root_fields = self.val['root_']['data'].dereference()['fields_'] + if root_fields['leaf']: + return root_fields['count'] + else: + return root_fields['size'] + else: + return 0 + + def to_string(self): # pylint: disable-msg=C6409 + """GDB calls this to compute the pretty-printed form.""" + return '%s with %d elements' % (self.typename, self.nelements()) + + +class BtreePrinter(BaseBtreePrinter): + """btree<> pretty-printer, for util/btree.""" + + def __init__(self, val): + BaseBtreePrinter.__init__(self, 'btree', val) + + +class BtreeSetPrinter(BaseBtreePrinter): + """btree_set<> pretty-printer.""" + + def __init__(self, val): + BaseBtreePrinter.__init__(self, 'btree_set', val['tree_']) + + +class BtreeMultisetPrinter(BaseBtreePrinter): + """btree_multiset<> pretty-printer.""" + + def __init__(self, val): + BaseBtreePrinter.__init__(self, 'btree_multiset', val['tree_']) + + +class BaseBtreeMapPrinter(BaseBtreePrinter): + """btree maps pretty-printer.""" + + def __init__(self, typename, val): + BaseBtreePrinter.__init__(self, typename, val['tree_']) + + def display_hint(self): + return 'map' + + def _my_map_iter(self, g): + for (_, pair) in g: + yield ('[key]', pair['first']) + yield ('[value]', pair['second']) + + def children(self): + # we need to break apart the pairs and yield them separately + if self.nelements() != 0: + return self._my_map_iter(BaseBtreePrinter.children(self)) + else: + return iter([]) + + +class BtreeMapPrinter(BaseBtreeMapPrinter): + """btree_map<> pretty-printer.""" + + def __init__(self, val): + BaseBtreeMapPrinter.__init__(self, 'btree_map', val) + + +class BtreeMultimapPrinter(BaseBtreeMapPrinter): + """btree_multimap<> pretty-printer.""" + + def __init__(self, val): + BaseBtreeMapPrinter.__init__(self, 'btree_multimap', val) + + +if __name__ == '__main__': + printing.RegisterGoogle3ClassPrettyPrinter('util::btree::btree<.*>', + BtreePrinter) + printing.RegisterGoogle3ClassPrettyPrinter('util::btree::btree_set<.*>', + BtreeSetPrinter) + printing.RegisterGoogle3ClassPrettyPrinter('util::btree::btree_multiset<.*>', + BtreeMultisetPrinter) + printing.RegisterGoogle3ClassPrettyPrinter('util::btree::btree_map<.*>', + BtreeMapPrinter) + printing.RegisterGoogle3ClassPrettyPrinter('util::btree::btree_multimap<.*>', + BtreeMultimapPrinter) diff --git a/btree_printer_test.py b/btree_printer_test.py new file mode 100755 index 00000000..95bc351e --- /dev/null +++ b/btree_printer_test.py @@ -0,0 +1,92 @@ +#!/usr/bin/python2.4 +# +# Copyright 2011 Google Inc. All Rights Reserved. + +"""Tests for btree_printer.py gdb pretty printer.""" + +__author__ = "leg@google.com (Lawrence Greenfield)" + +from google3.pyglib import flags +from google3.testing.gdb import gdb_script_test_util +from google3.testing.pybase import googletest + +FLAGS = flags.FLAGS + + +class BtreePrinterTest(gdb_script_test_util.TestCase): + def testBtreeSet(self): + self.InitSession("btree_set", + "util/btree/btree_test_program") + self.RunTo("StopHereForDebugger") + + self.SetOption("print elements", 20) + self.TestPrintOutputMatches("*empty_set", + """btree_set with 0 elements""") + self.TestPrintOutputMatches("*small_set", + """btree_set with 10 elements = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}""") + self.TestPrintOutputMatches("*small_multiset", + """btree_multiset with 10 elements = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4}""") + self.TestPrintOutputMatches("*big_set", + """btree_set with 80 elements = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...}""") + + self.RunSession() + + def testBtreeMap(self): + self.InitSession("btree_set", + "util/btree/btree_test_program") + self.RunTo("StopHereForDebugger") + + self.SetOption("print elements", 30) + self.TestPrintOutputMatches("*empty_map", + """btree_map with 0 elements""") + self.TestPrintOutputMatches("*small_map", + """btree_map with 10 elements = { + \\[0\\] = 0, + \\[1\\] = 13, + \\[2\\] = 26, + \\[3\\] = 39, + \\[4\\] = 52, + \\[5\\] = 65, + \\[6\\] = 78, + \\[7\\] = 91, + \\[8\\] = 104, + \\[9\\] = 117 +}""") + self.TestPrintOutputMatches("*small_multimap", + """btree_multimap with 10 elements = { + \\[0\\] = 0, + \\[0\\] = 1, + \\[1\\] = 2, + \\[1\\] = 3, + \\[2\\] = 4, + \\[2\\] = 5, + \\[3\\] = 6, + \\[3\\] = 7, + \\[4\\] = 8, + \\[4\\] = 9 +}""") + self.TestPrintOutputMatches("*big_map", + """btree_map with 80 elements = { + \\[0\\] = 0, + \\[1\\] = 7, + \\[2\\] = 14, + \\[3\\] = 21, + \\[4\\] = 28, + \\[5\\] = 35, + \\[6\\] = 42, + \\[7\\] = 49, + \\[8\\] = 56, + \\[9\\] = 63, + \\[10\\] = 70, + \\[11\\] = 77, + \\[12\\] = 84, + \\[13\\] = 91, + \\[14\\] = 98 + ... +}""") + + self.RunSession() + + +if __name__ == "__main__": + googletest.main() diff --git a/btree_set.h b/btree_set.h new file mode 100644 index 00000000..7b5ea88d --- /dev/null +++ b/btree_set.h @@ -0,0 +1,113 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: jmacd@google.com (Josh MacDonald) +// Author: pmattis@google.com (Peter Mattis) +// +// A btree_set<> implements the STL unique sorted associative container +// interface (a.k.a set<>) using a btree. A btree_multiset<> implements the STL +// multiple sorted associative container interface (a.k.a multiset<>) using a +// btree. See btree.h for details of the btree implementation and caveats. + +#ifndef UTIL_BTREE_BTREE_SET_H__ +#define UTIL_BTREE_BTREE_SET_H__ + +#include +#include +#include + +#include "util/btree/btree.h" // IWYU pragma: export +#include "util/btree/btree_container.h" // IWYU pragma: export + +namespace util { +namespace btree { + +// The btree_set class is needed mainly for it's constructors. +template , + typename Alloc = std::allocator, + int TargetNodeSize = 256> +class btree_set : public btree_unique_container< + btree > > { + + typedef btree_set self_type; + typedef btree_set_params params_type; + typedef btree btree_type; + typedef btree_unique_container super_type; + + public: + typedef typename btree_type::key_compare key_compare; + typedef typename btree_type::allocator_type allocator_type; + + public: + // Default constructor. + btree_set(const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + } + + // Copy constructor. + btree_set(const self_type &x) + : super_type(x) { + } + + // Range constructor. + template + btree_set(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(b, e, comp, alloc) { + } +}; + +template +inline void swap(btree_set &x, btree_set &y) { + x.swap(y); +} + +// The btree_multiset class is needed mainly for it's constructors. +template , + typename Alloc = std::allocator, + int TargetNodeSize = 256> +class btree_multiset : public btree_multi_container< + btree > > { + + typedef btree_multiset self_type; + typedef btree_set_params params_type; + typedef btree btree_type; + typedef btree_multi_container super_type; + + public: + typedef typename btree_type::key_compare key_compare; + typedef typename btree_type::allocator_type allocator_type; + + public: + // Default constructor. + btree_multiset(const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + } + + // Copy constructor. + btree_multiset(const self_type &x) + : super_type(x) { + } + + // Range constructor. + template + btree_multiset(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(b, e, comp, alloc) { + } +}; + +template +inline void swap(btree_multiset &x, + btree_multiset &y) { + x.swap(y); +} + +} // namespace btree +} // namespace util + +#endif // UTIL_BTREE_BTREE_SET_H__ diff --git a/btree_test.cc b/btree_test.cc new file mode 100644 index 00000000..ef4e574e --- /dev/null +++ b/btree_test.cc @@ -0,0 +1,167 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: jmacd@google.com (Josh MacDonald) +// Author: pmattis@google.com (Peter Mattis) + +#include "base/arena-inl.h" +#include "base/init_google.h" +#include "base/integral_types.h" +#include "base/logging.h" +#include "strings/stringpiece.h" +#include "testing/base/public/gunit.h" +#include "util/btree/btree_map.h" +#include "util/btree/btree_set.h" +#include "util/btree/btree_test.h" + +namespace util { +namespace btree { +namespace { + +template +void SetTest() { + typedef ArenaAllocator ArenaAlloc; + CHECK_EQ(sizeof(btree_set), sizeof(void*)); + BtreeTest, allocator, N>, set >(); + BtreeArenaTest, ArenaAlloc, N> >(); +} + +template +void MapTest() { + typedef ArenaAllocator ArenaAlloc; + CHECK_EQ(sizeof(btree_map), sizeof(void*)); + BtreeTest, allocator, N>, map >(); + BtreeArenaTest, ArenaAlloc, N> >(); + BtreeMapTest, allocator, N> >(); +} + +TEST(Btree, set_int32_32) { SetTest(); } +TEST(Btree, set_int32_64) { SetTest(); } +TEST(Btree, set_int32_128) { SetTest(); } +TEST(Btree, set_int32_256) { SetTest(); } +TEST(Btree, set_int64_256) { SetTest(); } +TEST(Btree, set_string_256) { SetTest(); } +TEST(Btree, set_cord_256) { SetTest(); } +TEST(Btree, set_pair_256) { SetTest, 256>(); } +TEST(Btree, map_int32_256) { MapTest(); } +TEST(Btree, map_int64_256) { MapTest(); } +TEST(Btree, map_string_256) { MapTest(); } +TEST(Btree, map_cord_256) { MapTest(); } +TEST(Btree, map_pair_256) { MapTest, 256>(); } + +template +void MultiSetTest() { + typedef ArenaAllocator ArenaAlloc; + CHECK_EQ(sizeof(btree_multiset), sizeof(void*)); + BtreeMultiTest, allocator, N>, + multiset >(); + BtreeArenaTest, ArenaAlloc, N> >(); +} + +template +void MultiMapTest() { + typedef ArenaAllocator ArenaAlloc; + CHECK_EQ(sizeof(btree_multimap), sizeof(void*)); + BtreeMultiTest, allocator, N>, + multimap >(); + BtreeMultiMapTest, allocator, N> >(); + BtreeArenaTest, ArenaAlloc, N> >(); +} + +TEST(Btree, multiset_int32_256) { MultiSetTest(); } +TEST(Btree, multiset_int64_256) { MultiSetTest(); } +TEST(Btree, multiset_string_256) { MultiSetTest(); } +TEST(Btree, multiset_cord_256) { MultiSetTest(); } +TEST(Btree, multiset_pair_256) { MultiSetTest, 256>(); } +TEST(Btree, multimap_int32_256) { MultiMapTest(); } +TEST(Btree, multimap_int64_256) { MultiMapTest(); } +TEST(Btree, multimap_string_256) { MultiMapTest(); } +TEST(Btree, multimap_cord_256) { MultiMapTest(); } +TEST(Btree, multimap_pair_256) { MultiMapTest, 256>(); } + +// Verify that swapping btrees swaps the key comparision functors. +struct SubstringLess { + SubstringLess() : n(2) {} + SubstringLess(int length) + : n(length) { + } + bool operator()(const string &a, const string &b) const { + return StringPiece(a).substr(0, n) < StringPiece(b).substr(0, n); + } + int n; +}; + +TEST(Btree, SwapKeyCompare) { + typedef btree_set SubstringSet; + SubstringSet s1(SubstringLess(1), SubstringSet::allocator_type()); + SubstringSet s2(SubstringLess(2), SubstringSet::allocator_type()); + + ASSERT_TRUE(s1.insert("a").second); + ASSERT_FALSE(s1.insert("aa").second); + + ASSERT_TRUE(s2.insert("a").second); + ASSERT_TRUE(s2.insert("aa").second); + ASSERT_FALSE(s2.insert("aaa").second); + + swap(s1, s2); + + ASSERT_TRUE(s1.insert("b").second); + ASSERT_TRUE(s1.insert("bb").second); + ASSERT_FALSE(s1.insert("bbb").second); + + ASSERT_TRUE(s2.insert("b").second); + ASSERT_FALSE(s2.insert("bb").second); +} + +TEST(Btree, UpperBoundRegression) { + // Regress a bug where upper_bound would default-construct a new key_compare + // instead of copying the existing one. + typedef btree_set SubstringSet; + SubstringSet my_set(SubstringLess(3)); + my_set.insert("aab"); + my_set.insert("abb"); + // We call upper_bound("aaa"). If this correctly uses the length 3 + // comparator, aaa < aab < abb, so we should get aab as the result. + // If it instead uses the default-constructed length 2 comparator, + // aa == aa < ab, so we'll get abb as our result. + SubstringSet::iterator it = my_set.upper_bound("aaa"); + ASSERT_TRUE(it != my_set.end()); + EXPECT_EQ("aab", *it); +} + + +TEST(Btree, IteratorIncrementBy) { + // Test that increment_by returns the same position as increment. + const int kSetSize = 2341; + btree_set my_set; + for (int i = 0; i < kSetSize; ++i) { + my_set.insert(i); + } + + { + // Simple increment vs. increment by. + btree_set::iterator a = my_set.begin(); + btree_set::iterator b = my_set.begin(); + a.increment(); + b.increment_by(1); + EXPECT_EQ(*a, *b); + } + + btree_set::iterator a = my_set.begin(); + for (int i = 1; i < kSetSize; ++i) { + ++a; + // increment_by + btree_set::iterator b = my_set.begin(); + b.increment_by(i); + EXPECT_EQ(*a, *b) << ": i=" << i; + } +} + + +} // namespace +} // namespace btree +} // namespace util + +int main(int argc, char **argv) { + FLAGS_logtostderr = true; + InitGoogle(argv[0], &argc, &argv, true); + return RUN_ALL_TESTS(); +} diff --git a/btree_test.h b/btree_test.h new file mode 100644 index 00000000..557c1e54 --- /dev/null +++ b/btree_test.h @@ -0,0 +1,930 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: jmacd@google.com (Josh MacDonald) +// Author: pmattis@google.com (Peter Mattis) + +#ifndef UTIL_BTREE_BTREE_TEST_H__ +#define UTIL_BTREE_BTREE_TEST_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base/arena.h" +#include "base/commandlineflags.h" +#include "base/logging.h" +#include "base/type_traits.h" +#include "strings/cord.h" +#include "strings/util.h" +#include "testing/base/public/googletest.h" +#include "util/btree/btree_container.h" +#include "util/random/acmrandom.h" + +DECLARE_int32(test_values); +DECLARE_int32(benchmark_values); + +namespace std { + +// Provide operator<< support for pair. +template +ostream& operator<<(ostream &os, const pair &p) { + os << "(" << p.first << "," << p.second << ")"; + return os; +} + +// Provide pair equality testing that works as long as x.first is comparable to +// y.first and x.second is comparable to y.second. Needed in the test for +// comparing pair to pair. +template +bool operator==(const pair &x, const pair &y) { + return x.first == y.first && x.second == y.second; +} + +} // namespace std + +namespace base { + +// Partial specialization of remove_const that propagates the removal through +// std::pair. +template +struct remove_const > { + typedef std::pair::type, + typename remove_const::type> type; +}; + +} // namespace base + +namespace util { +namespace btree { + +// Utility class to provide an accessor for a key given a value. The default +// behavior is to treat the value as a pair and return the first element. +template +struct KeyOfValue { + typedef select1st type; +}; + +// Partial specialization of KeyOfValue class for when the key and value are +// the same type such as in set<> and btree_set<>. +template +struct KeyOfValue { + typedef identity type; +}; + +// The base class for a sorted associative container checker. TreeType is the +// container type to check and CheckerType is the container type to check +// against. TreeType is expected to be btree_{set,map,multiset,multimap} and +// CheckerType is expected to be {set,map,multiset,multimap}. +template +class base_checker { + typedef base_checker self_type; + + public: + typedef typename TreeType::key_type key_type; + typedef typename TreeType::value_type value_type; + typedef typename TreeType::key_compare key_compare; + typedef typename TreeType::pointer pointer; + typedef typename TreeType::const_pointer const_pointer; + typedef typename TreeType::reference reference; + typedef typename TreeType::const_reference const_reference; + typedef typename TreeType::size_type size_type; + typedef typename TreeType::difference_type difference_type; + typedef typename TreeType::iterator iterator; + typedef typename TreeType::const_iterator const_iterator; + typedef typename TreeType::reverse_iterator reverse_iterator; + typedef typename TreeType::const_reverse_iterator const_reverse_iterator; + + public: + // Default constructor. + base_checker() + : const_tree_(tree_) { + } + // Copy constructor. + base_checker(const self_type &x) + : tree_(x.tree_), + const_tree_(tree_), + checker_(x.checker_) { + } + + // Iterator routines. + iterator begin() { return tree_.begin(); } + const_iterator begin() const { return tree_.begin(); } + iterator end() { return tree_.end(); } + const_iterator end() const { return tree_.end(); } + reverse_iterator rbegin() { return tree_.rbegin(); } + const_reverse_iterator rbegin() const { return tree_.rbegin(); } + reverse_iterator rend() { return tree_.rend(); } + const_reverse_iterator rend() const { return tree_.rend(); } + + // Helper routines. + template + IterType iter_check( + IterType tree_iter, CheckerIterType checker_iter) const { + if (tree_iter == tree_.end()) { + CHECK(checker_iter == checker_.end()); + } else { + CHECK_EQ(*tree_iter, *checker_iter); + } + return tree_iter; + } + template + IterType riter_check( + IterType tree_iter, CheckerIterType checker_iter) const { + if (tree_iter == tree_.rend()) { + CHECK(checker_iter == checker_.rend()); + } else { + CHECK_EQ(*tree_iter, *checker_iter); + } + return tree_iter; + } + void value_check(const value_type &x) { + typename KeyOfValue::type key_of_value; + const key_type &key = key_of_value(x); + CHECK_EQ(*find(key), x); + lower_bound(key); + upper_bound(key); + equal_range(key); + count(key); + } + void erase_check(const key_type &key) { + CHECK(tree_.find(key) == const_tree_.end()); + CHECK(const_tree_.find(key) == tree_.end()); + CHECK(tree_.equal_range(key).first == + const_tree_.equal_range(key).second); + } + + // Lookup routines. + iterator lower_bound(const key_type &key) { + return iter_check(tree_.lower_bound(key), checker_.lower_bound(key)); + } + const_iterator lower_bound(const key_type &key) const { + return iter_check(tree_.lower_bound(key), checker_.lower_bound(key)); + } + iterator upper_bound(const key_type &key) { + return iter_check(tree_.upper_bound(key), checker_.upper_bound(key)); + } + const_iterator upper_bound(const key_type &key) const { + return iter_check(tree_.upper_bound(key), checker_.upper_bound(key)); + } + pair equal_range(const key_type &key) { + pair checker_res = + checker_.equal_range(key); + pair tree_res = tree_.equal_range(key); + iter_check(tree_res.first, checker_res.first); + iter_check(tree_res.second, checker_res.second); + return tree_res; + } + pair equal_range(const key_type &key) const { + pair checker_res = + checker_.equal_range(key); + pair tree_res = tree_.equal_range(key); + iter_check(tree_res.first, checker_res.first); + iter_check(tree_res.second, checker_res.second); + return tree_res; + } + iterator find(const key_type &key) { + return iter_check(tree_.find(key), checker_.find(key)); + } + const_iterator find(const key_type &key) const { + return iter_check(tree_.find(key), checker_.find(key)); + } + size_type count(const key_type &key) const { + size_type res = checker_.count(key); + CHECK_EQ(res, tree_.count(key)); + return res; + } + + // Assignment operator. + self_type& operator=(const self_type &x) { + tree_ = x.tree_; + checker_ = x.checker_; + return *this; + } + + // Deletion routines. + int erase(const key_type &key) { + int size = tree_.size(); + int res = checker_.erase(key); + CHECK_EQ(res, tree_.count(key)); + CHECK_EQ(res, tree_.erase(key)); + CHECK_EQ(tree_.count(key), 0); + CHECK_EQ(tree_.size(), size - res); + erase_check(key); + return res; + } + iterator erase(iterator iter) { + key_type key = iter.key(); + int size = tree_.size(); + int count = tree_.count(key); + typename CheckerType::iterator checker_iter = checker_.find(key); + for (iterator tmp(tree_.find(key)); tmp != iter; ++tmp) { + ++checker_iter; + } + typename CheckerType::iterator checker_next = checker_iter; + ++checker_next; + checker_.erase(checker_iter); + iter = tree_.erase(iter); + CHECK_EQ(tree_.size(), checker_.size()); + CHECK_EQ(tree_.size(), size - 1); + CHECK_EQ(tree_.count(key), count - 1); + if (count == 1) { + erase_check(key); + } + return iter_check(iter, checker_next); + } + + void erase(iterator begin, iterator end) { + int size = tree_.size(); + int count = distance(begin, end); + typename CheckerType::iterator checker_begin = checker_.find(begin.key()); + for (iterator tmp(tree_.find(begin.key())); tmp != begin; ++tmp) { + ++checker_begin; + } + typename CheckerType::iterator checker_end = + end == tree_.end() ? checker_.end() : checker_.find(end.key()); + if (end != tree_.end()) { + for (iterator tmp(tree_.find(end.key())); tmp != end; ++tmp) { + ++checker_end; + } + } + checker_.erase(checker_begin, checker_end); + tree_.erase(begin, end); + CHECK_EQ(tree_.size(), checker_.size()); + CHECK_EQ(tree_.size(), size - count); + } + + // Utility routines. + void clear() { + tree_.clear(); + checker_.clear(); + } + void swap(self_type &x) { + tree_.swap(x.tree_); + checker_.swap(x.checker_); + } + + void verify() const { + tree_.verify(); + CHECK_EQ(tree_.size(), checker_.size()); + + // Move through the forward iterators using increment. + typename CheckerType::const_iterator + checker_iter(checker_.begin()); + const_iterator tree_iter(tree_.begin()); + for (; tree_iter != tree_.end(); + ++tree_iter, ++checker_iter) { + CHECK_EQ(*tree_iter, *checker_iter); + } + + // Move through the forward iterators using decrement. + for (int n = tree_.size() - 1; n >= 0; --n) { + iter_check(tree_iter, checker_iter); + --tree_iter; + --checker_iter; + } + CHECK(tree_iter == tree_.begin()); + CHECK(checker_iter == checker_.begin()); + + // Move through the reverse iterators using increment. + typename CheckerType::const_reverse_iterator + checker_riter(checker_.rbegin()); + const_reverse_iterator tree_riter(tree_.rbegin()); + for (; tree_riter != tree_.rend(); + ++tree_riter, ++checker_riter) { + CHECK_EQ(*tree_riter, *checker_riter); + } + + // Move through the reverse iterators using decrement. + for (int n = tree_.size() - 1; n >= 0; --n) { + riter_check(tree_riter, checker_riter); + --tree_riter; + --checker_riter; + } + CHECK(tree_riter == tree_.rbegin()); + CHECK(checker_riter == checker_.rbegin()); + } + + // Access to the underlying btree. + const TreeType& tree() const { return tree_; } + + // Size routines. + size_type size() const { + CHECK_EQ(tree_.size(), checker_.size()); + return tree_.size(); + } + size_type max_size() const { return tree_.max_size(); } + bool empty() const { + CHECK_EQ(tree_.empty(), checker_.empty()); + return tree_.empty(); + } + size_type height() const { return tree_.height(); } + size_type internal_nodes() const { return tree_.internal_nodes(); } + size_type leaf_nodes() const { return tree_.leaf_nodes(); } + size_type nodes() const { return tree_.nodes(); } + size_type bytes_used() const { return tree_.bytes_used(); } + double fullness() const { return tree_.fullness(); } + double overhead() const { return tree_.overhead(); } + + protected: + TreeType tree_; + const TreeType &const_tree_; + CheckerType checker_; +}; + +// A checker for unique sorted associative containers. TreeType is expected to +// be btree_{set,map} and CheckerType is expected to be {set,map}. +template +class unique_checker : public base_checker { + typedef base_checker super_type; + typedef unique_checker self_type; + + public: + typedef typename super_type::iterator iterator; + typedef typename super_type::value_type value_type; + + public: + // Default constructor. + unique_checker() + : super_type() { + } + // Copy constructor. + unique_checker(const self_type &x) + : super_type(x) { + } + // Range constructor. + template + unique_checker(InputIterator b, InputIterator e) { + insert(b, e); + } + + // Insertion routines. + pair insert(const value_type &x) { + int size = this->tree_.size(); + pair checker_res = + this->checker_.insert(x); + pair tree_res = this->tree_.insert(x); + CHECK_EQ(*tree_res.first, *checker_res.first); + CHECK_EQ(tree_res.second, checker_res.second); + CHECK_EQ(this->tree_.size(), this->checker_.size()); + CHECK_EQ(this->tree_.size(), size + tree_res.second); + return tree_res; + } + iterator insert(iterator position, const value_type &x) { + int size = this->tree_.size(); + pair checker_res = + this->checker_.insert(x); + iterator tree_res = this->tree_.insert(position, x); + CHECK_EQ(*tree_res, *checker_res.first); + CHECK_EQ(this->tree_.size(), this->checker_.size()); + CHECK_EQ(this->tree_.size(), size + checker_res.second); + return tree_res; + } + template + void insert(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert(*b); + } + } +}; + +// A checker for multiple sorted associative containers. TreeType is expected +// to be btree_{multiset,multimap} and CheckerType is expected to be +// {multiset,multimap}. +template +class multi_checker : public base_checker { + typedef base_checker super_type; + typedef multi_checker self_type; + + public: + typedef typename super_type::iterator iterator; + typedef typename super_type::value_type value_type; + + public: + // Default constructor. + multi_checker() + : super_type() { + } + // Copy constructor. + multi_checker(const self_type &x) + : super_type(x) { + } + // Range constructor. + template + multi_checker(InputIterator b, InputIterator e) { + insert(b, e); + } + + // Insertion routines. + iterator insert(const value_type &x) { + int size = this->tree_.size(); + typename CheckerType::iterator checker_res = this->checker_.insert(x); + iterator tree_res = this->tree_.insert(x); + CHECK_EQ(*tree_res, *checker_res); + CHECK_EQ(this->tree_.size(), this->checker_.size()); + CHECK_EQ(this->tree_.size(), size + 1); + return tree_res; + } + iterator insert(iterator position, const value_type &x) { + int size = this->tree_.size(); + typename CheckerType::iterator checker_res = this->checker_.insert(x); + iterator tree_res = this->tree_.insert(position, x); + CHECK_EQ(*tree_res, *checker_res); + CHECK_EQ(this->tree_.size(), this->checker_.size()); + CHECK_EQ(this->tree_.size(), size + 1); + return tree_res; + } + template + void insert(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert(*b); + } + } +}; + +char* GenerateDigits(char buf[16], int val, int maxval) { + DCHECK_LE(val, maxval); + int p = 15; + buf[p--] = 0; + while (maxval > 0) { + buf[p--] = '0' + (val % 10); + val /= 10; + maxval /= 10; + } + return buf + p + 1; +} + +template +struct Generator { + int maxval; + Generator(int m) + : maxval(m) { + } + K operator()(int i) const { + DCHECK_LE(i, maxval); + return i; + } +}; + +template <> +struct Generator { + int maxval; + Generator(int m) + : maxval(m) { + } + string operator()(int i) const { + char buf[16]; + return GenerateDigits(buf, i, maxval); + } +}; + +template <> +struct Generator { + int maxval; + Generator(int m) + : maxval(m) { + } + Cord operator()(int i) const { + char buf[16]; + return Cord(GenerateDigits(buf, i, maxval)); + } +}; + +template +struct Generator > { + Generator::type> tgen; + Generator::type> ugen; + + Generator(int m) + : tgen(m), + ugen(m) { + } + pair operator()(int i) const { + return make_pair(tgen(i), ugen(i)); + } +}; + +// Generate values for our tests and benchmarks. Value range is [0, maxval]. +const vector& GenerateNumbers(int n, int maxval) { + static ACMRandom rand(FLAGS_test_random_seed); + static vector values; + static set unique_values; + + if (values.size() < n) { + + for (int i = values.size(); i < n; i++) { + int value; + do { + value = rand.Next() % (maxval + 1); + } while (unique_values.find(value) != unique_values.end()); + + values.push_back(value); + unique_values.insert(value); + } + } + + return values; +} + +// Generates values in the range +// [0, 4 * min(FLAGS_benchmark_values, FLAGS_test_values)] +template +vector GenerateValues(int n) { + int two_times_max = 2 * max(FLAGS_benchmark_values, FLAGS_test_values); + int four_times_max = 2 * two_times_max; + DCHECK_LE(n, two_times_max); + const vector &nums = GenerateNumbers(n, four_times_max); + Generator gen(four_times_max); + vector vec; + + for (int i = 0; i < n; i++) { + vec.push_back(gen(nums[i])); + } + + return vec; +} + +template +double ContainerInfo(const set &s) { + int sizeof_node = sizeof(std::_Rb_tree_node); + int bytes_used = sizeof(s) + s.size() * sizeof_node; + double bytes_per_value = (double) bytes_used / s.size(); + VLOG(1) << " size=" << s.size() + << " bytes-used=" << bytes_used + << " bytes-per-value=" << bytes_per_value; + return bytes_per_value; +} + +template +double ContainerInfo(const multiset &s) { + int sizeof_node = sizeof(std::_Rb_tree_node); + int bytes_used = sizeof(s) + s.size() * sizeof_node; + double bytes_per_value = (double) bytes_used / s.size(); + VLOG(1) << " size=" << s.size() + << " bytes-used=" << bytes_used + << " bytes-per-value=" << bytes_per_value; + return bytes_per_value; +} + +template +double ContainerInfo(const map &m) { + int sizeof_node = sizeof(std::_Rb_tree_node >); + int bytes_used = sizeof(m) + m.size() * sizeof_node; + double bytes_per_value = (double) bytes_used / m.size(); + VLOG(1) << " size=" << m.size() + << " bytes-used=" << bytes_used + << " bytes-per-value=" << bytes_per_value; + return bytes_per_value; +} + +template +double ContainerInfo(const multimap &m) { + int sizeof_node = sizeof(std::_Rb_tree_node >); + int bytes_used = sizeof(m) + m.size() * sizeof_node; + double bytes_per_value = (double) bytes_used / m.size(); + VLOG(1) << " size=" << m.size() + << " bytes-used=" << bytes_used + << " bytes-per-value=" << bytes_per_value; + return bytes_per_value; +} + +template +double ContainerInfo(const btree_container

&b) { + double bytes_used = sizeof(b) + b.bytes_used(); + double bytes_per_value = (double) bytes_used / b.size(); + VLOG(1) << " size=" << b.size() + << " bytes-used=" << bytes_used + << " bytes-per-value=" << bytes_per_value + << " height=" << b.height() + << " internal-nodes=" << b.internal_nodes() + << " leaf-nodes=" << b.leaf_nodes() + << " fullness=" << b.fullness() + << " overhead=" << b.overhead(); + return bytes_per_value; +} + +template +void DoTest(const char *name, T *b, const vector &values) { + typename KeyOfValue::type key_of_value; + + T &mutable_b = *b; + const T &const_b = *b; + + // Test insert. + for (int i = 0; i < values.size(); ++i) { + mutable_b.insert(values[i]); + mutable_b.value_check(values[i]); + } + + const_b.verify(); + printf(" %s fullness=%0.2f overhead=%0.2f bytes-per-value=%0.2f\n", + name, const_b.fullness(), const_b.overhead(), + double(const_b.bytes_used()) / const_b.size()); + + // Test copy constructor. + T b_copy(const_b); + CHECK_EQ(b_copy.size(), const_b.size()); + CHECK_LE(b_copy.height(), const_b.height()); + CHECK_LE(b_copy.internal_nodes(), const_b.internal_nodes()); + CHECK_LE(b_copy.leaf_nodes(), const_b.leaf_nodes()); + for (int i = 0; i < values.size(); ++i) { + CHECK_EQ(*b_copy.find(key_of_value(values[i])), values[i]); + } + + // Test range constructor. + T b_range(const_b.begin(), const_b.end()); + CHECK_EQ(b_range.size(), const_b.size()); + CHECK_LE(b_range.height(), const_b.height()); + CHECK_LE(b_range.internal_nodes(), const_b.internal_nodes()); + CHECK_LE(b_range.leaf_nodes(), const_b.leaf_nodes()); + for (int i = 0; i < values.size(); ++i) { + CHECK_EQ(*b_range.find(key_of_value(values[i])), values[i]); + } + + // Test range insertion for values that already exist. + b_range.insert(b_copy.begin(), b_copy.end()); + b_range.verify(); + + // Test range insertion for new values. + b_range.clear(); + b_range.insert(b_copy.begin(), b_copy.end()); + CHECK_EQ(b_range.size(), b_copy.size()); + CHECK_EQ(b_range.height(), b_copy.height()); + CHECK_EQ(b_range.internal_nodes(), b_copy.internal_nodes()); + CHECK_EQ(b_range.leaf_nodes(), b_copy.leaf_nodes()); + for (int i = 0; i < values.size(); ++i) { + CHECK_EQ(*b_range.find(key_of_value(values[i])), values[i]); + } + + // Test assignment to self. Nothing should change. + b_range.operator=(b_range); + CHECK_EQ(b_range.size(), b_copy.size()); + CHECK_EQ(b_range.height(), b_copy.height()); + CHECK_EQ(b_range.internal_nodes(), b_copy.internal_nodes()); + CHECK_EQ(b_range.leaf_nodes(), b_copy.leaf_nodes()); + + // Test assignment of new values. + b_range.clear(); + b_range = b_copy; + CHECK_EQ(b_range.size(), b_copy.size()); + CHECK_EQ(b_range.height(), b_copy.height()); + CHECK_EQ(b_range.internal_nodes(), b_copy.internal_nodes()); + CHECK_EQ(b_range.leaf_nodes(), b_copy.leaf_nodes()); + + // Test swap. + b_range.clear(); + b_range.swap(b_copy); + CHECK_EQ(b_copy.size(), 0); + CHECK_EQ(b_range.size(), const_b.size()); + for (int i = 0; i < values.size(); ++i) { + CHECK_EQ(*b_range.find(key_of_value(values[i])), values[i]); + } + b_range.swap(b_copy); + + // Test erase via values. + for (int i = 0; i < values.size(); ++i) { + mutable_b.erase(key_of_value(values[i])); + // Erasing a non-existent key should have no effect. + CHECK_EQ(mutable_b.erase(key_of_value(values[i])), 0); + } + + const_b.verify(); + CHECK_EQ(const_b.internal_nodes(), 0); + CHECK_EQ(const_b.leaf_nodes(), 0); + CHECK_EQ(const_b.size(), 0); + + // Test erase via iterators. + mutable_b = b_copy; + for (int i = 0; i < values.size(); ++i) { + mutable_b.erase(mutable_b.find(key_of_value(values[i]))); + } + + const_b.verify(); + CHECK_EQ(const_b.internal_nodes(), 0); + CHECK_EQ(const_b.leaf_nodes(), 0); + CHECK_EQ(const_b.size(), 0); + + // Test insert with hint. + for (int i = 0; i < values.size(); i++) { + mutable_b.insert(mutable_b.upper_bound(key_of_value(values[i])), values[i]); + } + + const_b.verify(); + + // Test dumping of the btree to an ostream. There should be 1 line for each + // value. + ostringstream strm; + strm << mutable_b.tree(); + CHECK_EQ(mutable_b.size(), strcount(strm.str(), '\n')); + + // Test range erase. + mutable_b.erase(mutable_b.begin(), mutable_b.end()); + CHECK_EQ(mutable_b.size(), 0); + const_b.verify(); + + // First half. + mutable_b = b_copy; + typename T::iterator mutable_iter_end = mutable_b.begin(); + for (int i = 0; i < values.size() / 2; ++i) ++mutable_iter_end; + mutable_b.erase(mutable_b.begin(), mutable_iter_end); + CHECK_EQ(mutable_b.size(), values.size() - values.size() / 2); + const_b.verify(); + + // Second half. + mutable_b = b_copy; + typename T::iterator mutable_iter_begin = mutable_b.begin(); + for (int i = 0; i < values.size() / 2; ++i) ++mutable_iter_begin; + mutable_b.erase(mutable_iter_begin, mutable_b.end()); + CHECK_EQ(mutable_b.size(), values.size() / 2); + const_b.verify(); + + // Second quarter. + mutable_b = b_copy; + mutable_iter_begin = mutable_b.begin(); + for (int i = 0; i < values.size() / 4; ++i) ++mutable_iter_begin; + mutable_iter_end = mutable_iter_begin; + for (int i = 0; i < values.size() / 4; ++i) ++mutable_iter_end; + mutable_b.erase(mutable_iter_begin, mutable_iter_end); + CHECK_EQ(mutable_b.size(), values.size() - values.size() / 4); + const_b.verify(); + + mutable_b.clear(); +} + +template +void ConstTest() { + typedef typename T::value_type value_type; + typename KeyOfValue::type key_of_value; + + T mutable_b; + const T &const_b = mutable_b; + + // Insert a single value into the container and test looking it up. + value_type value = Generator(2)(2); + mutable_b.insert(value); + CHECK(mutable_b.find(key_of_value(value)) != const_b.end()); + CHECK(const_b.find(key_of_value(value)) != mutable_b.end()); + CHECK_EQ(*const_b.lower_bound(key_of_value(value)), value); + CHECK(const_b.upper_bound(key_of_value(value)) == const_b.end()); + CHECK_EQ(*const_b.equal_range(key_of_value(value)).first, value); + + // We can only create a non-const iterator from a non-const container. + typename T::iterator mutable_iter(mutable_b.begin()); + CHECK(mutable_iter == const_b.begin()); + CHECK(mutable_iter != const_b.end()); + CHECK(const_b.begin() == mutable_iter); + CHECK(const_b.end() != mutable_iter); + typename T::reverse_iterator mutable_riter(mutable_b.rbegin()); + CHECK(mutable_riter == const_b.rbegin()); + CHECK(mutable_riter != const_b.rend()); + CHECK(const_b.rbegin() == mutable_riter); + CHECK(const_b.rend() != mutable_riter); + + // We can create a const iterator from a non-const iterator. + typename T::const_iterator const_iter(mutable_iter); + CHECK(const_iter == mutable_b.begin()); + CHECK(const_iter != mutable_b.end()); + CHECK(mutable_b.begin() == const_iter); + CHECK(mutable_b.end() != const_iter); + typename T::const_reverse_iterator const_riter(mutable_riter); + CHECK(const_riter == mutable_b.rbegin()); + CHECK(const_riter != mutable_b.rend()); + CHECK(mutable_b.rbegin() == const_riter); + CHECK(mutable_b.rend() != const_riter); + + // Make sure various methods can be invoked on a const container. + const_b.verify(); + CHECK(!const_b.empty()); + CHECK_EQ(const_b.size(), 1); + CHECK_GT(const_b.max_size(), 0); + CHECK_EQ(const_b.height(), 1); + CHECK_EQ(const_b.count(key_of_value(value)), 1); + CHECK_EQ(const_b.internal_nodes(), 0); + CHECK_EQ(const_b.leaf_nodes(), 1); + CHECK_EQ(const_b.nodes(), 1); + CHECK_GT(const_b.bytes_used(), 0); + CHECK_GT(const_b.fullness(), 0); + CHECK_GT(const_b.overhead(), 0); +} + +template +void BtreeTest() { + ConstTest(); + + typedef typename base::remove_const::type V; + vector random_values = GenerateValues(FLAGS_test_values); + + unique_checker container; + + // Test key insertion/deletion in sorted order. + vector sorted_values(random_values); + sort(sorted_values.begin(), sorted_values.end()); + DoTest("sorted: ", &container, sorted_values); + + // Test key insertion/deletion in reverse sorted order. + reverse(sorted_values.begin(), sorted_values.end()); + DoTest("rsorted: ", &container, sorted_values); + + // Test key insertion/deletion in random order. + DoTest("random: ", &container, random_values); +} + +template +void BtreeMultiTest() { + ConstTest(); + + typedef typename base::remove_const::type V; + const vector& random_values = GenerateValues(FLAGS_test_values); + + multi_checker container; + + // Test keys in sorted order. + vector sorted_values(random_values); + sort(sorted_values.begin(), sorted_values.end()); + DoTest("sorted: ", &container, sorted_values); + + // Test keys in reverse sorted order. + reverse(sorted_values.begin(), sorted_values.end()); + DoTest("rsorted: ", &container, sorted_values); + + // Test keys in random order. + DoTest("random: ", &container, random_values); + + // Test keys in random order w/ duplicates. + vector duplicate_values(random_values); + duplicate_values.insert( + duplicate_values.end(), random_values.begin(), random_values.end()); + DoTest("duplicates:", &container, duplicate_values); + + // Test all identical keys. + vector identical_values(100); + fill(identical_values.begin(), identical_values.end(), Generator(2)(2)); + DoTest("identical: ", &container, identical_values); +} + +template +void BtreeArenaTest() { + typedef typename T::value_type value_type; + + UnsafeArena arena1(1000); + UnsafeArena arena2(1000); + T b1(typename T::key_compare(), &arena1); + T b2(typename T::key_compare(), &arena2); + + // This should swap the allocators! + swap(b1, b2); + + for (int i = 0; i < 1000; i++) { + b1.insert(Generator(1000)(i)); + } + + // We should have allocated out of arena2! + CHECK_LE(b1.bytes_used(), arena2.status().bytes_allocated()); + CHECK_GT(arena2.block_count(), arena1.block_count()); +} + +template +void BtreeMapTest() { + typedef typename T::value_type value_type; + typedef typename T::mapped_type mapped_type; + + mapped_type m = Generator(0)(0); + (void) m; + + T b; + + // Verify we can insert using operator[]. + for (int i = 0; i < 1000; i++) { + value_type v = Generator(1000)(i); + b[v.first] = v.second; + } + CHECK_EQ(b.size(), 1000); + + // Test whether we can use the "->" operator on iterators and + // reverse_iterators. This stresses the btree_map_params::pair_pointer + // mechanism. + CHECK_EQ(b.begin()->first, Generator(1000)(0).first); + CHECK_EQ(b.begin()->second, Generator(1000)(0).second); + CHECK_EQ(b.rbegin()->first, Generator(1000)(999).first); + CHECK_EQ(b.rbegin()->second, Generator(1000)(999).second); +} + +template +void BtreeMultiMapTest() { + typedef typename T::mapped_type mapped_type; + mapped_type m = Generator(0)(0); + (void) m; +} + +} // namespace btree +} // namespace util + +#endif // UTIL_BTREE_BTREE_TEST_H__ diff --git a/btree_test_flags.cc b/btree_test_flags.cc new file mode 100644 index 00000000..512c24e1 --- /dev/null +++ b/btree_test_flags.cc @@ -0,0 +1,9 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: pmattis@google.com (Peter Mattis) + +#include "base/commandlineflags.h" + +DEFINE_int32(test_values, 10000, + "The number of values to use for tests."); +DEFINE_int32(benchmark_values, 1000000, + "The number of values to use for benchmarks."); diff --git a/btree_test_program.cc b/btree_test_program.cc new file mode 100644 index 00000000..0c2dcf88 --- /dev/null +++ b/btree_test_program.cc @@ -0,0 +1,64 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: leg@google.com (Lawrence Greenfield) + +#include "base/init_google.h" +#include "base/logging.h" +#include "devtools/gdb/component/gdb_test_utils.h" +#include "util/btree/btree_map.h" +#include "util/btree/btree_set.h" + +using util::btree::btree_set; +using util::btree::btree_multiset; +using util::btree::btree_map; +using util::btree::btree_multimap; + +static btree_set* empty_set; +static btree_set* small_set; +static btree_set* big_set; +static btree_multiset* small_multiset; + +static btree_map* empty_map; +static btree_map* small_map; +static btree_map* big_map; +static btree_multimap* small_multimap; + +static void SetupBtreeSets() { + empty_set = new btree_set; + small_set = new btree_set; + small_multiset = new btree_multiset; + big_set = new btree_set; + + for (int i = 0; i < 10; ++i) { + small_set->insert(i); + small_multiset->insert(i / 2); + } + + for (int i = 0; i < 80; ++i) { + big_set->insert(i); + } +} + +static void SetupBtreeMaps() { + empty_map = new btree_map; + small_map = new btree_map; + small_multimap = new btree_multimap; + big_map = new btree_map; + + for (int i = 0; i < 10; ++i) { + small_map->insert(make_pair(i, i * 13)); + small_multimap->insert(make_pair(i / 2, i)); + } + + for (int i = 0; i < 80; ++i) { + big_map->insert(make_pair(i, i * 7)); + } +} + +int main(int argc, char** argv) { + FLAGS_logtostderr = true; + InitGoogle(argv[0], &argc, &argv, true); + SetupBtreeSets(); + SetupBtreeMaps(); + StopHereForDebugger(); + return 0; +} diff --git a/safe_btree.h b/safe_btree.h new file mode 100644 index 00000000..b3db5360 --- /dev/null +++ b/safe_btree.h @@ -0,0 +1,379 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: pmattis@google.com (Peter Mattis) +// +// A safe_btree<> wraps around a btree<> and removes the caveat that insertion +// and deletion invalidate iterators. A safe_btree<> maintains a generation +// number that is incremented on every mutation. A safe_btree<>::iterator keeps +// a pointer to the safe_btree<> it came from, the generation of the tree when +// it was last validated and the key the underlying btree<>::iterator points +// to. If an iterator is accessed and its generation differs from the tree +// generation it is revalidated. + +#ifndef UTIL_BTREE_SAFE_BTREE_H__ +#define UTIL_BTREE_SAFE_BTREE_H__ + +#include +#include +#include + +#include "base/integral_types.h" +#include "base/logging.h" +#include "util/btree/btree.h" + +namespace util { +namespace btree { + +template +class safe_btree_iterator { + public: + typedef typename Iterator::key_type key_type; + typedef typename Iterator::value_type value_type; + typedef typename Iterator::size_type size_type; + typedef typename Iterator::difference_type difference_type; + typedef typename Iterator::pointer pointer; + typedef typename Iterator::reference reference; + typedef typename Iterator::const_pointer const_pointer; + typedef typename Iterator::const_reference const_reference; + typedef typename Iterator::iterator_category iterator_category; + typedef typename Tree::iterator iterator; + typedef typename Tree::const_iterator const_iterator; + typedef safe_btree_iterator self_type; + + void update() const { + if (iter_ != tree_->internal_btree()->end()) { + // A positive generation indicates a valid key. + generation_ = tree_->generation(); + key_ = iter_.key(); + } else { + // Use a negative generation to indicate iter_ points to end(). + generation_ = -tree_->generation(); + } + } + + public: + safe_btree_iterator() + : generation_(0), + key_(), + iter_(), + tree_(NULL) { + } + safe_btree_iterator(const iterator &x) + : generation_(x.generation()), + key_(x.key()), + iter_(x.iter()), + tree_(x.tree()) { + } + safe_btree_iterator(Tree *tree, const Iterator &iter) + : generation_(), + key_(), + iter_(iter), + tree_(tree) { + update(); + } + + Tree* tree() const { return tree_; } + int64 generation() const { return generation_; } + + Iterator* mutable_iter() const { + if (generation_ != tree_->generation()) { + if (generation_ > 0) { + // This does the wrong thing for a multi{set,map}. If my iter was + // pointing to the 2nd of 2 values with the same key, then this will + // reset it to point to the first. This is why we don't provide a + // safe_btree_multi{set,map}. + iter_ = tree_->internal_btree()->lower_bound(key_); + update(); + } else if (-generation_ != tree_->generation()) { + iter_ = tree_->internal_btree()->end(); + generation_ = -tree_->generation(); + } + } + return &iter_; + } + const Iterator& iter() const { + return *mutable_iter(); + } + + // Equality/inequality operators. + bool operator==(const const_iterator &x) const { + return iter() == x.iter(); + } + bool operator!=(const const_iterator &x) const { + return iter() != x.iter(); + } + + // Accessors for the key/value the iterator is pointing at. + const key_type& key() const { + return key_; + } + reference operator*() const { + DCHECK_GT(generation_, 0); + return iter().operator*(); + } + pointer operator->() const { + DCHECK_GT(generation_, 0); + return iter().operator->(); + } + + // Increment/decrement operators. + self_type& operator++() { + ++(*mutable_iter()); + update(); + return *this; + } + self_type& operator--() { + --(*mutable_iter()); + update(); + return *this; + } + self_type operator++(int) { + self_type tmp = *this; + ++*this; + return tmp; + } + self_type operator--(int) { + self_type tmp = *this; + --*this; + return tmp; + } + + private: + // The generation of the tree when "iter" was updated. + mutable int64 generation_; + // The key the iterator points to. + mutable key_type key_; + // The underlying iterator. + mutable Iterator iter_; + // The tree the iterator is associated with. + Tree *tree_; +}; + +template +class safe_btree { + typedef safe_btree self_type; + + typedef btree btree_type; + typedef typename btree_type::iterator tree_iterator; + typedef typename btree_type::const_iterator tree_const_iterator; + + public: + typedef typename btree_type::params_type params_type; + typedef typename btree_type::key_type key_type; + typedef typename btree_type::data_type data_type; + typedef typename btree_type::mapped_type mapped_type; + typedef typename btree_type::value_type value_type; + typedef typename btree_type::key_compare key_compare; + typedef typename btree_type::allocator_type allocator_type; + typedef typename btree_type::pointer pointer; + typedef typename btree_type::const_pointer const_pointer; + typedef typename btree_type::reference reference; + typedef typename btree_type::const_reference const_reference; + typedef typename btree_type::size_type size_type; + typedef typename btree_type::difference_type difference_type; + typedef safe_btree_iterator iterator; + typedef safe_btree_iterator< + const self_type, tree_const_iterator> const_iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef std::reverse_iterator reverse_iterator; + + public: + // Default constructor. + safe_btree(const key_compare &comp, const allocator_type &alloc) + : tree_(comp, alloc), + generation_(1) { + } + + // Copy constructor. + safe_btree(const self_type &x) + : tree_(x.tree_), + generation_(1) { + } + + iterator begin() { + return iterator(this, tree_.begin()); + } + const_iterator begin() const { + return const_iterator(this, tree_.begin()); + } + iterator end() { + return iterator(this, tree_.end()); + } + const_iterator end() const { + return const_iterator(this, tree_.end()); + } + reverse_iterator rbegin() { + return reverse_iterator(end()); + } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + reverse_iterator rend() { + return reverse_iterator(begin()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + // Lookup routines. + iterator lower_bound(const key_type &key) { + return iterator(this, tree_.lower_bound(key)); + } + const_iterator lower_bound(const key_type &key) const { + return const_iterator(this, tree_.lower_bound(key)); + } + iterator upper_bound(const key_type &key) { + return iterator(this, tree_.upper_bound(key)); + } + const_iterator upper_bound(const key_type &key) const { + return const_iterator(this, tree_.upper_bound(key)); + } + pair equal_range(const key_type &key) { + pair p = tree_.equal_range(key); + return make_pair(iterator(this, p.first), + iterator(this, p.second)); + } + pair equal_range(const key_type &key) const { + pair p = tree_.equal_range(key); + return make_pair(const_iterator(this, p.first), + const_iterator(this, p.second)); + } + iterator find_unique(const key_type &key) { + return iterator(this, tree_.find_unique(key)); + } + const_iterator find_unique(const key_type &key) const { + return const_iterator(this, tree_.find_unique(key)); + } + iterator find_multi(const key_type &key) { + return iterator(this, tree_.find_multi(key)); + } + const_iterator find_multi(const key_type &key) const { + return const_iterator(this, tree_.find_multi(key)); + } + size_type count_unique(const key_type &key) const { + return tree_.count_unique(key); + } + size_type count_multi(const key_type &key) const { + return tree_.count_multi(key); + } + + // Insertion routines. + template + pair insert_unique(const key_type &key, ValuePointer value) { + pair p = tree_.insert_unique(key, value); + generation_ += p.second; + return make_pair(iterator(this, p.first), p.second); + } + pair insert_unique(const value_type &v) { + pair p = tree_.insert_unique(v); + generation_ += p.second; + return make_pair(iterator(this, p.first), p.second); + } + iterator insert_unique(iterator position, const value_type &v) { + tree_iterator tree_pos = position.iter(); + ++generation_; + return iterator(this, tree_.insert_unique(tree_pos, v)); + } + template + void insert_unique(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert_unique(*b); + } + } + iterator insert_multi(const value_type &v) { + ++generation_; + return iterator(this, tree_.insert_multi(v)); + } + iterator insert_multi(iterator position, const value_type &v) { + tree_iterator tree_pos = position.iter(); + ++generation_; + return iterator(this, tree_.insert_multi(tree_pos, v)); + } + template + void insert_multi(InputIterator b, InputIterator e) { + for (; b != e; ++b) { + insert_multi(*b); + } + } + self_type& operator=(const self_type &x) { + if (&x == this) { + // Don't copy onto ourselves. + return *this; + } + ++generation_; + tree_ = x.tree_; + return *this; + } + + // Deletion routines. + void erase(const iterator &begin, const iterator &end) { + tree_.erase(begin.iter(), end.iter()); + ++generation_; + } + // Erase the specified iterator from the btree. The iterator must be valid + // (i.e. not equal to end()). Return an iterator pointing to the node after + // the one that was erased (or end() if none exists). + iterator erase(iterator iter) { + tree_iterator res = tree_.erase(iter.iter()); + ++generation_; + return iterator(this, res); + } + int erase_unique(const key_type &key) { + int res = tree_.erase_unique(key); + generation_ += res; + return res; + } + int erase_multi(const key_type &key) { + int res = tree_.erase_multi(key); + generation_ += res; + return res; + } + + // Access to the underlying btree. + btree_type* internal_btree() { return &tree_; } + const btree_type* internal_btree() const { return &tree_; } + + // Utility routines. + void clear() { + ++generation_; + tree_.clear(); + } + void swap(self_type &x) { + ++generation_; + ++x.generation_; + tree_.swap(x.tree_); + } + void dump(ostream &os) const { + tree_.dump(os); + } + void verify() const { + tree_.verify(); + } + int64 generation() const { + return generation_; + } + key_compare key_comp() const { return tree_.key_comp(); } + + // Size routines. + size_type size() const { return tree_.size(); } + size_type max_size() const { return tree_.max_size(); } + bool empty() const { return tree_.empty(); } + size_type height() const { return tree_.height(); } + size_type internal_nodes() const { return tree_.internal_nodes(); } + size_type leaf_nodes() const { return tree_.leaf_nodes(); } + size_type nodes() const { return tree_.nodes(); } + size_type bytes_used() const { return tree_.bytes_used(); } + static double average_bytes_per_value() { + return btree_type::average_bytes_per_value(); + } + double fullness() const { return tree_.fullness(); } + double overhead() const { return tree_.overhead(); } + + private: + btree_type tree_; + int64 generation_; +}; + +} // namespace btree +} // namespace util + +#endif // UTIL_BTREE_SAFE_BTREE_H__ diff --git a/safe_btree_map.h b/safe_btree_map.h new file mode 100644 index 00000000..74c5fa37 --- /dev/null +++ b/safe_btree_map.h @@ -0,0 +1,70 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: pmattis@google.com (Peter Mattis) +// +// The safe_btree_map<> is like btree_map<> except that it removes the caveat +// about insertion and deletion invalidating existing iterators at a small cost +// in making iterators larger and slower. + +#ifndef UTIL_BTREE_SAFE_BTREE_MAP_H__ +#define UTIL_BTREE_SAFE_BTREE_MAP_H__ + +#include +#include +#include + +#include "util/btree/btree_container.h" +#include "util/btree/btree_map.h" +#include "util/btree/safe_btree.h" + +namespace util { +namespace btree { + +// The safe_btree_map class is needed mainly for it's constructors. +template , + typename Alloc = std::allocator >, + int TargetNodeSize = 256> +class safe_btree_map : public btree_map_container< + safe_btree > > { + + typedef safe_btree_map self_type; + typedef btree_map_params< + Key, Value, Compare, Alloc, TargetNodeSize> params_type; + typedef safe_btree btree_type; + typedef btree_map_container super_type; + + public: + typedef typename btree_type::key_compare key_compare; + typedef typename btree_type::allocator_type allocator_type; + + public: + // Default constructor. + safe_btree_map(const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + } + + // Copy constructor. + safe_btree_map(const self_type &x) + : super_type(x) { + } + + // Range constructor. + template + safe_btree_map(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(b, e, comp, alloc) { + } +}; + +template +inline void swap(safe_btree_map &x, + safe_btree_map &y) { + x.swap(y); +} + +} // namespace btree +} // namespace util + +#endif // UTIL_BTREE_SAFE_BTREE_MAP_H__ diff --git a/safe_btree_set.h b/safe_btree_set.h new file mode 100644 index 00000000..329a46f9 --- /dev/null +++ b/safe_btree_set.h @@ -0,0 +1,68 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: pmattis@google.com (Peter Mattis) +// +// The safe_btree_set<> is like btree_set<> except that it removes the caveat +// about insertion and deletion invalidating existing iterators at a small cost +// in making iterators larger and slower. + +#ifndef UTIL_BTREE_SAFE_BTREE_SET_H__ +#define UTIL_BTREE_SAFE_BTREE_SET_H__ + +#include +#include + +#include "util/btree/btree_container.h" +#include "util/btree/btree_set.h" +#include "util/btree/safe_btree.h" + +namespace util { +namespace btree { + +// The safe_btree_set class is needed mainly for it's constructors. +template , + typename Alloc = std::allocator, + int TargetNodeSize = 256> +class safe_btree_set : public btree_unique_container< + safe_btree > > { + + typedef safe_btree_set self_type; + typedef btree_set_params params_type; + typedef safe_btree btree_type; + typedef btree_unique_container super_type; + + public: + typedef typename btree_type::key_compare key_compare; + typedef typename btree_type::allocator_type allocator_type; + + public: + // Default constructor. + safe_btree_set(const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(comp, alloc) { + } + + // Copy constructor. + safe_btree_set(const self_type &x) + : super_type(x) { + } + + // Range constructor. + template + safe_btree_set(InputIterator b, InputIterator e, + const key_compare &comp = key_compare(), + const allocator_type &alloc = allocator_type()) + : super_type(b, e, comp, alloc) { + } +}; + +template +inline void swap(safe_btree_set &x, + safe_btree_set &y) { + x.swap(y); +} + +} // namespace btree +} // namespace util + +#endif // UTIL_BTREE_SAFE_BTREE_SET_H__ diff --git a/safe_btree_test.cc b/safe_btree_test.cc new file mode 100644 index 00000000..d86623f9 --- /dev/null +++ b/safe_btree_test.cc @@ -0,0 +1,67 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: jmacd@google.com (Josh MacDonald) +// Author: pmattis@google.com (Peter Mattis) + +// TODO(pmattis): Add some tests that iterators are not invalidated by +// insertion and deletion. + +#include +#include +#include +#include +#include + +#include "base/arena-inl.h" +#include "base/init_google.h" +#include "base/integral_types.h" +#include "base/logging.h" +#include "strings/cord.h" +#include "testing/base/public/gunit.h" +#include "util/btree/btree_test.h" +#include "util/btree/safe_btree_map.h" +#include "util/btree/safe_btree_set.h" + +class UnsafeArena; + +namespace util { +namespace btree { +namespace { + +template +void SetTest() { + typedef ArenaAllocator ArenaAlloc; + BtreeTest, allocator, N>, set >(); + BtreeArenaTest, ArenaAlloc, N> >(); +} + +template +void MapTest() { + typedef ArenaAllocator ArenaAlloc; + BtreeTest, allocator, N>, map >(); + BtreeArenaTest, ArenaAlloc, N> >(); + BtreeMapTest, allocator, N> >(); +} + +TEST(SafeBtree, set_int32_32) { SetTest(); } +TEST(SafeBtree, set_int32_64) { SetTest(); } +TEST(SafeBtree, set_int32_128) { SetTest(); } +TEST(SafeBtree, set_int32_256) { SetTest(); } +TEST(SafeBtree, set_int64_256) { SetTest(); } +TEST(SafeBtree, set_string_256) { SetTest(); } +TEST(SafeBtree, set_cord_256) { SetTest(); } +TEST(SafeBtree, set_pair_256) { SetTest, 256>(); } +TEST(SafeBtree, map_int32_256) { MapTest(); } +TEST(SafeBtree, map_int64_256) { MapTest(); } +TEST(SafeBtree, map_string_256) { MapTest(); } +TEST(SafeBtree, map_cord_256) { MapTest(); } +TEST(SafeBtree, map_pair_256) { MapTest, 256>(); } + +} // namespace +} // namespace btree +} // namespace util + +int main(int argc, char **argv) { + FLAGS_logtostderr = true; + InitGoogle(argv[0], &argc, &argv, true); + return RUN_ALL_TESTS(); +}