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