diff --git a/doomsday/libdeng/include/de/stringpool.h b/doomsday/libdeng/include/de/stringpool.h index 46267d0efd..51109c3298 100644 --- a/doomsday/libdeng/include/de/stringpool.h +++ b/doomsday/libdeng/include/de/stringpool.h @@ -1,35 +1,14 @@ /** * @file de/stringpool.h - * String pool (case insensitive). @ingroup base - * - * Container data structure for a set of unique case-insensitive strings. - * Comparable to a @c std::set with unique IDs assigned to each contained string. - * - * The term "intern" is used here to refer to the act of inserting a string to - * the pool. As a result of interning a string, a new internal copy of the - * string may be created in the pool. - * - * Each string that actually gets added to the pool is assigned a unique - * identifier. If one tries to intern a string that already exists in the pool - * (case insensitively speaking), no new internal copy is created and no new - * identifier is assigned. Instead, the existing id of the previously interned - * string is returned. The string identifiers are not unique over the lifetime - * of the container: if a string is removed from the pool, its id is free to be - * reassigned to the next new string. Zero is not a valid id. * - * Each string can also have an associated, custom user-defined uint32 value. - * - * The implementation has, at worst, O(log n) complexity for addition, removal, - * string lookup, and user value set/get. + * String pool (case insensitive). @ingroup base * * @par Build Options * Define the macro @c DENG_STRINGPOOL_ZONE_ALLOCS to make StringPool allocate * memory from the memory zone instead of with system malloc(). * - * @todo Add case-sensitive mode. - * - * @authors Copyright © 2010-2012 Daniel Swanson - * @authors Copyright © 2012 Jaakko Keränen + * @author Copyright © 2010-2012 Daniel Swanson + * @author Copyright © 2012 Jaakko Keränen * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -59,6 +38,236 @@ extern "C" { #endif +/// String identifier. Each string is assigned its own Id. +typedef uint StringPoolId; + +#ifdef __cplusplus +} // extern "C" +#endif + +#ifdef __cplusplus +#ifndef DENG2_C_API_ONLY + +namespace de +{ + /** + * Container data structure for a set of unique case-insensitive strings. + * Comparable to a @c std::set with unique IDs assigned to each contained string. + * + * The term "intern" is used here to refer to the act of inserting a string to + * the pool. As a result of interning a string, a new internal copy of the + * string may be created in the pool. + * + * Each string that actually gets added to the pool is assigned a unique + * identifier. If one tries to intern a string that already exists in the pool + * (case insensitively speaking), no new internal copy is created and no new + * identifier is assigned. Instead, the existing id of the previously interned + * string is returned. The string identifiers are not unique over the lifetime + * of the container: if a string is removed from the pool, its id is free to be + * reassigned to the next new string. Zero is not a valid id. + * + * Each string can also have an associated, custom user-defined uint32 value. + * + * The implementation has, at worst, O(log n) complexity for addition, removal, + * string lookup, and user value set/get. + * + * @todo Add case-sensitive mode. + */ + class DENG_PUBLIC StringPool + { + public: + /** + * Constructs an empty StringPool. The pool must be destroyed with + * StringPool_Delete() when no longer needed. + */ + StringPool(); + + /** + * Constructs an empty StringPool and interns a number of strings. The pool + * must be destroyed with StringPool_Delete() when no longer needed. + * + * @param strings Array of strings to be interned (must contain at least @a count strings). + * @param count Number of strings to be interned. + */ + StringPool(ddstring_t const* strings, uint count); + + /** + * Destroys the stringpool. + * @param pool StringPool instance. + */ + ~StringPool(); + + /** + * Clear the string pool. All strings in the pool will be destroyed. + */ + void clear(); + + /** + * Is the pool empty? + * @param pool StringPool instance. + * @return @c true if there are no strings present in the pool. + */ + bool empty() const; + + /** + * Determines the number of strings in the pool. + * @param pool StringPool instance. + * @return Number of strings in the pool. + */ + uint size() const; + + /** + * Interns string @a str. If this string is not already in the pool, a new + * internal copy is created; otherwise, the existing internal copy is returned. + * New internal copies will be assigned new identifiers. + * + * @param str String to be added (must not be of zero-length). + * A copy of this is made if the string is interned. + * + * @return Unique Id associated with the internal copy of @a str. + */ + StringPoolId intern(ddstring_t const* str); + + /** + * Interns string @a str. If this string is not already in the pool, a new + * internal copy is created; otherwise, the existing internal copy is returned. + * New internal copies will be assigned new identifiers. + * + * @param str String to be added (must not be of zero-length). + * A copy of this is made if the string is interned. + * + * @return The interned copy of the string owned by the pool. + */ + ddstring_t const* internAndRetrieve(ddstring_t const* str); + + /** + * Sets the user-specified custom value associated with the string @a id. + * The default user value is zero. + * + * @param id Id of a string. + * @param value User value. + */ + void setUserValue(StringPoolId id, uint value); + + /** + * Retrieves the user-specified custom value associated with the string @a id. + * The default user value is zero. + * + * @param id Id of a string. + * + * @return User value. + */ + uint userValue(StringPoolId id) const; + + /** + * Sets the user-specified custom pointer associated with the string @a id. + * By default the pointer is NULL. + * + * @note User pointer values are @em not serialized. + * + * @param id Id of a string. + * @param ptr User pointer. + */ + void setUserPointer(StringPoolId id, void* ptr); + + /** + * Retrieves the user-specified custom pointer associated with the string @a id. + * + * @param pool StringPool instance. + * @param id Id of a string. + * + * @return User pointer. + */ + void* userPointer(StringPoolId id) const; + + /** + * Is @a str considered to be in the pool? + * + * @param str String to look for. + * + * @return Id of the matching string; else @c 0. + */ + StringPoolId isInterned(ddstring_t const* str) const; + + /** + * Retrieve an immutable copy of the interned string associated with the + * string @a id. + * + * @param id Id of the string to retrieve. + * + * @return Interned string associated with @a internId. Owned by the pool. + */ + ddstring_t const* string(StringPoolId id) const; + + /** + * Removes a string from the pool. + * + * @param str String to be removed. + * + * @return @c true, if string @a str was found and removed. + */ + bool remove(ddstring_t const* str); + + /** + * Removes a string from the pool. + * + * @param id Id of the string to remove. + * + * @return @c true if the string was found and removed. + */ + bool removeById(StringPoolId id); + + /** + * Iterate over all strings in the pool making a callback for each. Iteration + * ends when all strings have been processed or a callback returns non-zero. + * + * @param callback Callback to make for each iteration. + * @param data User data to be passed to the callback. + * + * @return @c 0 iff iteration completed wholly. Otherwise the non-zero value returned + * by @a callback. + */ + int iterate(int (*callback)(StringPoolId, void*), void* data) const; + + /** + * Serializes the pool using @a writer. + * + * @param writer Writer instance. + */ + void write(Writer* writer) const; + + /** + * Deserializes the pool from @a reader. + * + * @param reader Reader instance. + */ + void read(Reader* reader); + +#if _DEBUG + /** + * Print contents of the pool. For debug. + * @param pool StringPool instance. + */ + void print() const; +#endif + + private: + struct Instance; + Instance* d; + }; + +} // namespace de +#endif // DENG2_C_API_ONLY +#endif //__cplusplus + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * C wrapper API: + */ + struct stringpool_s; // The stringpool instance (opaque). /** @@ -66,9 +275,6 @@ struct stringpool_s; // The stringpool instance (opaque). */ typedef struct stringpool_s StringPool; -/// String identifier. Each string is assigned its own Id. -typedef uint StringPoolId; - /** * Constructs an empty StringPool. The pool must be destroyed with * StringPool_Delete() when no longer needed. @@ -82,7 +288,7 @@ DENG_PUBLIC StringPool* StringPool_New(void); * @param strings Array of strings to be interned (must contain at least @a count strings). * @param count Number of strings to be interned. */ -DENG_PUBLIC StringPool* StringPool_NewWithStrings(const ddstring_t* strings, uint count); +DENG_PUBLIC StringPool* StringPool_NewWithStrings(ddstring_t const* strings, uint count); /** * Destroys the stringpool. @@ -101,14 +307,14 @@ DENG_PUBLIC void StringPool_Clear(StringPool* pool); * @param pool StringPool instance. * @return @c true if there are no strings present in the pool. */ -DENG_PUBLIC boolean StringPool_Empty(const StringPool* pool); +DENG_PUBLIC boolean StringPool_Empty(StringPool const* pool); /** * Determines the number of strings in the pool. * @param pool StringPool instance. * @return Number of strings in the pool. */ -DENG_PUBLIC uint StringPool_Size(const StringPool* pool); +DENG_PUBLIC uint StringPool_Size(StringPool const* pool); /** * Interns string @a str. If this string is not already in the pool, a new @@ -121,7 +327,7 @@ DENG_PUBLIC uint StringPool_Size(const StringPool* pool); * * @return Unique Id associated with the internal copy of @a str. */ -DENG_PUBLIC StringPoolId StringPool_Intern(StringPool* pool, const ddstring_t* str); +DENG_PUBLIC StringPoolId StringPool_Intern(StringPool* pool, ddstring_t const* str); /** * Interns string @a str. If this string is not already in the pool, a new @@ -134,7 +340,7 @@ DENG_PUBLIC StringPoolId StringPool_Intern(StringPool* pool, const ddstring_t* s * * @return The interned copy of the string owned by the pool. */ -DENG_PUBLIC const ddstring_t* StringPool_InternAndRetrieve(StringPool* pool, const ddstring_t* str); +DENG_PUBLIC ddstring_t const* StringPool_InternAndRetrieve(StringPool* pool, ddstring_t const* str); /** * Sets the user-specified custom value associated with the string @a id. @@ -155,7 +361,7 @@ DENG_PUBLIC void StringPool_SetUserValue(StringPool* pool, StringPoolId id, uint * * @return User value. */ -DENG_PUBLIC uint StringPool_UserValue(StringPool* pool, StringPoolId id); +DENG_PUBLIC uint StringPool_UserValue(StringPool const* pool, StringPoolId id); /** * Sets the user-specified custom pointer associated with the string @a id. @@ -177,7 +383,7 @@ DENG_PUBLIC void StringPool_SetUserPointer(StringPool* pool, StringPoolId id, vo * * @return User pointer. */ -DENG_PUBLIC void* StringPool_UserPointer(StringPool* pool, StringPoolId id); +DENG_PUBLIC void* StringPool_UserPointer(StringPool const* pool, StringPoolId id); /** * Is @a str considered to be in the pool? @@ -187,7 +393,7 @@ DENG_PUBLIC void* StringPool_UserPointer(StringPool* pool, StringPoolId id); * * @return Id of the matching string; else @c 0. */ -DENG_PUBLIC StringPoolId StringPool_IsInterned(const StringPool* pool, const ddstring_t* str); +DENG_PUBLIC StringPoolId StringPool_IsInterned(StringPool const* pool, ddstring_t const* str); /** * Retrieve an immutable copy of the interned string associated with the @@ -198,7 +404,7 @@ DENG_PUBLIC StringPoolId StringPool_IsInterned(const StringPool* pool, const dds * * @return Interned string associated with @a internId. Owned by the pool. */ -DENG_PUBLIC const ddstring_t* StringPool_String(const StringPool* pool, StringPoolId id); +DENG_PUBLIC ddstring_t const* StringPool_String(StringPool const* pool, StringPoolId id); /** * Removes a string from the pool. @@ -208,7 +414,7 @@ DENG_PUBLIC const ddstring_t* StringPool_String(const StringPool* pool, StringPo * * @return @c true, if string @a str was found and removed. */ -DENG_PUBLIC boolean StringPool_Remove(StringPool* pool, const ddstring_t* str); +DENG_PUBLIC boolean StringPool_Remove(StringPool* pool, ddstring_t const* str); /** * Removes a string from the pool. @@ -231,7 +437,7 @@ DENG_PUBLIC boolean StringPool_RemoveById(StringPool* pool, StringPoolId id); * @return @c 0 iff iteration completed wholly. Otherwise the non-zero value returned * by @a callback. */ -DENG_PUBLIC int StringPool_Iterate(const StringPool* pool, int (*callback)(StringPoolId, void*), void* data); +DENG_PUBLIC int StringPool_Iterate(StringPool const* pool, int (*callback)(StringPoolId, void*), void* data); /** * Serializes the pool using @a writer. @@ -239,7 +445,7 @@ DENG_PUBLIC int StringPool_Iterate(const StringPool* pool, int (*callback)(Strin * @param ar StringPool instance. * @param writer Writer instance. */ -DENG_PUBLIC void StringPool_Write(const StringPool* ar, Writer* writer); +DENG_PUBLIC void StringPool_Write(StringPool const* ar, Writer* writer); /** * Deserializes the pool from @a reader. @@ -254,7 +460,7 @@ DENG_PUBLIC void StringPool_Read(StringPool* ar, Reader* reader); * Print contents of the pool. For debug. * @param pool StringPool instance. */ -DENG_PUBLIC void StringPool_Print(const StringPool* pool); +DENG_PUBLIC void StringPool_Print(StringPool const* pool); #endif #ifdef __cplusplus diff --git a/doomsday/libdeng/src/stringpool.cpp b/doomsday/libdeng/src/stringpool.cpp index 2cc9cf7bce..ca4d9bec62 100644 --- a/doomsday/libdeng/src/stringpool.cpp +++ b/doomsday/libdeng/src/stringpool.cpp @@ -50,7 +50,9 @@ #define EXPORT_ID(i) (uint(i) + 1) #define IMPORT_ID(i) (StringPoolId((i) - 1)) -static const de::Str nullString = "(nullptr)"; +namespace de { + +static de::Str const nullString = "(nullptr)"; typedef uint InternalId; @@ -61,14 +63,14 @@ typedef uint InternalId; class CaselessStr { public: - CaselessStr(const char* text = 0) : _id(0), _userValue(0), _userPointer(0) { + CaselessStr(char const* text = 0) : _id(0), _userValue(0), _userPointer(0) { setText(text); } - CaselessStr(const CaselessStr& other) + CaselessStr(CaselessStr const& other) : _id(other._id), _userValue(other._userValue), _userPointer(0) { setText(other._str.str); } - void setText(const char* text) { + void setText(char const* text) { Str_InitStatic(&_str, text); // no ownership taken; not copied } char* toCString() const { @@ -123,10 +125,10 @@ class CaselessStr */ class CaselessStrRef { public: - CaselessStrRef(const CaselessStr* s = 0) { + CaselessStrRef(CaselessStr const* s = 0) { _str = s; } - CaselessStrRef(const CaselessStrRef& other) { + CaselessStrRef(CaselessStrRef const& other) { _str = other._str; } CaselessStr* toStr() const { @@ -136,12 +138,12 @@ class CaselessStrRef { DENG_ASSERT(_str); return _str->id(); } - bool operator < (const CaselessStrRef& other) const { + bool operator < (CaselessStrRef const& other) const { DENG_ASSERT(_str); DENG_ASSERT(other._str); return strcasecmp(*_str, *other._str) < 0; } - bool operator == (const CaselessStrRef& other) const { + bool operator == (CaselessStrRef const& other) const { DENG_ASSERT(_str); DENG_ASSERT(other._str); return !strcasecmp(*_str, *other._str); @@ -154,7 +156,8 @@ typedef std::set Interns; typedef std::vector IdMap; typedef std::list AvailableIds; -struct stringpool_s { +struct StringPool::Instance +{ /// Interned strings (owns the CaselessStr instances). Interns interns; @@ -167,69 +170,15 @@ struct stringpool_s { /// List of currently unused ids in idMap. AvailableIds available; - stringpool_s() : count(0) + Instance() : count(0) {} - ~stringpool_s() + ~Instance() { // Free all allocated memory. clear(); } - void serialize(Writer* writer) const - { - // Number of strings altogether (includes unused ids). - Writer_WriteUInt32(writer, idMap.size()); - - // Write the interns. - Writer_WriteUInt32(writer, interns.size()); - for(Interns::const_iterator i = interns.begin(); i != interns.end(); ++i) - { - i->toStr()->serialize(writer); - } - } - - void deserialize(Reader* reader) - { - clear(); - - uint numStrings = Reader_ReadUInt32(reader); - idMap.resize(numStrings, 0); - - uint numInterns = Reader_ReadUInt32(reader); - while(numInterns--) - { - ddstring_t text; - Str_InitStd(&text); - - CaselessStr* str = new CaselessStr; - str->deserialize(reader, &text); - // Create a copy of the string whose ownership StringPool controls. - str->setText(STRINGPOOL_STRDUP(Str_Text(&text))); - interns.insert(str); - Str_Free(&text); - - // Update the id map. - idMap[str->id()] = str; - - count++; - } - - // Update the available ids. - for(uint i = 0; i < idMap.size(); ++i) - { - if(!idMap[i]) available.push_back(i); - } - - assertCount(); - } - - void inline assertCount() const - { - DENG_ASSERT(count == interns.size()); - DENG_ASSERT(count == idMap.size() - available.size()); - } - void clear() { for(uint i = 0; i < idMap.size(); ++i) @@ -245,15 +194,21 @@ struct stringpool_s { assertCount(); } - Interns::iterator findIntern(const char* text) + void inline assertCount() const + { + DENG_ASSERT(count == interns.size()); + DENG_ASSERT(count == idMap.size() - available.size()); + } + + Interns::iterator findIntern(char const* text) { - const CaselessStr key(text); + CaselessStr const key(text); return interns.find(CaselessStrRef(&key)); // O(log n) } - Interns::const_iterator findIntern(const char* text) const + Interns::const_iterator findIntern(char const* text) const { - const CaselessStr key(text); + CaselessStr const key(text); return interns.find(CaselessStrRef(&key)); // O(log n) } @@ -264,7 +219,7 @@ struct stringpool_s { * @param text Text string to add to the interned strings. * A copy is made of this. */ - InternalId copyAndAssignUniqueId(const char* text) + InternalId copyAndAssignUniqueId(char const* text) { CaselessStr* str = new CaselessStr(STRINGPOOL_STRDUP(text)); @@ -330,124 +285,111 @@ struct stringpool_s { } }; -StringPool* StringPool_New(void) +StringPool::StringPool() { - StringPool* pool = new StringPool; - return pool; + d = new Instance(); } -StringPool* StringPool_NewWithStrings(const ddstring_t* strings, uint count) +StringPool::StringPool(ddstring_t const* strings, uint count) { - StringPool* pool = StringPool_New(); + d = new Instance(); for(uint i = 0; strings && i < count; ++i) { - StringPool_Intern(pool, strings + i); + intern(strings + i); } - return pool; } -void StringPool_Delete(StringPool* pool) +StringPool::~StringPool() { - DENG_ASSERT(pool); - delete pool; + delete d; } -void StringPool_Clear(StringPool* pool) +void StringPool::clear() { - DENG_ASSERT(pool); - pool->clear(); + d->clear(); } -boolean StringPool_Empty(const StringPool* pool) +bool StringPool::empty() const { - DENG_ASSERT(pool); - pool->assertCount(); - return !pool->count; + d->assertCount(); + return !d->count; } -uint StringPool_Size(const StringPool* pool) +uint StringPool::size() const { - DENG_ASSERT(pool); - pool->assertCount(); - return uint(pool->count); + d->assertCount(); + return uint(d->count); } -StringPoolId StringPool_Intern(StringPool* pool, const ddstring_t* str) +StringPoolId StringPool::intern(ddstring_t const* str) { - DENG_ASSERT(pool); - Interns::iterator found = pool->findIntern(Str_Text(str)); // O(log n) - if(found != pool->interns.end()) + Interns::iterator found = d->findIntern(Str_Text(str)); // O(log n) + if(found != d->interns.end()) { // Already got this one. return EXPORT_ID(found->id()); } - return EXPORT_ID(pool->copyAndAssignUniqueId(Str_Text(str))); // O(log n) + return EXPORT_ID(d->copyAndAssignUniqueId(Str_Text(str))); // O(log n) } -const ddstring_t* StringPool_InternAndRetrieve(StringPool* pool, const ddstring_t* str) +ddstring_t const* StringPool::internAndRetrieve(ddstring_t const* str) { - DENG_ASSERT(pool); - InternalId id = IMPORT_ID(StringPool_Intern(pool, str)); - return *pool->idMap[id]; + InternalId id = IMPORT_ID(intern(str)); + return *d->idMap[id]; } -void StringPool_SetUserValue(StringPool* pool, StringPoolId id, uint value) +void StringPool::setUserValue(StringPoolId id, uint value) { if(id == 0) return; - const InternalId internalId = IMPORT_ID(id); + InternalId const internalId = IMPORT_ID(id); - DENG_ASSERT(pool); - DENG_ASSERT(internalId < pool->idMap.size()); - DENG_ASSERT(pool->idMap[internalId] != 0); + DENG_ASSERT(internalId < d->idMap.size()); + DENG_ASSERT(d->idMap[internalId] != 0); - pool->idMap[internalId]->setUserValue(value); // O(1) + d->idMap[internalId]->setUserValue(value); // O(1) } -uint StringPool_UserValue(StringPool* pool, StringPoolId id) +uint StringPool::userValue(StringPoolId id) const { if(id == 0) return 0; - const InternalId internalId = IMPORT_ID(id); + InternalId const internalId = IMPORT_ID(id); - DENG_ASSERT(pool); - DENG_ASSERT(internalId < pool->idMap.size()); - DENG_ASSERT(pool->idMap[internalId] != 0); + DENG_ASSERT(internalId < d->idMap.size()); + DENG_ASSERT(d->idMap[internalId] != 0); - return pool->idMap[internalId]->userValue(); // O(1) + return d->idMap[internalId]->userValue(); // O(1) } -void StringPool_SetUserPointer(StringPool* pool, StringPoolId id, void* ptr) +void StringPool::setUserPointer(StringPoolId id, void* ptr) { if(id == 0) return; - const InternalId internalId = IMPORT_ID(id); + InternalId const internalId = IMPORT_ID(id); - DENG_ASSERT(pool); - DENG_ASSERT(internalId < pool->idMap.size()); - DENG_ASSERT(pool->idMap[internalId] != 0); + DENG_ASSERT(internalId < d->idMap.size()); + DENG_ASSERT(d->idMap[internalId] != 0); - pool->idMap[internalId]->setUserPointer(ptr); // O(1) + d->idMap[internalId]->setUserPointer(ptr); // O(1) } -void* StringPool_UserPointer(StringPool* pool, StringPoolId id) +void* StringPool::userPointer(StringPoolId id) const { if(id == 0) return NULL; - const InternalId internalId = IMPORT_ID(id); + InternalId const internalId = IMPORT_ID(id); - DENG_ASSERT(pool); - DENG_ASSERT(internalId < pool->idMap.size()); - DENG_ASSERT(pool->idMap[internalId] != 0); + DENG_ASSERT(internalId < d->idMap.size()); + DENG_ASSERT(d->idMap[internalId] != 0); - return pool->idMap[internalId]->userPointer(); // O(1) + return d->idMap[internalId]->userPointer(); // O(1) } -StringPoolId StringPool_IsInterned(const StringPool* pool, const ddstring_t* str) +StringPoolId StringPool::isInterned(ddstring_t const* str) const { - DENG_ASSERT(pool); - Interns::const_iterator found = pool->findIntern(Str_Text(str)); // O(log n) - if(found != pool->interns.end()) + Interns::const_iterator found = d->findIntern(Str_Text(str)); // O(log n) + if(found != d->interns.end()) { return EXPORT_ID(found->id()); } @@ -455,101 +397,271 @@ StringPoolId StringPool_IsInterned(const StringPool* pool, const ddstring_t* str return 0; } -const ddstring_t* StringPool_String(const StringPool* pool, StringPoolId id) +ddstring_t const* StringPool::string(StringPoolId id) const { - DENG_ASSERT(pool); if(id == 0) return NULL; - const InternalId internalId = IMPORT_ID(id); - DENG_ASSERT(internalId < pool->idMap.size()); - return *pool->idMap[internalId]; + InternalId const internalId = IMPORT_ID(id); + DENG_ASSERT(internalId < d->idMap.size()); + return *d->idMap[internalId]; } -boolean StringPool_Remove(StringPool* pool, const ddstring_t* str) +bool StringPool::remove(ddstring_t const* str) { - DENG_ASSERT(pool); - Interns::iterator found = pool->findIntern(Str_Text(str)); // O(log n) - if(found != pool->interns.end()) + Interns::iterator found = d->findIntern(Str_Text(str)); // O(log n) + if(found != d->interns.end()) { - pool->releaseAndDestroy(found->id(), &found); // O(1) (amortized) + d->releaseAndDestroy(found->id(), &found); // O(1) (amortized) return true; } return false; } -boolean StringPool_RemoveById(StringPool* pool, StringPoolId id) +bool StringPool::removeById(StringPoolId id) { - DENG_ASSERT(pool); - if(id == 0) return false; - const InternalId internalId = IMPORT_ID(id); - if(id >= pool->idMap.size()) return false; + InternalId const internalId = IMPORT_ID(id); + if(id >= d->idMap.size()) return false; - CaselessStr* str = pool->idMap[internalId]; + CaselessStr* str = d->idMap[internalId]; if(!str) return false; - pool->interns.erase(str); // O(log n) - pool->releaseAndDestroy(str->id()); + d->interns.erase(str); // O(log n) + d->releaseAndDestroy(str->id()); return true; } -int StringPool_Iterate(const StringPool* pool, int (*callback)(StringPoolId, void*), void* data) +int StringPool::iterate(int (*callback)(StringPoolId, void*), void* data) const { - DENG_ASSERT(pool); if(!callback) return 0; - for(uint i = 0; i < pool->idMap.size(); ++i) + for(uint i = 0; i < d->idMap.size(); ++i) { - if(!pool->idMap[i]) continue; + if(!d->idMap[i]) continue; int result = callback(EXPORT_ID(i), data); if(result) return result; } return 0; } -void StringPool_Write(const StringPool* pool, Writer* writer) +void StringPool::write(Writer* writer) const { - DENG_ASSERT(pool); - pool->serialize(writer); + // Number of strings altogether (includes unused ids). + Writer_WriteUInt32(writer, d->idMap.size()); + + // Write the interns. + Writer_WriteUInt32(writer, d->interns.size()); + for(Interns::const_iterator i = d->interns.begin(); i != d->interns.end(); ++i) + { + i->toStr()->serialize(writer); + } } -void StringPool_Read(StringPool* pool, Reader* reader) +void StringPool::read(Reader* reader) { - DENG_ASSERT(pool); - pool->deserialize(reader); + clear(); + + uint numStrings = Reader_ReadUInt32(reader); + d->idMap.resize(numStrings, 0); + + uint numInterns = Reader_ReadUInt32(reader); + while(numInterns--) + { + ddstring_t text; + Str_InitStd(&text); + + CaselessStr* str = new CaselessStr; + str->deserialize(reader, &text); + // Create a copy of the string whose ownership StringPool controls. + str->setText(STRINGPOOL_STRDUP(Str_Text(&text))); + d->interns.insert(str); + Str_Free(&text); + + // Update the id map. + d->idMap[str->id()] = str; + + d->count++; + } + + // Update the available ids. + for(uint i = 0; i < d->idMap.size(); ++i) + { + if(!d->idMap[i]) d->available.push_back(i); + } + + d->assertCount(); } #if _DEBUG typedef struct { int padding; ///< Number of characters to left-pad output. uint count; ///< Running total of the number of strings printed. - const StringPool* pool; ///< StringPool instance being printed. + StringPool const* pool; ///< StringPool instance being printed. } printinternedstring_params_t; static int printInternedString(StringPoolId internId, void* params) { printinternedstring_params_t* p = (printinternedstring_params_t*)params; - const ddstring_t* string = StringPool_String(p->pool, internId); + ddstring_t const* string = p->pool->string(internId); fprintf(stderr, "%*u %5u %s\n", p->padding, p->count++, internId, Str_Text(string)); return 0; // Continue iteration. } -void StringPool_Print(const StringPool* pool) +void StringPool::print() const { + int numDigits = 5; printinternedstring_params_t p; - int numDigits; - - if(!pool) return; - - numDigits = 5; p.padding = 2 + numDigits; - p.pool = pool; + p.pool = this; p.count = 0; - fprintf(stderr, "StringPool [%p]\n idx id string\n", (void*)pool); - StringPool_Iterate(pool, printInternedString, &p); - fprintf(stderr, " There is %u %s in the pool.\n", StringPool_Size(pool), - StringPool_Size(pool)==1? "string":"strings"); + fprintf(stderr, "StringPool [%p]\n idx id string\n", (void*)this); + iterate(printInternedString, &p); + fprintf(stderr, " There is %u %s in the pool.\n", size(), + size() == 1? "string":"strings"); +} +#endif + +} // namespace de + +/* + * C wrapper API: + */ + +#define TOINTERNAL(inst) \ + (inst) != 0? reinterpret_cast(inst) : NULL + +#define TOINTERNAL_CONST(inst) \ + (inst) != 0? reinterpret_cast(inst) : NULL + +#define SELF(inst) \ + DENG2_ASSERT(inst); \ + de::StringPool* self = TOINTERNAL(inst) + +#define SELF_CONST(inst) \ + DENG2_ASSERT(inst); \ + de::StringPool const* self = TOINTERNAL_CONST(inst) + +StringPool* StringPool_New(void) +{ + return reinterpret_cast(new de::StringPool()); +} + +StringPool* StringPool_NewWithStrings(ddstring_t const* strings, uint count) +{ + return reinterpret_cast(new de::StringPool(strings, count)); +} + +void StringPool_Delete(StringPool* pool) +{ + if(pool) + { + SELF(pool); + delete self; + } +} + +void StringPool_Clear(StringPool* pool) +{ + SELF(pool); + self->clear(); +} + +boolean StringPool_Empty(StringPool const* pool) +{ + SELF_CONST(pool); + return self->empty(); +} + +uint StringPool_Size(StringPool const* pool) +{ + SELF_CONST(pool); + return self->size(); +} + +StringPoolId StringPool_Intern(StringPool* pool, ddstring_t const* str) +{ + SELF(pool); + return self->intern(str); +} + +ddstring_t const* StringPool_InternAndRetrieve(StringPool* pool, ddstring_t const* str) +{ + SELF(pool); + return self->internAndRetrieve(str); +} + +void StringPool_SetUserValue(StringPool* pool, StringPoolId id, uint value) +{ + SELF(pool); + self->setUserValue(id, value); +} + +uint StringPool_UserValue(StringPool const* pool, StringPoolId id) +{ + SELF_CONST(pool); + return self->userValue(id); +} + +void StringPool_SetUserPointer(StringPool* pool, StringPoolId id, void* ptr) +{ + SELF(pool); + self->setUserPointer(id, ptr); +} + +void* StringPool_UserPointer(StringPool const* pool, StringPoolId id) +{ + SELF_CONST(pool); + return self->userPointer(id); +} + +StringPoolId StringPool_IsInterned(StringPool const* pool, ddstring_t const* str) +{ + SELF_CONST(pool); + return self->isInterned(str); +} + +ddstring_t const* StringPool_String(StringPool const* pool, StringPoolId id) +{ + SELF_CONST(pool); + return self->string(id); +} + +boolean StringPool_Remove(StringPool* pool, ddstring_t const* str) +{ + SELF(pool); + return self->remove(str); +} + +boolean StringPool_RemoveById(StringPool* pool, StringPoolId id) +{ + SELF(pool); + return self->removeById(id); +} + +int StringPool_Iterate(StringPool const* pool, int (*callback)(StringPoolId, void*), void* data) +{ + SELF_CONST(pool); + return self->iterate(callback, data); +} + +void StringPool_Write(StringPool const* pool, Writer* writer) +{ + SELF_CONST(pool); + self->write(writer); +} + +void StringPool_Read(StringPool* pool, Reader* reader) +{ + SELF(pool); + self->read(reader); +} + +#if _DEBUG +void StringPool_Print(StringPool const* pool) +{ + SELF_CONST(pool); + self->print(); } #endif