1 /*
2 ** name.cpp
3 ** Implements int-as-string mapping.
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 2005-2007 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34
35 #include <string.h>
36 #include "name.h"
37 #include "c_dispatch.h"
38
39 // MACROS ------------------------------------------------------------------
40
41 // The number of bytes to allocate to each NameBlock unless somebody is evil
42 // and wants a really long name. In that case, it gets its own NameBlock
43 // that is just large enough to hold it.
44 #define BLOCK_SIZE 4096
45
46 // How many entries to grow the NameArray by when it needs to grow.
47 #define NAME_GROW_AMOUNT 256
48
49 // TYPES -------------------------------------------------------------------
50
51 // Name text is stored in a linked list of NameBlock structures. This
52 // is really the header for the block, with the remainder of the block
53 // being populated by text for names.
54
55 struct FName::NameManager::NameBlock
56 {
57 size_t NextAlloc;
58 NameBlock *NextBlock;
59 };
60
61 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
62
63 // PUBLIC DATA DEFINITIONS -------------------------------------------------
64
65 // PRIVATE DATA DEFINITIONS ------------------------------------------------
66
67 FName::NameManager FName::NameData;
68 bool FName::NameManager::Inited;
69
70 // Define the predefined names.
71 static const char *PredefinedNames[] =
72 {
73 #define xx(n) #n,
74 #include "namedef.h"
75 #undef xx
76 };
77
78 // CODE --------------------------------------------------------------------
79
80 //==========================================================================
81 //
82 // FName :: NameManager :: FindName
83 //
84 // Returns the index of a name. If the name does not exist and noCreate is
85 // true, then it returns false. If the name does not exist and noCreate is
86 // false, then the name is added to the table and its new index is returned.
87 //
88 //==========================================================================
89
FindName(const char * text,bool noCreate)90 int FName::NameManager::FindName (const char *text, bool noCreate)
91 {
92 if (!Inited)
93 {
94 InitBuckets ();
95 }
96
97 if (text == NULL)
98 {
99 return 0;
100 }
101
102 unsigned int hash = MakeKey (text);
103 unsigned int bucket = hash % HASH_SIZE;
104 int scanner = Buckets[bucket];
105
106 // See if the name already exists.
107 while (scanner >= 0)
108 {
109 if (NameArray[scanner].Hash == hash && stricmp (NameArray[scanner].Text, text) == 0)
110 {
111 return scanner;
112 }
113 scanner = NameArray[scanner].NextHash;
114 }
115
116 // If we get here, then the name does not exist.
117 if (noCreate)
118 {
119 return 0;
120 }
121
122 return AddName (text, hash, bucket);
123 }
124
125 //==========================================================================
126 //
127 // The same as above, but the text length is also passed, for creating
128 // a name from a substring or for speed if the length is already known.
129 //
130 //==========================================================================
131
FindName(const char * text,size_t textLen,bool noCreate)132 int FName::NameManager::FindName (const char *text, size_t textLen, bool noCreate)
133 {
134 if (!Inited)
135 {
136 InitBuckets ();
137 }
138
139 if (text == NULL)
140 {
141 return 0;
142 }
143
144 unsigned int hash = MakeKey (text, textLen);
145 unsigned int bucket = hash % HASH_SIZE;
146 int scanner = Buckets[bucket];
147
148 // See if the name already exists.
149 while (scanner >= 0)
150 {
151 if (NameArray[scanner].Hash == hash &&
152 strnicmp (NameArray[scanner].Text, text, textLen) == 0 &&
153 NameArray[scanner].Text[textLen] == '\0')
154 {
155 return scanner;
156 }
157 scanner = NameArray[scanner].NextHash;
158 }
159
160 // If we get here, then the name does not exist.
161 if (noCreate)
162 {
163 return 0;
164 }
165
166 return AddName (text, hash, bucket);
167 }
168
169 //==========================================================================
170 //
171 // FName :: NameManager :: InitBuckets
172 //
173 // Sets up the hash table and inserts all the default names into the table.
174 //
175 //==========================================================================
176
InitBuckets()177 void FName::NameManager::InitBuckets ()
178 {
179 Inited = true;
180 memset (Buckets, -1, sizeof(Buckets));
181
182 // Register built-in names. 'None' must be name 0.
183 for (size_t i = 0; i < countof(PredefinedNames); ++i)
184 {
185 FindName (PredefinedNames[i], false);
186 }
187 }
188
189 //==========================================================================
190 //
191 // FName :: NameManager :: AddName
192 //
193 // Adds a new name to the name table.
194 //
195 //==========================================================================
196
AddName(const char * text,unsigned int hash,unsigned int bucket)197 int FName::NameManager::AddName (const char *text, unsigned int hash, unsigned int bucket)
198 {
199 char *textstore;
200 NameBlock *block = Blocks;
201 size_t len = strlen (text) + 1;
202
203 // Get a block large enough for the name. Only the first block in the
204 // list is ever considered for name storage.
205 if (block == NULL || block->NextAlloc + len >= BLOCK_SIZE)
206 {
207 block = AddBlock (len);
208 }
209
210 // Copy the string into the block.
211 textstore = (char *)block + block->NextAlloc;
212 strcpy (textstore, text);
213 block->NextAlloc += len;
214
215 // Add an entry for the name to the NameArray
216 if (NumNames >= MaxNames)
217 {
218 // If no names have been defined yet, make the first allocation
219 // large enough to hold all the predefined names.
220 MaxNames += MaxNames == 0 ? countof(PredefinedNames) + NAME_GROW_AMOUNT : NAME_GROW_AMOUNT;
221
222 NameArray = (NameEntry *)M_Realloc (NameArray, MaxNames * sizeof(NameEntry));
223 }
224
225 NameArray[NumNames].Text = textstore;
226 NameArray[NumNames].Hash = hash;
227 NameArray[NumNames].NextHash = Buckets[bucket];
228 Buckets[bucket] = NumNames;
229
230 return NumNames++;
231 }
232
233 //==========================================================================
234 //
235 // FName :: NameManager :: AddBlock
236 //
237 // Creates a new NameBlock at least large enough to hold the required
238 // number of chars.
239 //
240 //==========================================================================
241
AddBlock(size_t len)242 FName::NameManager::NameBlock *FName::NameManager::AddBlock (size_t len)
243 {
244 NameBlock *block;
245
246 len += sizeof(NameBlock);
247 if (len < BLOCK_SIZE)
248 {
249 len = BLOCK_SIZE;
250 }
251 block = (NameBlock *)M_Malloc (len);
252 block->NextAlloc = sizeof(NameBlock);
253 block->NextBlock = Blocks;
254 Blocks = block;
255 return block;
256 }
257
258 //==========================================================================
259 //
260 // FName :: NameManager :: ~NameManager
261 //
262 // Release all the memory used for name bookkeeping.
263 //
264 //==========================================================================
265
~NameManager()266 FName::NameManager::~NameManager()
267 {
268 NameBlock *block, *next;
269
270 for (block = Blocks; block != NULL; block = next)
271 {
272 next = block->NextBlock;
273 M_Free (block);
274 }
275 Blocks = NULL;
276
277 if (NameArray != NULL)
278 {
279 M_Free (NameArray);
280 NameArray = NULL;
281 }
282 NumNames = MaxNames = 0;
283 memset (Buckets, -1, sizeof(Buckets));
284 }
285