Fully implement string interning.

Also, more const.

Change-Id: I09cae88d677e8e6e42d0fa9b5d1093c79d225e66
diff --git a/src/intern_table_test.cc b/src/intern_table_test.cc
index a01c6dd..d230a01 100644
--- a/src/intern_table_test.cc
+++ b/src/intern_table_test.cc
@@ -11,10 +11,10 @@
 
 TEST_F(InternTableTest, Intern) {
   InternTable intern_table;
-  String* foo_1 = intern_table.Intern(3, "foo");
-  String* foo_2 = intern_table.Intern(3, "foo");
-  String* foo_3 = String::AllocFromModifiedUtf8("foo");
-  String* bar = intern_table.Intern(3, "bar");
+  const String* foo_1 = intern_table.InternStrong(3, "foo");
+  const String* foo_2 = intern_table.InternStrong(3, "foo");
+  const String* foo_3 = String::AllocFromModifiedUtf8("foo");
+  const String* bar = intern_table.InternStrong(3, "bar");
   EXPECT_TRUE(foo_1->Equals("foo"));
   EXPECT_TRUE(foo_2->Equals("foo"));
   EXPECT_TRUE(foo_3->Equals("foo"));
@@ -26,4 +26,94 @@
   EXPECT_NE(foo_3, bar);
 }
 
+TEST_F(InternTableTest, Size) {
+  InternTable t;
+  EXPECT_EQ(0U, t.Size());
+  t.InternStrong(3, "foo");
+  t.InternWeak(String::AllocFromModifiedUtf8("foo"));
+  EXPECT_EQ(1U, t.Size());
+  t.InternStrong(3, "bar");
+  EXPECT_EQ(2U, t.Size());
+}
+
+std::vector<const String*> gExpectedWeakStrings;
+bool TestPredicate(const String* s) {
+  bool erased = false;
+  typedef std::vector<const String*>::iterator It; // TODO: C++0x auto
+  for (It it = gExpectedWeakStrings.begin(), end = gExpectedWeakStrings.end(); it != end; ++it) {
+    if (*it == s) {
+      gExpectedWeakStrings.erase(it);
+      erased = true;
+      break;
+    }
+  }
+  EXPECT_TRUE(erased);
+  return true;
+}
+
+TEST_F(InternTableTest, RemoveWeakIf) {
+  InternTable t;
+  t.InternStrong(3, "foo");
+  t.InternStrong(3, "bar");
+  const String* s0 = t.InternWeak(String::AllocFromModifiedUtf8("hello"));
+  const String* s1 = t.InternWeak(String::AllocFromModifiedUtf8("world"));
+
+  EXPECT_EQ(4U, t.Size());
+
+  // We should traverse only the weaks...
+  gExpectedWeakStrings.clear();
+  gExpectedWeakStrings.push_back(s0);
+  gExpectedWeakStrings.push_back(s1);
+  t.RemoveWeakIf(TestPredicate);
+  EXPECT_EQ(0U, gExpectedWeakStrings.size());
+
+  EXPECT_EQ(2U, t.Size());
+
+  // Just check that we didn't corrupt the unordered_multimap.
+  t.InternWeak(String::AllocFromModifiedUtf8("still here"));
+  EXPECT_EQ(3U, t.Size());
+}
+
+TEST_F(InternTableTest, ContainsWeak) {
+  {
+    // Strongs are never weak.
+    InternTable t;
+    const String* foo_1 = t.InternStrong(3, "foo");
+    EXPECT_FALSE(t.ContainsWeak(foo_1));
+    const String* foo_2 = t.InternStrong(3, "foo");
+    EXPECT_FALSE(t.ContainsWeak(foo_2));
+    EXPECT_EQ(foo_1, foo_2);
+  }
+
+  {
+    // Weaks are always weak.
+    InternTable t;
+    const String* foo_1 = t.InternWeak(String::AllocFromModifiedUtf8("foo"));
+    EXPECT_TRUE(t.ContainsWeak(foo_1));
+    const String* foo_2 = t.InternWeak(String::AllocFromModifiedUtf8("foo"));
+    EXPECT_TRUE(t.ContainsWeak(foo_2));
+    EXPECT_EQ(foo_1, foo_2);
+  }
+
+  {
+    // A weak can be promoted to a strong.
+    InternTable t;
+    const String* foo_1 = t.InternWeak(String::AllocFromModifiedUtf8("foo"));
+    EXPECT_TRUE(t.ContainsWeak(foo_1));
+    const String* foo_2 = t.InternStrong(3, "foo");
+    EXPECT_FALSE(t.ContainsWeak(foo_2));
+    EXPECT_EQ(foo_1, foo_2);
+  }
+
+  {
+    // Interning a weak after a strong gets you the strong.
+    InternTable t;
+    const String* foo_1 = t.InternStrong(3, "foo");
+    EXPECT_FALSE(t.ContainsWeak(foo_1));
+    const String* foo_2 = t.InternWeak(String::AllocFromModifiedUtf8("foo"));
+    EXPECT_FALSE(t.ContainsWeak(foo_2));
+    EXPECT_EQ(foo_1, foo_2);
+  }
+}
+
 }  // namespace art