1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
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 // -------------------------------------------------------------------------*/
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
;
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
)
42 if (inStringPool
== NULL
|| inStream
== NULL
|| outString
== NULL
)
43 return SJME_ERROR_NULL_ARGUMENTS
;
45 /* Read in string length. */
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
);
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? */
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
)
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
;
87 if (inStringPool
== NULL
|| inUtf
== NULL
|| outString
== NULL
)
88 return SJME_ERROR_NULL_ARGUMENTS
;
91 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
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. */
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
;
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. */
116 if (possible
!= NULL
)
117 if (sjme_error_is(error
= sjme_alloc_weakRefGet(possible
,
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
;
133 /* Is this a filled slot? */
134 if (possible
== NULL
)
136 /* We can put a new string here. */
142 /* If hash or length differ, not a possible match */
143 if (possible
->hashCode
!= hash
|| possible
->length
!= inUtfLen
)
146 /* Must be exactly the same! */
147 if (0 == memcmp(&possible
->chars
[0], inUtf
, inUtfLen
))
154 /* String is not in the pool. */
157 /* Need to make the pool bigger? */
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
)
171 inStringPool
->strings
= strings
;
173 /* Clear old list. */
174 if (sjme_error_is(error
= sjme_alloc_free(oldStrings
)))
179 /* Allocate new result to store in the slot. */
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
)) ||
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
)
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(
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
;
217 return SJME_ERROR_NONE
;
223 sjme_alloc_free(result
);
226 fail_corruptPossible
:
228 /* Unlock before failing. */
229 sjme_thread_spinLockRelease(&inStringPool
->common
.lock
,
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. */
248 if (sjme_error_is(error
= sjme_list_alloc(
249 inPool
, SJME_STRING_POOL_GROW
,
250 &strings
, sjme_stringPool_string
, 0)) || strings
== NULL
)
253 /* Allocate result. */
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
;
261 result
->inPool
= inPool
;
262 result
->strings
= strings
;
265 *outStringPool
= result
;
266 return SJME_ERROR_NONE
;
271 sjme_alloc_free(result
);
274 sjme_alloc_free(strings
);
275 return sjme_error_default(error
);