// Copyright (c) 2007, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef COMMON_SIMPLE_STRING_DICTIONARY_H_ #define COMMON_SIMPLE_STRING_DICTIONARY_H_ #include #include #include "common/basictypes.h" namespace google_breakpad { // Opaque type for the serialized representation of a NonAllocatingMap. One is // created in NonAllocatingMap::Serialize and can be deserialized using one of // the constructors. struct SerializedNonAllocatingMap; // NonAllocatingMap is an implementation of a map/dictionary collection that // uses a fixed amount of storage, so that it does not perform any dynamic // allocations for its operations. // // The actual map storage (the Entry) is guaranteed to be POD, so that it can // be transmitted over various IPC mechanisms. // // The template parameters control the amount of storage used for the key, // value, and map. The KeySize and ValueSize are measured in bytes, not glyphs, // and includes space for a \0 byte. This gives space for KeySize-1 and // ValueSize-1 characters in an entry. NumEntries is the total number of // entries that will fit in the map. template class NonAllocatingMap { public: // Constant and publicly accessible versions of the template parameters. static const size_t key_size = KeySize; static const size_t value_size = ValueSize; static const size_t num_entries = NumEntries; // An Entry object is a single entry in the map. If the key is a 0-length // NUL-terminated string, the entry is empty. struct Entry { char key[KeySize]; char value[ValueSize]; bool is_active() const { return key[0] != '\0'; } }; // An Iterator can be used to iterate over all the active entries in a // NonAllocatingMap. class Iterator { public: explicit Iterator(const NonAllocatingMap& map) : map_(map), current_(0) { } // Returns the next entry in the map, or NULL if at the end of the // collection. const Entry* Next() { while (current_ < map_.num_entries) { const Entry* entry = &map_.entries_[current_++]; if (entry->is_active()) { return entry; } } return NULL; } private: const NonAllocatingMap& map_; size_t current_; DISALLOW_COPY_AND_ASSIGN(Iterator); }; NonAllocatingMap() : entries_() { } NonAllocatingMap(const NonAllocatingMap& other) { *this = other; } NonAllocatingMap& operator=(const NonAllocatingMap& other) { assert(other.key_size == key_size); assert(other.value_size == value_size); assert(other.num_entries == num_entries); if (other.key_size == key_size && other.value_size == value_size && other.num_entries == num_entries) { memcpy(entries_, other.entries_, sizeof(entries_)); } return *this; } // Constructs a map from its serialized form. |map| should be the out // parameter from Serialize() and |size| should be its return value. NonAllocatingMap(const SerializedNonAllocatingMap* map, size_t size) { assert(size == sizeof(entries_)); if (size == sizeof(entries_)) { memcpy(entries_, map, size); } } // Returns the number of active key/value pairs. The upper limit for this // is NumEntries. size_t GetCount() const { size_t count = 0; for (size_t i = 0; i < num_entries; ++i) { if (entries_[i].is_active()) { ++count; } } return count; } // Given |key|, returns its corresponding |value|. |key| must not be NULL. If // the key is not found, NULL is returned. const char* GetValueForKey(const char* key) const { assert(key); if (!key) return NULL; size_t index = GetEntryIndexForKey(key); if (index == num_entries) return NULL; 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. 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 num_entries; } assert(key); if (!key) return num_entries; // Key must not be an empty string. assert(key[0] != '\0'); if (key[0] == '\0') return num_entries; size_t entry_index = GetEntryIndexForKey(key); // If it does not yet exist, attempt to insert it. if (entry_index == num_entries) { for (size_t i = 0; i < num_entries; ++i) { if (!entries_[i].is_active()) { entry_index = i; Entry* entry = &entries_[i]; strncpy(entry->key, key, key_size); entry->key[key_size - 1] = '\0'; break; } } } // If the map is out of space, entry will be NULL. if (entry_index == num_entries) return num_entries; #ifndef NDEBUG // Sanity check that the key only appears once. int count = 0; for (size_t i = 0; i < num_entries; ++i) { if (strncmp(entries_[i].key, key, key_size) == 0) ++count; } 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. This invalidates the index // returned by SetKeyValue(). bool RemoveKey(const char* key) { assert(key); if (!key) return false; return RemoveAtIndex(GetEntryIndexForKey(key)); } // 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. // Both of these should be passed to the deserializing constructor. Note that // the serialized |map| is scoped to the lifetime of the non-serialized // instance of this class. The |map| can be copied across IPC boundaries. size_t Serialize(const SerializedNonAllocatingMap** map) const { *map = reinterpret_cast(entries_); return sizeof(entries_); } private: 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 i; } } return num_entries; } Entry entries_[NumEntries]; }; // For historical reasons this specialized version is available with the same // size factors as a previous implementation. typedef NonAllocatingMap<256, 256, 64> SimpleStringDictionary; } // namespace google_breakpad #endif // COMMON_SIMPLE_STRING_DICTIONARY_H_