1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "String/BsStringID.h"
4
5namespace bs
6{
7 const StringID StringID::NONE;
8
9 volatile StringID::InitStatics StringID::mInitStatics = StringID::InitStatics();
10
11 StringID::InternalData* StringID::mStringHashTable[HASH_TABLE_SIZE];
12 StringID::InternalData* StringID::mChunks[MAX_CHUNK_COUNT];
13
14 UINT32 StringID::mNextId = 0;
15 UINT32 StringID::mNumChunks = 0;
16 SpinLock StringID::mSync;
17
18 StringID::InitStatics::InitStatics()
19 {
20 ScopedSpinLock lock(mSync);
21
22 memset(mStringHashTable, 0, sizeof(mStringHashTable));
23 memset(mChunks, 0, sizeof(mChunks));
24
25 mChunks[0] = (InternalData*)bs_alloc(sizeof(InternalData) * ELEMENTS_PER_CHUNK);
26 memset(mChunks[0], 0, sizeof(InternalData) * ELEMENTS_PER_CHUNK);
27
28 mNumChunks++;
29 }
30
31 template<class T>
32 void StringID::construct(T const& name)
33 {
34 assert(StringIDUtil<T>::size(name) <= STRING_SIZE);
35
36 UINT32 hash = calcHash(name) & (sizeof(mStringHashTable) / sizeof(mStringHashTable[0]) - 1);
37 InternalData* existingEntry = mStringHashTable[hash];
38
39 while (existingEntry != nullptr)
40 {
41 if (StringIDUtil<T>::compare(name, existingEntry->chars))
42 {
43 mData = existingEntry;
44 return;
45 }
46
47 existingEntry = existingEntry->next;
48 }
49
50 ScopedSpinLock lock(mSync);
51
52 // Search for the value again in case other thread just added it
53 existingEntry = mStringHashTable[hash];
54 InternalData* lastEntry = nullptr;
55 while (existingEntry != nullptr)
56 {
57 if (StringIDUtil<T>::compare(name, existingEntry->chars))
58 {
59 mData = existingEntry;
60 return;
61 }
62
63 lastEntry = existingEntry;
64 existingEntry = existingEntry->next;
65 }
66
67 mData = allocEntry();
68 StringIDUtil<T>::copy(name, mData->chars);
69
70 if (lastEntry == nullptr)
71 mStringHashTable[hash] = mData;
72 else
73 lastEntry->next = mData;
74 }
75
76 template<class T>
77 UINT32 StringID::calcHash(T const& input)
78 {
79 UINT32 size = StringIDUtil<T>::size(input);
80
81 UINT32 hash = 0;
82 for (UINT32 i = 0; i < size; i++)
83 hash = hash * 101 + input[i];
84
85 return hash;
86 }
87
88 StringID::InternalData* StringID::allocEntry()
89 {
90 UINT32 chunkIdx = mNextId / ELEMENTS_PER_CHUNK;
91
92 assert(chunkIdx < MAX_CHUNK_COUNT);
93 assert(chunkIdx <= mNumChunks); // Can only increment sequentially
94
95 if (chunkIdx >= mNumChunks)
96 {
97 mChunks[chunkIdx] = (InternalData*)bs_alloc(sizeof(InternalData) * ELEMENTS_PER_CHUNK);
98 memset(mChunks[chunkIdx], 0, sizeof(InternalData) * ELEMENTS_PER_CHUNK);
99
100 mNumChunks++;
101 }
102
103 InternalData* chunk = mChunks[chunkIdx];
104 UINT32 chunkSpecificIndex = mNextId % ELEMENTS_PER_CHUNK;
105
106 InternalData* newEntry = &chunk[chunkSpecificIndex];
107 newEntry->id = mNextId++;
108 newEntry->next = nullptr;
109
110 return newEntry;
111 }
112
113 template<>
114 class StringID::StringIDUtil<const char*>
115 {
116 public:
117 static UINT32 size(const char* const& input) { return (UINT32)strlen(input); }
118 static void copy(const char* const& input, char* dest) { memcpy(dest, input, strlen(input) + 1); }
119 static bool compare(const char* const& a, char* b) { return strcmp(a, b) == 0; }
120 };
121
122 template<>
123 class StringID::StringIDUtil <String>
124 {
125 public:
126 static UINT32 size(String const& input) { return (UINT32)input.length(); }
127 static void copy(String const& input, char* dest)
128 {
129 UINT32 len = (UINT32)input.length();
130 input.copy(dest, len);
131 dest[len] = '\0';
132 }
133 static bool compare(String const& a, char* b) { return a.compare(b) == 0; }
134 };
135
136 template BS_UTILITY_EXPORT void StringID::construct(const char* const&);
137 template BS_UTILITY_EXPORT void StringID::construct(String const&);
138
139 template BS_UTILITY_EXPORT UINT32 StringID::calcHash(const char* const&);
140 template BS_UTILITY_EXPORT UINT32 StringID::calcHash(String const&);
141}
142