Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1219)

Unified Diff: runtime/vm/hash_table_test.cc

Issue 304253002: Hash tables templates, wrapping Array. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/hash_table.h ('k') | runtime/vm/object.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/hash_table_test.cc
===================================================================
--- runtime/vm/hash_table_test.cc (revision 0)
+++ runtime/vm/hash_table_test.cc (revision 0)
@@ -0,0 +1,287 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#include <algorithm>
+#include <cstring>
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "platform/assert.h"
+#include "vm/unit_test.h"
+#include "vm/hash_table.h"
+
+namespace dart {
+
+// Various ways to look up strings. Uses length as the hash code to make it
+// easy to engineer collisions.
+class TestTraits {
+ public:
+ static bool IsMatch(const char* key, const Object& obj) {
+ return String::Cast(obj).Equals(key);
+ }
+ static uword Hash(const char* key) {
+ return static_cast<uword>(strlen(key));
+ }
+ static bool IsMatch(const Object& a, const Object& b) {
+ return a.IsString() && b.IsString() &&
+ String::Cast(a).Equals(String::Cast(b));
+ }
+ static uword Hash(const Object& obj) {
+ return String::Cast(obj).Length();
+ }
+};
+
+
+template<typename Table>
+void Validate(const Table& table) {
+ // Verify consistency of entry state tracking.
+ intptr_t num_entries = table.NumEntries();
+ intptr_t num_unused = table.NumUnused();
+ intptr_t num_occupied = table.NumOccupied();
+ intptr_t num_deleted = table.NumDeleted();
+ for (intptr_t i = 0; i < num_entries; ++i) {
+ EXPECT_EQ(1, table.IsUnused(i) + table.IsOccupied(i) + table.IsDeleted(i));
+ num_unused -= table.IsUnused(i);
+ num_occupied -= table.IsOccupied(i);
+ num_deleted -= table.IsDeleted(i);
+ }
+ EXPECT_EQ(0, num_unused);
+ EXPECT_EQ(0, num_occupied);
+ EXPECT_EQ(0, num_deleted);
+}
+
+
+TEST_CASE(HashTable) {
+ typedef HashTable<TestTraits, 2, 1> Table;
+ Table table(Array::Handle(HashTables::New<Table>(5)));
+ // Ensure that we did get at least 5 entries.
+ EXPECT_LE(5, table.NumEntries());
+ EXPECT_EQ(0, table.NumOccupied());
+ Validate(table);
+ EXPECT_EQ(-1, table.FindKey("a"));
+
+ // Insertion and lookup.
+ intptr_t a_entry = -1;
+ EXPECT(!table.FindKeyOrDeletedOrUnused("a", &a_entry));
+ EXPECT_NE(-1, a_entry);
+ String& a = String::Handle(String::New("a"));
+ table.InsertKey(a_entry, a);
+ EXPECT_EQ(1, table.NumOccupied());
+ Validate(table);
+ EXPECT_EQ(a_entry, table.FindKey("a"));
+ EXPECT_EQ(-1, table.FindKey("b"));
+ intptr_t a_entry_again = -1;
+ EXPECT(table.FindKeyOrDeletedOrUnused("a", &a_entry_again));
+ EXPECT_EQ(a_entry, a_entry_again);
+ intptr_t b_entry = -1;
+ EXPECT(!table.FindKeyOrDeletedOrUnused("b", &b_entry));
+ String& b = String::Handle(String::New("b"));
+ table.InsertKey(b_entry, b);
+ EXPECT_EQ(2, table.NumOccupied());
+ Validate(table);
+
+ // Deletion.
+ table.DeleteEntry(a_entry);
+ EXPECT_EQ(1, table.NumOccupied());
+ Validate(table);
+ EXPECT_EQ(-1, table.FindKey("a"));
+ EXPECT_EQ(b_entry, table.FindKey("b"));
+ intptr_t c_entry = -1;
+ EXPECT(!table.FindKeyOrDeletedOrUnused("c", &c_entry));
+ String& c = String::Handle(String::New("c"));
+ table.InsertKey(c_entry, c);
+ EXPECT_EQ(2, table.NumOccupied());
+ Validate(table);
+ EXPECT_EQ(c_entry, table.FindKey("c"));
+
+ // Ensure we can actually reach 5 occupied entries (without expansion).
+ {
+ intptr_t entry = -1;
+ EXPECT(!table.FindKeyOrDeletedOrUnused("d", &entry));
+ String& k = String::Handle(String::New("d"));
+ table.InsertKey(entry, k);
+ EXPECT(!table.FindKeyOrDeletedOrUnused("e", &entry));
+ k = String::New("e");
+ table.InsertKey(entry, k);
+ EXPECT(!table.FindKeyOrDeletedOrUnused("f", &entry));
+ k = String::New("f");
+ table.InsertKey(entry, k);
+ EXPECT_EQ(5, table.NumOccupied());
+ }
+ table.Release();
+}
+
+
+TEST_CASE(EnumIndexHashMap) {
+ typedef EnumIndexHashMap<TestTraits> Table;
+ Table table(Array::Handle(HashTables::New<Table>(5)));
+ table.UpdateOrInsert(String::Handle(String::New("a")),
+ String::Handle(String::New("A")));
+ EXPECT(table.ContainsKey("a"));
+ table.UpdateValue("a", String::Handle(String::New("AAA")));
+ String& a_value = String::Handle();
+ a_value ^= table.GetOrNull("a");
+ EXPECT(a_value.Equals("AAA"));
+ Object& null_value = Object::Handle(table.GetOrNull("0"));
+ EXPECT(null_value.IsNull());
+ table.Release();
+}
+
+
+std::string ToStdString(const String& str) {
+ EXPECT(str.IsOneByteString());
+ std::string result;
+ for (intptr_t i = 0; i < str.Length(); ++i) {
+ result += static_cast<char>(str.CharAt(i));
+ }
+ return result;
+}
+
+
+// Checks that 'expected' and 'actual' are equal sets. If 'ordered' is true,
+// it also verifies that their iteration orders match, i.e., that actual's
+// insertion order coincides with lexicographic order.
+template<typename Set>
+void VerifyStringSetsEqual(const std::set<std::string>& expected,
+ const Set& actual,
+ bool ordered) {
+ // Get actual keys in iteration order.
+ Array& keys = Array::Handle(HashTables::ToArray(actual, true));
+ // Cardinality must match.
+ EXPECT_EQ(static_cast<intptr_t>(expected.size()), keys.Length());
+ std::vector<std::string> expected_vec(expected.begin(), expected.end());
+ // Check containment.
+ for (uintptr_t i = 0; i < expected_vec.size(); ++i) {
+ EXPECT(actual.ContainsKey(expected_vec[i].c_str()));
+ }
+ // Equality, including order, if requested.
+ std::vector<std::string> actual_vec;
+ String& key = String::Handle();
+ for (int i = 0; i < keys.Length(); ++i) {
+ key ^= keys.At(i);
+ actual_vec.push_back(ToStdString(key));
+ }
+ if (!ordered) {
+ std::sort(actual_vec.begin(), actual_vec.end());
+ }
+ EXPECT(std::equal(actual_vec.begin(), actual_vec.end(),
+ expected_vec.begin()));
+}
+
+
+// Checks that 'expected' and 'actual' are equal maps. If 'ordered' is true,
+// it also verifies that their iteration orders match, i.e., that actual's
+// insertion order coincides with lexicographic order.
+template<typename Map>
+void VerifyStringMapsEqual(const std::map<std::string, int>& expected,
+ const Map& actual,
+ bool ordered) {
+ intptr_t expected_size = expected.size();
+ // Get actual concatenated (key, value) pairs in iteration order.
+ Array& entries = Array::Handle(HashTables::ToArray(actual, true));
+ // Cardinality must match.
+ EXPECT_EQ(expected_size * 2, entries.Length());
+ std::vector<std::pair<std::string, int> > expected_vec(expected.begin(),
+ expected.end());
+ // Check containment.
+ Smi& value = Smi::Handle();
+ for (uintptr_t i = 0; i < expected_vec.size(); ++i) {
+ std::string key = expected_vec[i].first;
+ EXPECT(actual.ContainsKey(key.c_str()));
+ value ^= actual.GetOrNull(key.c_str());
+ EXPECT_EQ(expected_vec[i].second, value.Value());
+ }
+ if (!ordered) {
+ return;
+ }
+ // Equality including order.
+ std::vector<std::string> actual_vec;
+ String& key = String::Handle();
+ for (int i = 0; i < expected_size; ++i) {
+ key ^= entries.At(2 * i);
+ value ^= entries.At(2 * i + 1);
+ EXPECT(expected_vec[i].first == ToStdString(key));
+ EXPECT_EQ(expected_vec[i].second, value.Value());
+ }
+}
+
+
+template<typename Set>
+void TestSet(intptr_t initial_capacity, bool ordered) {
+ std::set<std::string> expected;
+ Set actual(Array::Handle(HashTables::New<Set>(initial_capacity)));
+ // Insert the following strings twice:
+ // aaa...aaa (length 26)
+ // bbb..bbb
+ // ...
+ // yy
+ // z
+ for (int i = 0; i < 2; ++i) {
+ for (char ch = 'a'; ch <= 'z'; ++ch) {
+ std::string key('z' - ch + 1, ch);
+ expected.insert(key);
+ bool present = actual.Insert(String::Handle(String::New(key.c_str())));
+ EXPECT_EQ((i != 0), present);
+ Validate(actual);
+ VerifyStringSetsEqual(expected, actual, ordered);
+ }
+ }
+ // TODO(koda): Delete all entries.
+ actual.Release();
+}
+
+
+template<typename Map>
+void TestMap(intptr_t initial_capacity, bool ordered) {
+ std::map<std::string, int> expected;
+ Map actual(Array::Handle(HashTables::New<Map>(initial_capacity)));
+ // Insert the following (strings, int) mapping:
+ // aaa...aaa -> 26
+ // bbb..bbb -> 25
+ // ...
+ // yy -> 2
+ // z -> 1
+ for (int i = 0; i < 2; ++i) {
+ for (char ch = 'a'; ch <= 'z'; ++ch) {
+ int length = 'z' - ch + 1;
+ std::string key(length, ch);
+ // Map everything to zero initially, then update to their final values.
+ int value = length * i;
+ expected[key] = value;
+ bool present =
+ actual.UpdateOrInsert(String::Handle(String::New(key.c_str())),
+ Smi::Handle(Smi::New(value)));
+ EXPECT_EQ((i != 0), present);
+ Validate(actual);
+ VerifyStringMapsEqual(expected, actual, ordered);
+ }
+ }
+ // TODO(koda): Delete all entries.
+ actual.Release();
+}
+
+
+TEST_CASE(Sets) {
+ for (intptr_t initial_capacity = 0;
+ initial_capacity < 32;
+ ++initial_capacity) {
+ TestSet<UnorderedHashSet<TestTraits> >(initial_capacity, false);
+ TestSet<EnumIndexHashSet<TestTraits> >(initial_capacity, true);
+ }
+}
+
+
+TEST_CASE(Maps) {
+ for (intptr_t initial_capacity = 0;
+ initial_capacity < 32;
+ ++initial_capacity) {
+ TestMap<UnorderedHashMap<TestTraits> >(initial_capacity, false);
+ TestMap<EnumIndexHashMap<TestTraits> >(initial_capacity, true);
+ }
+}
+
+} // namespace dart
« no previous file with comments | « runtime/vm/hash_table.h ('k') | runtime/vm/object.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698