diff --git a/btree_container.h b/btree_container.h index 6703d1bf..beebf6ff 100644 --- a/btree_container.h +++ b/btree_container.h @@ -106,6 +106,23 @@ class btree_container { double fullness() const { return tree_.fullness(); } double overhead() const { return tree_.overhead(); } + bool operator==(const self_type& x) const { + if (size() != x.size()) { + return false; + } + for (const_iterator i = begin(), xi = x.begin(); i != end(); ++i, ++xi) { + if (*i != *xi) { + return false; + } + } + return true; + } + + bool operator!=(const self_type& other) const { + return !operator==(other); + } + + protected: Tree tree_; }; diff --git a/btree_test.cc b/btree_test.cc index d984ec64..335318de 100644 --- a/btree_test.cc +++ b/btree_test.cc @@ -188,6 +188,60 @@ TEST(Btree, IteratorIncrementBy) { } } +TEST(Btree, Comparison) { + const int kSetSize = 1201; + btree_set my_set; + for (int i = 0; i < kSetSize; ++i) { + my_set.insert(i); + } + btree_set my_set_copy(my_set); + EXPECT_TRUE(my_set_copy == my_set); + EXPECT_TRUE(my_set == my_set_copy); + EXPECT_FALSE(my_set_copy != my_set); + EXPECT_FALSE(my_set != my_set_copy); + + my_set.insert(kSetSize); + EXPECT_FALSE(my_set_copy == my_set); + EXPECT_FALSE(my_set == my_set_copy); + EXPECT_TRUE(my_set_copy != my_set); + EXPECT_TRUE(my_set != my_set_copy); + + my_set.erase(kSetSize - 1); + EXPECT_FALSE(my_set_copy == my_set); + EXPECT_FALSE(my_set == my_set_copy); + EXPECT_TRUE(my_set_copy != my_set); + EXPECT_TRUE(my_set != my_set_copy); + + btree_map my_map; + for (int i = 0; i < kSetSize; ++i) { + my_map[string(i, 'a')] = i; + } + btree_map my_map_copy(my_map); + EXPECT_TRUE(my_map_copy == my_map); + EXPECT_TRUE(my_map == my_map_copy); + EXPECT_FALSE(my_map_copy != my_map); + EXPECT_FALSE(my_map != my_map_copy); + + ++my_map_copy[string(7, 'a')]; + EXPECT_FALSE(my_map_copy == my_map); + EXPECT_FALSE(my_map == my_map_copy); + EXPECT_TRUE(my_map_copy != my_map); + EXPECT_TRUE(my_map != my_map_copy); + + my_map_copy = my_map; + my_map["hello"] = kSetSize; + EXPECT_FALSE(my_map_copy == my_map); + EXPECT_FALSE(my_map == my_map_copy); + EXPECT_TRUE(my_map_copy != my_map); + EXPECT_TRUE(my_map != my_map_copy); + + my_map.erase(string(kSetSize - 1, 'a')); + EXPECT_FALSE(my_map_copy == my_map); + EXPECT_FALSE(my_map == my_map_copy); + EXPECT_TRUE(my_map_copy != my_map); + EXPECT_TRUE(my_map != my_map_copy); +} + } // namespace } // namespace btree } // namespace util diff --git a/safe_btree.h b/safe_btree.h index b3db5360..2a354232 100644 --- a/safe_btree.h +++ b/safe_btree.h @@ -1,4 +1,4 @@ -// Copyright 2007 Google Inc. All Rights Reserved. +// Copyright 2007, 2012 Google Inc. All Rights Reserved. // Author: pmattis@google.com (Peter Mattis) // // A safe_btree<> wraps around a btree<> and removes the caveat that insertion @@ -8,6 +8,11 @@ // 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. +// +// References and pointers returned by safe_btree iterators are not safe. +// +// See the incorrect usage examples mentioned in safe_btree_set.h and +// safe_btree_map.h. #ifndef UTIL_BTREE_SAFE_BTREE_H__ #define UTIL_BTREE_SAFE_BTREE_H__ @@ -106,10 +111,14 @@ class safe_btree_iterator { const key_type& key() const { return key_; } + // This reference value is potentially invalidated by any non-const + // method on the tree; it is NOT safe. reference operator*() const { DCHECK_GT(generation_, 0); return iter().operator*(); } + // This pointer value is potentially invalidated by any non-const + // method on the tree; it is NOT safe. pointer operator->() const { DCHECK_GT(generation_, 0); return iter().operator->(); diff --git a/safe_btree_map.h b/safe_btree_map.h index 74c5fa37..11274930 100644 --- a/safe_btree_map.h +++ b/safe_btree_map.h @@ -1,10 +1,20 @@ -// Copyright 2007 Google Inc. All Rights Reserved. +// Copyright 2007, 2012 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. - +// +// Revalidation occurs whenever an iterator is accessed. References +// and pointers returned by safe_btree_map<> iterators are not stable, +// they are potentially invalidated by any non-const method on the map. +// +// BEGIN INCORRECT EXAMPLE +// for (auto i = safe_map->begin(); i != safe_map->end(); ++i) { +// const T *value = &i->second; // DO NOT DO THIS +// [code that modifies safe_map and uses value]; +// } +// END INCORRECT EXAMPLE #ifndef UTIL_BTREE_SAFE_BTREE_MAP_H__ #define UTIL_BTREE_SAFE_BTREE_MAP_H__ @@ -19,7 +29,7 @@ namespace util { namespace btree { -// The safe_btree_map class is needed mainly for it's constructors. +// The safe_btree_map class is needed mainly for its constructors. template , typename Alloc = std::allocator >, diff --git a/safe_btree_set.h b/safe_btree_set.h index 329a46f9..72ed2d91 100644 --- a/safe_btree_set.h +++ b/safe_btree_set.h @@ -1,9 +1,20 @@ -// Copyright 2007 Google Inc. All Rights Reserved. +// Copyright 2007, 2012 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. +// +// Revalidation occurs whenever an iterator is accessed. References +// and pointers returned by safe_btree_map<> iterators are not stable, +// they are potentially invalidated by any non-const method on the set. +// +// BEGIN INCORRECT EXAMPLE +// for (auto i = safe_set->begin(); i != safe_set->end(); ++i) { +// const T &value = *i; // DO NOT DO THIS +// [code that modifies safe_set and uses value]; +// } +// END INCORRECT EXAMPLE #ifndef UTIL_BTREE_SAFE_BTREE_SET_H__ #define UTIL_BTREE_SAFE_BTREE_SET_H__ @@ -18,7 +29,7 @@ namespace util { namespace btree { -// The safe_btree_set class is needed mainly for it's constructors. +// The safe_btree_set class is needed mainly for its constructors. template , typename Alloc = std::allocator, diff --git a/safe_btree_test.cc b/safe_btree_test.cc index d86623f9..cf179b28 100644 --- a/safe_btree_test.cc +++ b/safe_btree_test.cc @@ -56,6 +56,60 @@ TEST(SafeBtree, map_string_256) { MapTest(); } TEST(SafeBtree, map_cord_256) { MapTest(); } TEST(SafeBtree, map_pair_256) { MapTest, 256>(); } +TEST(SafeBtree, Comparison) { + const int kSetSize = 1201; + safe_btree_set my_set; + for (int i = 0; i < kSetSize; ++i) { + my_set.insert(i); + } + safe_btree_set my_set_copy(my_set); + EXPECT_TRUE(my_set_copy == my_set); + EXPECT_TRUE(my_set == my_set_copy); + EXPECT_FALSE(my_set_copy != my_set); + EXPECT_FALSE(my_set != my_set_copy); + + my_set.insert(kSetSize); + EXPECT_FALSE(my_set_copy == my_set); + EXPECT_FALSE(my_set == my_set_copy); + EXPECT_TRUE(my_set_copy != my_set); + EXPECT_TRUE(my_set != my_set_copy); + + my_set.erase(kSetSize - 1); + EXPECT_FALSE(my_set_copy == my_set); + EXPECT_FALSE(my_set == my_set_copy); + EXPECT_TRUE(my_set_copy != my_set); + EXPECT_TRUE(my_set != my_set_copy); + + safe_btree_map my_map; + for (int i = 0; i < kSetSize; ++i) { + my_map[string(i, 'a')] = i; + } + safe_btree_map my_map_copy(my_map); + EXPECT_TRUE(my_map_copy == my_map); + EXPECT_TRUE(my_map == my_map_copy); + EXPECT_FALSE(my_map_copy != my_map); + EXPECT_FALSE(my_map != my_map_copy); + + ++my_map_copy[string(7, 'a')]; + EXPECT_FALSE(my_map_copy == my_map); + EXPECT_FALSE(my_map == my_map_copy); + EXPECT_TRUE(my_map_copy != my_map); + EXPECT_TRUE(my_map != my_map_copy); + + my_map_copy = my_map; + my_map["hello"] = kSetSize; + EXPECT_FALSE(my_map_copy == my_map); + EXPECT_FALSE(my_map == my_map_copy); + EXPECT_TRUE(my_map_copy != my_map); + EXPECT_TRUE(my_map != my_map_copy); + + my_map.erase(string(kSetSize - 1, 'a')); + EXPECT_FALSE(my_map_copy == my_map); + EXPECT_FALSE(my_map == my_map_copy); + EXPECT_TRUE(my_map_copy != my_map); + EXPECT_TRUE(my_map != my_map_copy); +} + } // namespace } // namespace btree } // namespace util