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