Add index-based set functionality to NonAllocatingMap.
This enables repeatedly setting a value based on index, which avoids a linear scan of the entry table after the first SetKeyValue(). Bug: chromium:598854 Change-Id: I9964670a09dcd8ff76180d031a373f20990bf4d8 Reviewed-on: https://chromium-review.googlesource.com/757579 Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
3bbf3fb0db
commit
8a0edac9ab
2 changed files with 79 additions and 31 deletions
|
@ -147,39 +147,42 @@ class NonAllocatingMap {
|
|||
if (!key)
|
||||
return NULL;
|
||||
|
||||
const Entry* entry = GetConstEntryForKey(key);
|
||||
if (!entry)
|
||||
size_t index = GetEntryIndexForKey(key);
|
||||
if (index == num_entries)
|
||||
return NULL;
|
||||
|
||||
return entry->value;
|
||||
return entries_[index].value;
|
||||
}
|
||||
|
||||
// Stores |value| into |key|, replacing the existing value if |key| is
|
||||
// already present. |key| must not be NULL. If |value| is NULL, the key is
|
||||
// removed from the map. If there is no more space in the map, then the
|
||||
// operation silently fails.
|
||||
void SetKeyValue(const char* key, const char* value) {
|
||||
// operation silently fails. Returns an index into the map that can be used
|
||||
// to quickly access the entry, or |num_entries| on failure or when clearing
|
||||
// a key with a null value.
|
||||
size_t SetKeyValue(const char* key, const char* value) {
|
||||
if (!value) {
|
||||
RemoveKey(key);
|
||||
return;
|
||||
return num_entries;
|
||||
}
|
||||
|
||||
assert(key);
|
||||
if (!key)
|
||||
return;
|
||||
return num_entries;
|
||||
|
||||
// Key must not be an empty string.
|
||||
assert(key[0] != '\0');
|
||||
if (key[0] == '\0')
|
||||
return;
|
||||
return num_entries;
|
||||
|
||||
Entry* entry = GetEntryForKey(key);
|
||||
size_t entry_index = GetEntryIndexForKey(key);
|
||||
|
||||
// If it does not yet exist, attempt to insert it.
|
||||
if (!entry) {
|
||||
if (entry_index == num_entries) {
|
||||
for (size_t i = 0; i < num_entries; ++i) {
|
||||
if (!entries_[i].is_active()) {
|
||||
entry = &entries_[i];
|
||||
entry_index = i;
|
||||
Entry* entry = &entries_[i];
|
||||
|
||||
strncpy(entry->key, key, key_size);
|
||||
entry->key[key_size - 1] = '\0';
|
||||
|
@ -190,8 +193,8 @@ class NonAllocatingMap {
|
|||
}
|
||||
|
||||
// If the map is out of space, entry will be NULL.
|
||||
if (!entry)
|
||||
return;
|
||||
if (entry_index == num_entries)
|
||||
return num_entries;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Sanity check that the key only appears once.
|
||||
|
@ -203,28 +206,46 @@ class NonAllocatingMap {
|
|||
assert(count == 1);
|
||||
#endif
|
||||
|
||||
strncpy(entries_[entry_index].value, value, value_size);
|
||||
entries_[entry_index].value[value_size - 1] = '\0';
|
||||
|
||||
return entry_index;
|
||||
}
|
||||
|
||||
// Sets a value for a key that has already been set with SetKeyValue(), using
|
||||
// the index returned from that function.
|
||||
void SetValueAtIndex(size_t index, const char* value) {
|
||||
assert(index < num_entries);
|
||||
if (index >= num_entries)
|
||||
return;
|
||||
|
||||
Entry* entry = &entries_[index];
|
||||
assert(entry->key[0] != '\0');
|
||||
|
||||
strncpy(entry->value, value, value_size);
|
||||
entry->value[value_size - 1] = '\0';
|
||||
}
|
||||
|
||||
// Given |key|, removes any associated value. |key| must not be NULL. If
|
||||
// the key is not found, this is a noop.
|
||||
// the key is not found, this is a noop. This invalidates the index
|
||||
// returned by SetKeyValue().
|
||||
bool RemoveKey(const char* key) {
|
||||
assert(key);
|
||||
if (!key)
|
||||
return false;
|
||||
|
||||
Entry* entry = GetEntryForKey(key);
|
||||
if (entry) {
|
||||
entry->key[0] = '\0';
|
||||
entry->value[0] = '\0';
|
||||
return true;
|
||||
}
|
||||
return RemoveAtIndex(GetEntryIndexForKey(key));
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
assert(GetEntryForKey(key) == NULL);
|
||||
#endif
|
||||
return false;
|
||||
// Removes a value and key using an index that was returned from
|
||||
// SetKeyValue(). After a call to this function, the index is invalidated.
|
||||
bool RemoveAtIndex(size_t index) {
|
||||
if (index >= num_entries)
|
||||
return false;
|
||||
|
||||
entries_[index].key[0] = '\0';
|
||||
entries_[index].value[0] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
// Places a serialized version of the map into |map| and returns the size.
|
||||
|
@ -237,17 +258,13 @@ class NonAllocatingMap {
|
|||
}
|
||||
|
||||
private:
|
||||
const Entry* GetConstEntryForKey(const char* key) const {
|
||||
size_t GetEntryIndexForKey(const char* key) const {
|
||||
for (size_t i = 0; i < num_entries; ++i) {
|
||||
if (strncmp(key, entries_[i].key, key_size) == 0) {
|
||||
return &entries_[i];
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Entry* GetEntryForKey(const char* key) {
|
||||
return const_cast<Entry*>(GetConstEntryForKey(key));
|
||||
return num_entries;
|
||||
}
|
||||
|
||||
Entry entries_[NumEntries];
|
||||
|
|
|
@ -288,6 +288,37 @@ TEST(NonAllocatingMapTest, OutOfSpace) {
|
|||
EXPECT_FALSE(map.GetValueForKey("c"));
|
||||
}
|
||||
|
||||
TEST(NonAllocatingMapTest, ByIndex) {
|
||||
NonAllocatingMap<10, 10, 3> map;
|
||||
|
||||
size_t index1 = map.SetKeyValue("test", "one");
|
||||
EXPECT_TRUE(index1 >= 0 && index1 <= map.num_entries);
|
||||
|
||||
size_t index2 = map.SetKeyValue("moo", "foo");
|
||||
EXPECT_TRUE(index2 >= 0 && index2 <= map.num_entries);
|
||||
EXPECT_NE(index1, index2);
|
||||
|
||||
size_t index3 = map.SetKeyValue("blob", "kebab");
|
||||
EXPECT_TRUE(index3 >= 0 && index3 <= map.num_entries);
|
||||
EXPECT_NE(index2, index3);
|
||||
|
||||
size_t index4 = map.SetKeyValue("nogo", "full");
|
||||
EXPECT_TRUE(index4 == map.num_entries);
|
||||
|
||||
EXPECT_STREQ("one", map.GetValueForKey("test"));
|
||||
EXPECT_STREQ("foo", map.GetValueForKey("moo"));
|
||||
EXPECT_STREQ("kebab", map.GetValueForKey("blob"));
|
||||
|
||||
map.SetValueAtIndex(index2, "booo");
|
||||
EXPECT_STREQ("booo", map.GetValueForKey("moo"));
|
||||
|
||||
EXPECT_TRUE(map.RemoveAtIndex(index1));
|
||||
EXPECT_FALSE(map.GetValueForKey("test"));
|
||||
|
||||
EXPECT_FALSE(map.RemoveAtIndex(map.num_entries));
|
||||
EXPECT_FALSE(map.RemoveAtIndex(9999));
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
TEST(NonAllocatingMapTest, NullKey) {
|
||||
|
|
Loading…
Reference in a new issue