summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-27 01:03:01 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-27 01:03:01 +0000
commit0c8b8949074dae8be097a093f7bde6580621b692 (patch)
tree0ec0efc1ea24e74822ea3e8fad46d8dea7bf8d89
parenta8cdf1e19df5f77ebd2bc5d4251b987459f44f23 (diff)
downloadchromium_src-0c8b8949074dae8be097a093f7bde6580621b692.zip
chromium_src-0c8b8949074dae8be097a093f7bde6580621b692.tar.gz
chromium_src-0c8b8949074dae8be097a093f7bde6580621b692.tar.bz2
Make IDMap respect C++ rule of three,
i.e. when the dtor is defined, copy ctor and assignment operator should also be defined. We can't use DISALLOW_COPY_AND_ASSIGN because IDMap is being copied/assigned in the codebase. BUG=none TEST=none Review URL: https://chromiumcodereview.appspot.com/11263023 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@164473 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/id_map.h34
-rw-r--r--base/id_map_unittest.cc69
2 files changed, 100 insertions, 3 deletions
diff --git a/base/id_map.h b/base/id_map.h
index d3f23aa..bb9f05b 100644
--- a/base/id_map.h
+++ b/base/id_map.h
@@ -110,6 +110,12 @@ class IDMap : public base::NonThreadSafe {
return data_.size() - removed_ids_.size();
}
+#if defined(UNIT_TEST)
+ int iteration_depth() const {
+ return iteration_depth_;
+ }
+#endif // defined(UNIT_TEST)
+
// It is safe to remove elements from the map during iteration. All iterators
// will remain valid.
template<class ReturnType>
@@ -118,13 +124,29 @@ class IDMap : public base::NonThreadSafe {
Iterator(IDMap<T, OS>* map)
: map_(map),
iter_(map_->data_.begin()) {
- DCHECK(map->CalledOnValidThread());
- ++map_->iteration_depth_;
- SkipRemovedEntries();
+ Init();
+ }
+
+ Iterator(const Iterator& iter)
+ : map_(iter.map_),
+ iter_(iter.iter_) {
+ Init();
+ }
+
+ const Iterator& operator=(const Iterator& iter) {
+ map_ = iter.map;
+ iter_ = iter.iter;
+ Init();
+ return *this;
}
~Iterator() {
DCHECK(map_->CalledOnValidThread());
+
+ // We're going to decrement iteration depth. Make sure it's greater than
+ // zero so that it doesn't become negative.
+ DCHECK_LT(0, map_->iteration_depth_);
+
if (--map_->iteration_depth_ == 0)
map_->Compact();
}
@@ -151,6 +173,12 @@ class IDMap : public base::NonThreadSafe {
}
private:
+ void Init() {
+ DCHECK(map_->CalledOnValidThread());
+ ++map_->iteration_depth_;
+ SkipRemovedEntries();
+ }
+
void SkipRemovedEntries() {
while (iter_ != map_->data_.end() &&
map_->removed_ids_.find(iter_->first) !=
diff --git a/base/id_map_unittest.cc b/base/id_map_unittest.cc
index feb150b..c4838ee 100644
--- a/base/id_map_unittest.cc
+++ b/base/id_map_unittest.cc
@@ -54,6 +54,8 @@ TEST_F(IDMapTest, Basic) {
map.AddWithID(&obj2, 2);
EXPECT_EQ(&obj1, map.Lookup(1));
EXPECT_EQ(&obj2, map.Lookup(2));
+
+ EXPECT_EQ(0, map.iteration_depth());
}
TEST_F(IDMapTest, IteratorRemainsValidWhenRemovingCurrentElement) {
@@ -69,6 +71,9 @@ TEST_F(IDMapTest, IteratorRemainsValidWhenRemovingCurrentElement) {
{
IDMap<TestObject>::const_iterator iter(&map);
+
+ EXPECT_EQ(1, map.iteration_depth());
+
while (!iter.IsAtEnd()) {
map.Remove(iter.GetCurrentKey());
iter.Advance();
@@ -82,6 +87,8 @@ TEST_F(IDMapTest, IteratorRemainsValidWhenRemovingCurrentElement) {
EXPECT_TRUE(map.IsEmpty());
EXPECT_EQ(0U, map.size());
+
+ EXPECT_EQ(0, map.iteration_depth());
}
TEST_F(IDMapTest, IteratorRemainsValidWhenRemovingOtherElements) {
@@ -97,6 +104,8 @@ TEST_F(IDMapTest, IteratorRemainsValidWhenRemovingOtherElements) {
int counter = 0;
for (IDMap<TestObject>::const_iterator iter(&map);
!iter.IsAtEnd(); iter.Advance()) {
+ EXPECT_EQ(1, map.iteration_depth());
+
switch (counter) {
case 0:
EXPECT_EQ(ids[0], iter.GetCurrentKey());
@@ -120,6 +129,66 @@ TEST_F(IDMapTest, IteratorRemainsValidWhenRemovingOtherElements) {
counter++;
}
+
+ EXPECT_EQ(0, map.iteration_depth());
+}
+
+TEST_F(IDMapTest, CopyIterator) {
+ IDMap<TestObject> map;
+
+ TestObject obj1;
+ TestObject obj2;
+ TestObject obj3;
+
+ map.Add(&obj1);
+ map.Add(&obj2);
+ map.Add(&obj3);
+
+ EXPECT_EQ(0, map.iteration_depth());
+
+ {
+ IDMap<TestObject>::const_iterator iter1(&map);
+ EXPECT_EQ(1, map.iteration_depth());
+
+ // Make sure that copying the iterator correctly increments
+ // map's iteration depth.
+ IDMap<TestObject>::const_iterator iter2(iter1);
+ EXPECT_EQ(2, map.iteration_depth());
+ }
+
+ // Make sure after destroying all iterators the map's iteration depth
+ // returns to initial state.
+ EXPECT_EQ(0, map.iteration_depth());
+}
+
+TEST_F(IDMapTest, AssignIterator) {
+ IDMap<TestObject> map;
+
+ TestObject obj1;
+ TestObject obj2;
+ TestObject obj3;
+
+ map.Add(&obj1);
+ map.Add(&obj2);
+ map.Add(&obj3);
+
+ EXPECT_EQ(0, map.iteration_depth());
+
+ {
+ IDMap<TestObject>::const_iterator iter1(&map);
+ EXPECT_EQ(1, map.iteration_depth());
+
+ IDMap<TestObject>::const_iterator iter2(&map);
+ EXPECT_EQ(2, map.iteration_depth());
+
+ // Make sure that assigning the iterator correctly updates
+ // map's iteration depth (-1 for destruction, +1 for assignment).
+ EXPECT_EQ(2, map.iteration_depth());
+ }
+
+ // Make sure after destroying all iterators the map's iteration depth
+ // returns to initial state.
+ EXPECT_EQ(0, map.iteration_depth());
}
TEST_F(IDMapTest, OwningPointersDeletesThemOnRemove) {