1 /* 2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "StringPool.h" 8 9 #include "DebugSupport.h" 10 11 12 static const size_t kInitialStringTableSize = 128; 13 14 static char sStringsBuffer[sizeof(StringDataHash)]; 15 16 StringData StringData::fEmptyString(StringDataKey("", 0)); 17 18 mutex StringPool::sLock; 19 StringDataHash* StringPool::sStrings; 20 21 22 // #pragma mark - StringData 23 24 25 /*static*/ void 26 StringData::Init() 27 { 28 new(&fEmptyString) StringData(StringDataKey("", 0)); 29 } 30 31 32 // #pragma mark - StringPool 33 34 35 /*static*/ status_t 36 StringPool::Init() 37 { 38 sStrings = new(sStringsBuffer) StringDataHash; 39 status_t error = sStrings->Init(kInitialStringTableSize); 40 if (error != B_OK) { 41 sStrings->~StringDataHash(); 42 sStrings = NULL; 43 return error; 44 } 45 46 mutex_init(&sLock, "string pool"); 47 48 StringData::Init(); 49 sStrings->Insert(StringData::Empty()); 50 51 return B_OK; 52 } 53 54 55 /*static*/ void 56 StringPool::Cleanup() 57 { 58 sStrings->Remove(StringData::Empty()); 59 60 sStrings->~StringDataHash(); 61 sStrings = NULL; 62 63 mutex_destroy(&sLock); 64 } 65 66 67 /*static*/ inline StringData* 68 StringPool::_GetLocked(const StringDataKey& key) 69 { 70 if (StringData* string = sStrings->Lookup(key)) { 71 if (!string->AcquireReference()) 72 return string; 73 74 // The object was fully dereferenced and will be deleted. Remove it 75 // from the hash table, so it isn't in the way. 76 sStrings->Remove(string); 77 } 78 79 return NULL; 80 } 81 82 83 /*static*/ StringData* 84 StringPool::Get(const char* string, size_t length) 85 { 86 MutexLocker locker(sLock); 87 StringDataKey key(string, length); 88 StringData* data = _GetLocked(key); 89 if (data != NULL) 90 return data; 91 92 locker.Unlock(); 93 94 StringData* newString = StringData::Create(key); 95 if (newString == NULL) 96 return NULL; 97 98 locker.Lock(); 99 100 data = _GetLocked(key); 101 if (data != NULL) { 102 locker.Unlock(); 103 newString->Delete(); 104 return data; 105 } 106 107 sStrings->Insert(newString); 108 return newString; 109 } 110 111 112 /*static*/ void 113 StringPool::LastReferenceReleased(StringData* data) 114 { 115 MutexLocker locker(sLock); 116 sStrings->Remove(data); 117 locker.Unlock(); 118 data->Delete(); 119 } 120 121 122 /*static*/ void 123 StringPool::DumpUsageStatistics() 124 { 125 size_t unsharedStringCount = 0; 126 size_t totalReferenceCount = 0; 127 size_t totalStringSize = 0; 128 size_t totalStringSizeWithDuplicates = 0; 129 130 MutexLocker locker(sLock); 131 for (StringDataHash::Iterator it = sStrings->GetIterator(); it.HasNext();) { 132 StringData* data = it.Next(); 133 int32 referenceCount = data->CountReferences(); 134 totalReferenceCount += referenceCount; 135 if (referenceCount == 1) 136 unsharedStringCount++; 137 138 size_t stringSize = strlen(data->String() + 1); 139 totalStringSize += stringSize; 140 totalStringSizeWithDuplicates += stringSize * referenceCount; 141 } 142 143 size_t stringCount = sStrings->CountElements(); 144 size_t overhead = stringCount * (sizeof(StringData) - 1); 145 146 INFORM("StringPool usage:\n"); 147 INFORM(" total unique strings: %8zu, %8zu bytes, overhead: %zu bytes\n", 148 stringCount, totalStringSize, overhead); 149 INFORM(" total strings with dups: %8zu, %8zu bytes\n", totalReferenceCount, 150 totalStringSizeWithDuplicates); 151 INFORM(" unshared strings: %8zu\n", unsharedStringCount); 152 INFORM(" bytes saved: %8zd\n", 153 (ssize_t)(totalStringSizeWithDuplicates - totalStringSize - overhead)); 154 } 155