2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/common/const-string.h>
42 for(auto& page : mPages)
47 char* Allocate(uint32_t size, uint32_t alignment)
49 uintptr_t mask = alignment - 1;
50 uintptr_t alignedOffset = (~reinterpret_cast<uintptr_t>(mCursor) + 1) & mask;
51 uintptr_t totalSize = size + alignedOffset;
53 if(totalSize > static_cast<uintptr_t>(mEnd - mCursor))
56 alignedOffset = (~reinterpret_cast<uintptr_t>(mCursor) + 1) & mask;
58 char* ptr = mCursor + alignedOffset;
66 constexpr size_t pageSize = 4096;
67 char* page = new char[pageSize];
68 mPages.push_back(page);
70 mEnd = mCursor + pageSize;
73 char* mCursor{nullptr};
75 std::vector<char*> mPages;
81 explicit StringEntry(uint16_t stringLength)
82 : mStringLength(stringLength)
85 size_t getStringLength() const
89 const char* GetStringData() const
91 return reinterpret_cast<const char*>(this + 1);
94 std::string_view GetString() const
96 return std::string_view(GetStringData(), getStringLength());
99 * GetStringEntryFromData - Given key data that is known to be embedded
100 * into a StringEntry, return the StringEntry itself.
102 static StringEntry& GetStringEntryFromData(const char* stringData)
104 char* ptr = const_cast<char*>(stringData) - sizeof(StringEntry);
105 return *reinterpret_cast<StringEntry*>(ptr);
108 * Create a StringEntry from string_view
111 static StringEntry* Create(std::string_view str, ArenaAllocator& allocator)
113 size_t length = str.size();
115 // Allocate a new item with space for the string at the end and a null
117 size_t allocSize = sizeof(StringEntry) + length + 1;
118 size_t alignment = alignof(StringEntry);
120 // Construct the value.
121 StringEntry* newItem = new(allocator.Allocate(allocSize, alignment)) StringEntry(length);
123 // Copy the string information.
124 char* strBuffer = const_cast<char*>(newItem->GetStringData());
126 memcpy(strBuffer, str.data(), length);
127 strBuffer[length] = 0; // Null terminate.
132 uint16_t mStringLength;
138 unsigned Size() const
147 static StringPool& Instance()
149 static StringPool object;
153 const char* Intern(std::string_view str)
155 const std::lock_guard<std::mutex> lock(mMutex);
157 auto bucketNumber = FindBucket(str);
158 auto& bucket = mTable[bucketNumber];
162 // string already exists
163 return bucket->GetStringData();
166 // assign the newly created StringEntry to the designated bucket.
167 bucket = StringEntry::Create(str, mAllocator);
171 bucketNumber = RehashTable(bucketNumber);
173 return mTable[bucketNumber]->GetStringData();
177 unsigned NextBucket(unsigned currentBucket, unsigned totalBucket, unsigned ProbeAmount)
179 return (currentBucket + ProbeAmount) & (totalBucket - 1);
184 unsigned newBuckets = 512;
186 * for memory efficiency and cache locality we store the StringEntry and corresponding HashValue in separate
187 * segments in this way each entry in the bucket will take 12byte instead of 16 if we keep them together.
188 * So we allocate one memory and treat 1st segment as a array of StringEntry* and the second segment as array of
191 mTable = static_cast<StringEntry**>(calloc(newBuckets + 1, sizeof(StringEntry**) + sizeof(unsigned)));
192 mBuckets = newBuckets;
195 unsigned FindBucket(std::string_view name)
197 unsigned bucketSize = mBuckets;
198 unsigned fullHashValue = std::hash<std::string_view>{}(name);
199 unsigned bucketNumber = fullHashValue & (bucketSize - 1);
200 // point to the start of the hashvalue segment.
201 unsigned* hashTable = reinterpret_cast<unsigned*>(mTable + mBuckets + 1);
203 unsigned probeAmt = 1;
206 StringEntry* bucketItem = mTable[bucketNumber];
207 // If we found an empty bucket, this key isn't in the table yet, return it.
210 hashTable[bucketNumber] = fullHashValue;
214 if(hashTable[bucketNumber] == fullHashValue)
216 // If the full hash value matches, check deeply for a match.
217 if(name == bucketItem->GetString())
224 // Okay, we didn't find the item. Probe to the next bucket.
225 bucketNumber = NextBucket(bucketNumber, bucketSize, probeAmt);
227 // Use quadratic probing, it has fewer clumping artifacts than linear
231 unsigned RehashTable(unsigned bucketNumber)
234 unsigned* hashTable = reinterpret_cast<unsigned*>(mTable + mBuckets + 1);
236 // If the hash table is now more than 3/4 full.
237 // grow/rehash the table.
238 if((mItems * 4) > (mBuckets * 3))
240 newSize = mBuckets * 2;
246 unsigned newBucketNumber = bucketNumber;
247 // Allocate one extra bucket which will always be non-empty.
248 auto newTable = static_cast<StringEntry**>(calloc(newSize + 1, sizeof(StringEntry*) + sizeof(unsigned)));
249 // point to the start of the hashvalue segment. as the pointer is of type StringEntry* , but the
250 // second segment keeps only unsigned data hence the reinterpret_cast.
251 unsigned* newHashTable = reinterpret_cast<unsigned*>(newTable + newSize + 1);
253 // Rehash all the items into their new buckets.
254 for(unsigned i = 0, j = mBuckets; i != j; ++i)
256 StringEntry* bucket = mTable[i];
259 // Fast case, bucket available.
260 unsigned fullHash = hashTable[i];
261 unsigned newBucket = fullHash & (newSize - 1);
262 if(!newTable[newBucket])
264 newTable[fullHash & (newSize - 1)] = bucket;
265 newHashTable[fullHash & (newSize - 1)] = fullHash;
266 if(i == bucketNumber)
267 newBucketNumber = newBucket;
271 // Otherwise probe for a spot.
272 unsigned ProbeSize = 1;
275 newBucket = NextBucket(newBucket, newSize, ProbeSize++);
276 } while(newTable[newBucket]);
278 // Finally found a slot. Fill it in.
279 newTable[newBucket] = bucket;
280 newHashTable[newBucket] = fullHash;
281 if(i == bucketNumber)
282 newBucketNumber = newBucket;
290 return newBucketNumber;
295 ArenaAllocator mAllocator;
296 StringEntry** mTable{nullptr};
297 unsigned mBuckets{0};
303 Dali::Internal::ConstString::ConstString(std::string_view str)
305 mString = StringPool::Instance().Intern(str);
308 size_t Dali::Internal::ConstString::GetLength() const
310 return mString ? StringEntry::GetStringEntryFromData(mString).getStringLength() : 0;
313 void Dali::Internal::ConstString::SetString(std::string_view str)
321 mString = StringPool::Instance().Intern(str);