Add link to posts because IFTTT really hates feeds without links.
[SquirrelJME.git] / nanocoat / src / stringPool.c
blob573d24544adaf3eed97131a1ec37fad136dc67a4
1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
10 #include <string.h>
12 #include "sjme/nvm/stringPool.h"
13 #include "sjme/cleanup.h"
14 #include "sjme/util.h"
16 /** The amount the size of the string pool should grow. */
17 #define SJME_STRING_POOL_GROW 256
19 sjme_errorCode sjme_stringPool_locateSeq(
20 sjme_attrInNotNull sjme_stringPool inStringPool,
21 sjme_attrInNotNull sjme_charSeq* inSeq,
22 sjme_attrOutNotNull sjme_stringPool_string* outString)
24 if (inStringPool == NULL || inSeq == NULL || outString == NULL)
25 return SJME_ERROR_NULL_ARGUMENTS;
27 sjme_todo("Impl?");
28 return SJME_ERROR_NOT_IMPLEMENTED;
31 sjme_errorCode sjme_stringPool_locateStreamR(
32 sjme_attrInNotNull sjme_stringPool inStringPool,
33 sjme_attrInNotNull sjme_stream_input inStream,
34 sjme_attrOutNotNull sjme_stringPool_string* outString
35 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
37 sjme_errorCode error;
38 sjme_jshort length;
39 sjme_jbyte* chars;
40 sjme_jint count;
42 if (inStringPool == NULL || inStream == NULL || outString == NULL)
43 return SJME_ERROR_NULL_ARGUMENTS;
45 /* Read in string length. */
46 length = -1;
47 if (sjme_error_is(error = sjme_stream_inputReadValueJS(
48 inStream, &length)) || length < 0)
49 return sjme_error_default(error);
51 /* Allocate buffer to store it within. */
52 chars = sjme_alloca(length);
53 if (chars == NULL)
54 return sjme_error_outOfMemory(NULL, NULL);
55 memset(chars, 0, length);
57 /* Need to read in everything. */
58 if (sjme_error_is(error = sjme_stream_inputReadFully(
59 inStream, &count, chars, length)))
60 return sjme_error_default(error);
62 /* Too short of a read? */
63 if (count != length)
64 return SJME_ERROR_END_OF_FILE;
66 /* Use normal locating logic. */
67 return sjme_stringPool_locateUtfR(inStringPool,
68 (sjme_lpcstr)chars, length, outString
69 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY);
72 sjme_errorCode sjme_stringPool_locateUtfR(
73 sjme_attrInNotNull sjme_stringPool inStringPool,
74 sjme_attrInNotNull sjme_lpcstr inUtf,
75 sjme_attrInNegativeOnePositive sjme_jint inUtfLen,
76 sjme_attrOutNotNull sjme_stringPool_string* outString
77 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
79 sjme_errorCode error;
80 sjme_jint hash, i, n, firstFree;
81 sjme_list_sjme_stringPool_string* strings;
82 sjme_list_sjme_stringPool_string* oldStrings;
83 sjme_stringPool_string result;
84 sjme_stringPool_string possible;
85 sjme_alloc_weak weak;
87 if (inStringPool == NULL || inUtf == NULL || outString == NULL)
88 return SJME_ERROR_NULL_ARGUMENTS;
90 if (inUtfLen < -1)
91 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
93 /* Lock pool. */
94 if (sjme_error_is(error = sjme_thread_spinLockGrab(
95 &inStringPool->common.lock)))
96 return sjme_error_default(error);
98 /* Determine actual string length, if unknown. */
99 if (inUtfLen < 0)
100 inUtfLen = strlen(inUtf);
102 /* Calculate hash of string. */
103 hash = sjme_string_hashN(inUtf, inUtfLen);
105 /* Try to locate the string first. */
106 strings = inStringPool->strings;
107 firstFree = -1;
108 result = NULL;
109 for (i = 0, n = strings->length; i < n; i++)
111 /* There might be an element here. */
112 possible = strings->elements[i];
114 /* Check to see if this no longer exists in the pool. */
115 weak = NULL;
116 if (possible != NULL)
117 if (sjme_error_is(error = sjme_alloc_weakRefGet(possible,
118 &weak)))
120 /* Not a valid error here. */
121 if (error != SJME_ERROR_NOT_WEAK_REFERENCE)
122 goto fail_corruptPossible;
125 /* Is a weak reference but does not actually point to the string? */
126 /* If so then this was freed! */
127 if (weak != NULL && weak->pointer != possible)
129 strings->elements[i] = NULL;
130 possible = NULL;
133 /* Is this a filled slot? */
134 if (possible == NULL)
136 /* We can put a new string here. */
137 if (firstFree < 0)
138 firstFree = i;
139 continue;
142 /* If hash or length differ, not a possible match */
143 if (possible->hashCode != hash || possible->length != inUtfLen)
144 continue;
146 /* Must be exactly the same! */
147 if (0 == memcmp(&possible->chars[0], inUtf, inUtfLen))
149 result = possible;
150 break;
154 /* String is not in the pool. */
155 if (result == NULL)
157 /* Need to make the pool bigger? */
158 if (firstFree < 0)
160 /* First free is always at the end. */
161 firstFree = strings->length;
163 /* Reallocate the list. */
164 oldStrings = strings;
165 if (sjme_error_is(error = sjme_list_copy(inStringPool->inPool,
166 strings->length + SJME_STRING_POOL_GROW, strings,
167 &strings, sjme_stringPool_string, 0)) || strings == NULL)
168 goto fail_growList;
170 /* Set new list. */
171 inStringPool->strings = strings;
173 /* Clear old list. */
174 if (sjme_error_is(error = sjme_alloc_free(oldStrings)))
175 goto fail_freeOld;
176 oldStrings = NULL;
179 /* Allocate new result to store in the slot. */
180 result = NULL;
181 #if defined(SJME_CONFIG_DEBUG)
182 if (sjme_error_is(error = sjme_nvm_allocR(inStringPool->inPool,
183 sizeof(*result) + inUtfLen,
184 SJME_NVM_STRUCT_STRING_POOL_STRING,
185 SJME_AS_NVM_COMMONP(&result), file, line, func)) ||
186 result == NULL)
187 #else
188 if (sjme_error_is(error = sjme_nvm_alloc(inStringPool->inPool,
189 sizeof(*result) + inUtfLen,
190 SJME_NVM_STRUCT_STRING_POOL_STRING,
191 SJME_AS_NVM_COMMONP(&result))) || result == NULL)
192 #endif
193 goto fail_stringAlloc;
195 /* Fill in information. */
196 memmove(&result->chars[0], inUtf, inUtfLen);
197 result->hashCode = hash;
198 result->length = inUtfLen;
199 result->seq.context = &result->chars[0];
201 /* Count up as the pool itself references it. */
202 if (sjme_error_is(error = sjme_alloc_weakRef(
203 result, NULL)))
204 goto fail_countUp;
206 /* Store it into the pool. */
207 strings->elements[firstFree] = result;
210 /* Release the lock. */
211 if (sjme_error_is(error = sjme_thread_spinLockRelease(
212 &inStringPool->common.lock, NULL)))
213 goto fail_releaseLock;
215 /* Success! */
216 *outString = result;
217 return SJME_ERROR_NONE;
219 fail_countUp:
220 fail_initCommon:
221 fail_stringAlloc:
222 if (result != NULL)
223 sjme_alloc_free(result);
224 fail_freeOld:
225 fail_growList:
226 fail_corruptPossible:
227 fail_releaseLock:
228 /* Unlock before failing. */
229 sjme_thread_spinLockRelease(&inStringPool->common.lock,
230 NULL);
232 return sjme_error_default(error);
235 sjme_errorCode sjme_stringPool_new(
236 sjme_attrInNotNull sjme_alloc_pool* inPool,
237 sjme_attrOutNotNull sjme_stringPool* outStringPool)
239 sjme_errorCode error;
240 sjme_stringPool result;
241 sjme_list_sjme_stringPool_string* strings;
243 if (inPool == NULL || outStringPool == NULL)
244 return SJME_ERROR_NULL_ARGUMENTS;
246 /* Make sure we have the memory to store the buffer. */
247 strings = NULL;
248 if (sjme_error_is(error = sjme_list_alloc(
249 inPool, SJME_STRING_POOL_GROW,
250 &strings, sjme_stringPool_string, 0)) || strings == NULL)
251 goto fail_allocList;
253 /* Allocate result. */
254 result = NULL;
255 if (sjme_error_is(error = sjme_nvm_alloc(inPool,
256 sizeof(*result), SJME_NVM_STRUCT_STRING_POOL,
257 SJME_AS_NVM_COMMONP(&result))) || result == NULL)
258 goto fail_allocResult;
260 /* Setup fields. */
261 result->inPool = inPool;
262 result->strings = strings;
264 /* Success! */
265 *outStringPool = result;
266 return SJME_ERROR_NONE;
268 fail_initCommon:
269 fail_allocResult:
270 if (result != NULL)
271 sjme_alloc_free(result);
272 fail_allocList:
273 if (strings != NULL)
274 sjme_alloc_free(strings);
275 return sjme_error_default(error);