diff options
Diffstat (limited to 'common/unqlite/jx9_memobj.c')
-rw-r--r-- | common/unqlite/jx9_memobj.c | 1078 |
1 files changed, 1078 insertions, 0 deletions
diff --git a/common/unqlite/jx9_memobj.c b/common/unqlite/jx9_memobj.c new file mode 100644 index 0000000..7bc7f2f --- /dev/null +++ b/common/unqlite/jx9_memobj.c | |||
@@ -0,0 +1,1078 @@ | |||
1 | /* | ||
2 | * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. | ||
3 | * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ | ||
4 | * Version 1.7.2 | ||
5 | * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES | ||
6 | * please contact Symisc Systems via: | ||
7 | * legal@symisc.net | ||
8 | * licensing@symisc.net | ||
9 | * contact@symisc.net | ||
10 | * or visit: | ||
11 | * http://jx9.symisc.net/ | ||
12 | */ | ||
13 | /* $SymiscID: memobj.c v2.7 FreeBSD 2012-08-09 03:40 stable <chm@symisc.net> $ */ | ||
14 | #ifndef JX9_AMALGAMATION | ||
15 | #include "jx9Int.h" | ||
16 | #endif | ||
17 | /* This file manage low-level stuff related to indexed memory objects [i.e: jx9_value] */ | ||
18 | /* | ||
19 | * Notes on memory objects [i.e: jx9_value]. | ||
20 | * Internally, the JX9 virtual machine manipulates nearly all JX9 values | ||
21 | * [i.e: string, int, float, resource, object, bool, null..] as jx9_values structures. | ||
22 | * Each jx9_values struct may cache multiple representations (string, | ||
23 | * integer etc.) of the same value. | ||
24 | */ | ||
25 | /* | ||
26 | * Convert a 64-bit IEEE double into a 64-bit signed integer. | ||
27 | * If the double is too large, return 0x8000000000000000. | ||
28 | * | ||
29 | * Most systems appear to do this simply by assigning ariables and without | ||
30 | * the extra range tests. | ||
31 | * But there are reports that windows throws an expection if the floating | ||
32 | * point value is out of range. | ||
33 | */ | ||
34 | static sxi64 MemObjRealToInt(jx9_value *pObj) | ||
35 | { | ||
36 | #ifdef JX9_OMIT_FLOATING_POINT | ||
37 | /* Real and 64bit integer are the same when floating point arithmetic | ||
38 | * is omitted from the build. | ||
39 | */ | ||
40 | return pObj->x.rVal; | ||
41 | #else | ||
42 | /* | ||
43 | ** Many compilers we encounter do not define constants for the | ||
44 | ** minimum and maximum 64-bit integers, or they define them | ||
45 | ** inconsistently. And many do not understand the "LL" notation. | ||
46 | ** So we define our own static constants here using nothing | ||
47 | ** larger than a 32-bit integer constant. | ||
48 | */ | ||
49 | static const sxi64 maxInt = LARGEST_INT64; | ||
50 | static const sxi64 minInt = SMALLEST_INT64; | ||
51 | jx9_real r = pObj->x.rVal; | ||
52 | if( r<(jx9_real)minInt ){ | ||
53 | return minInt; | ||
54 | }else if( r>(jx9_real)maxInt ){ | ||
55 | /* minInt is correct here - not maxInt. It turns out that assigning | ||
56 | ** a very large positive number to an integer results in a very large | ||
57 | ** negative integer. This makes no sense, but it is what x86 hardware | ||
58 | ** does so for compatibility we will do the same in software. */ | ||
59 | return minInt; | ||
60 | }else{ | ||
61 | return (sxi64)r; | ||
62 | } | ||
63 | #endif | ||
64 | } | ||
65 | /* | ||
66 | * Convert a raw token value typically a stream of digit [i.e: hex, octal, binary or decimal] | ||
67 | * to a 64-bit integer. | ||
68 | */ | ||
69 | JX9_PRIVATE sxi64 jx9TokenValueToInt64(SyString *pVal) | ||
70 | { | ||
71 | sxi64 iVal = 0; | ||
72 | if( pVal->nByte <= 0 ){ | ||
73 | return 0; | ||
74 | } | ||
75 | if( pVal->zString[0] == '0' ){ | ||
76 | sxi32 c; | ||
77 | if( pVal->nByte == sizeof(char) ){ | ||
78 | return 0; | ||
79 | } | ||
80 | c = pVal->zString[1]; | ||
81 | if( c == 'x' || c == 'X' ){ | ||
82 | /* Hex digit stream */ | ||
83 | SyHexStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0); | ||
84 | }else if( c == 'b' || c == 'B' ){ | ||
85 | /* Binary digit stream */ | ||
86 | SyBinaryStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0); | ||
87 | }else{ | ||
88 | /* Octal digit stream */ | ||
89 | SyOctalStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0); | ||
90 | } | ||
91 | }else{ | ||
92 | /* Decimal digit stream */ | ||
93 | SyStrToInt64(pVal->zString, pVal->nByte, (void *)&iVal, 0); | ||
94 | } | ||
95 | return iVal; | ||
96 | } | ||
97 | /* | ||
98 | * Return some kind of 64-bit integer value which is the best we can | ||
99 | * do at representing the value that pObj describes as a string | ||
100 | * representation. | ||
101 | */ | ||
102 | static sxi64 MemObjStringToInt(jx9_value *pObj) | ||
103 | { | ||
104 | SyString sVal; | ||
105 | SyStringInitFromBuf(&sVal, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); | ||
106 | return jx9TokenValueToInt64(&sVal); | ||
107 | } | ||
108 | /* | ||
109 | * Return some kind of integer value which is the best we can | ||
110 | * do at representing the value that pObj describes as an integer. | ||
111 | * If pObj is an integer, then the value is exact. If pObj is | ||
112 | * a floating-point then the value returned is the integer part. | ||
113 | * If pObj is a string, then we make an attempt to convert it into | ||
114 | * a integer and return that. | ||
115 | * If pObj represents a NULL value, return 0. | ||
116 | */ | ||
117 | static sxi64 MemObjIntValue(jx9_value *pObj) | ||
118 | { | ||
119 | sxi32 iFlags; | ||
120 | iFlags = pObj->iFlags; | ||
121 | if (iFlags & MEMOBJ_REAL ){ | ||
122 | return MemObjRealToInt(&(*pObj)); | ||
123 | }else if( iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ | ||
124 | return pObj->x.iVal; | ||
125 | }else if (iFlags & MEMOBJ_STRING) { | ||
126 | return MemObjStringToInt(&(*pObj)); | ||
127 | }else if( iFlags & MEMOBJ_NULL ){ | ||
128 | return 0; | ||
129 | }else if( iFlags & MEMOBJ_HASHMAP ){ | ||
130 | jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther; | ||
131 | sxu32 n = pMap->nEntry; | ||
132 | jx9HashmapUnref(pMap); | ||
133 | /* Return total number of entries in the hashmap */ | ||
134 | return n; | ||
135 | }else if(iFlags & MEMOBJ_RES ){ | ||
136 | return pObj->x.pOther != 0; | ||
137 | } | ||
138 | /* CANT HAPPEN */ | ||
139 | return 0; | ||
140 | } | ||
141 | /* | ||
142 | * Return some kind of real value which is the best we can | ||
143 | * do at representing the value that pObj describes as a real. | ||
144 | * If pObj is a real, then the value is exact.If pObj is an | ||
145 | * integer then the integer is promoted to real and that value | ||
146 | * is returned. | ||
147 | * If pObj is a string, then we make an attempt to convert it | ||
148 | * into a real and return that. | ||
149 | * If pObj represents a NULL value, return 0.0 | ||
150 | */ | ||
151 | static jx9_real MemObjRealValue(jx9_value *pObj) | ||
152 | { | ||
153 | sxi32 iFlags; | ||
154 | iFlags = pObj->iFlags; | ||
155 | if( iFlags & MEMOBJ_REAL ){ | ||
156 | return pObj->x.rVal; | ||
157 | }else if (iFlags & (MEMOBJ_INT|MEMOBJ_BOOL) ){ | ||
158 | return (jx9_real)pObj->x.iVal; | ||
159 | }else if (iFlags & MEMOBJ_STRING){ | ||
160 | SyString sString; | ||
161 | #ifdef JX9_OMIT_FLOATING_POINT | ||
162 | jx9_real rVal = 0; | ||
163 | #else | ||
164 | jx9_real rVal = 0.0; | ||
165 | #endif | ||
166 | SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); | ||
167 | if( SyBlobLength(&pObj->sBlob) > 0 ){ | ||
168 | /* Convert as much as we can */ | ||
169 | #ifdef JX9_OMIT_FLOATING_POINT | ||
170 | rVal = MemObjStringToInt(&(*pObj)); | ||
171 | #else | ||
172 | SyStrToReal(sString.zString, sString.nByte, (void *)&rVal, 0); | ||
173 | #endif | ||
174 | } | ||
175 | return rVal; | ||
176 | }else if( iFlags & MEMOBJ_NULL ){ | ||
177 | #ifdef JX9_OMIT_FLOATING_POINT | ||
178 | return 0; | ||
179 | #else | ||
180 | return 0.0; | ||
181 | #endif | ||
182 | }else if( iFlags & MEMOBJ_HASHMAP ){ | ||
183 | /* Return the total number of entries in the hashmap */ | ||
184 | jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther; | ||
185 | jx9_real n = (jx9_real)pMap->nEntry; | ||
186 | jx9HashmapUnref(pMap); | ||
187 | return n; | ||
188 | }else if(iFlags & MEMOBJ_RES ){ | ||
189 | return (jx9_real)(pObj->x.pOther != 0); | ||
190 | } | ||
191 | /* NOT REACHED */ | ||
192 | return 0; | ||
193 | } | ||
194 | /* | ||
195 | * Return the string representation of a given jx9_value. | ||
196 | * This function never fail and always return SXRET_OK. | ||
197 | */ | ||
198 | static sxi32 MemObjStringValue(SyBlob *pOut,jx9_value *pObj) | ||
199 | { | ||
200 | if( pObj->iFlags & MEMOBJ_REAL ){ | ||
201 | SyBlobFormat(&(*pOut), "%.15g", pObj->x.rVal); | ||
202 | }else if( pObj->iFlags & MEMOBJ_INT ){ | ||
203 | SyBlobFormat(&(*pOut), "%qd", pObj->x.iVal); | ||
204 | /* %qd (BSD quad) is equivalent to %lld in the libc printf */ | ||
205 | }else if( pObj->iFlags & MEMOBJ_BOOL ){ | ||
206 | if( pObj->x.iVal ){ | ||
207 | SyBlobAppend(&(*pOut),"true", sizeof("true")-1); | ||
208 | }else{ | ||
209 | SyBlobAppend(&(*pOut),"false", sizeof("false")-1); | ||
210 | } | ||
211 | }else if( pObj->iFlags & MEMOBJ_HASHMAP ){ | ||
212 | /* Serialize JSON object or array */ | ||
213 | jx9JsonSerialize(pObj,pOut); | ||
214 | jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther); | ||
215 | }else if(pObj->iFlags & MEMOBJ_RES ){ | ||
216 | SyBlobFormat(&(*pOut), "ResourceID_%#x", pObj->x.pOther); | ||
217 | } | ||
218 | return SXRET_OK; | ||
219 | } | ||
220 | /* | ||
221 | * Return some kind of boolean value which is the best we can do | ||
222 | * at representing the value that pObj describes as a boolean. | ||
223 | * When converting to boolean, the following values are considered FALSE: | ||
224 | * NULL | ||
225 | * the boolean FALSE itself. | ||
226 | * the integer 0 (zero). | ||
227 | * the real 0.0 (zero). | ||
228 | * the empty string, a stream of zero [i.e: "0", "00", "000", ...] and the string | ||
229 | * "false". | ||
230 | * an array with zero elements. | ||
231 | */ | ||
232 | static sxi32 MemObjBooleanValue(jx9_value *pObj) | ||
233 | { | ||
234 | sxi32 iFlags; | ||
235 | iFlags = pObj->iFlags; | ||
236 | if (iFlags & MEMOBJ_REAL ){ | ||
237 | #ifdef JX9_OMIT_FLOATING_POINT | ||
238 | return pObj->x.rVal ? 1 : 0; | ||
239 | #else | ||
240 | return pObj->x.rVal != 0.0 ? 1 : 0; | ||
241 | #endif | ||
242 | }else if( iFlags & MEMOBJ_INT ){ | ||
243 | return pObj->x.iVal ? 1 : 0; | ||
244 | }else if (iFlags & MEMOBJ_STRING) { | ||
245 | SyString sString; | ||
246 | SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); | ||
247 | if( sString.nByte == 0 ){ | ||
248 | /* Empty string */ | ||
249 | return 0; | ||
250 | }else if( (sString.nByte == sizeof("true") - 1 && SyStrnicmp(sString.zString, "true", sizeof("true")-1) == 0) || | ||
251 | (sString.nByte == sizeof("on") - 1 && SyStrnicmp(sString.zString, "on", sizeof("on")-1) == 0) || | ||
252 | (sString.nByte == sizeof("yes") - 1 && SyStrnicmp(sString.zString, "yes", sizeof("yes")-1) == 0) ){ | ||
253 | return 1; | ||
254 | }else if( sString.nByte == sizeof("false") - 1 && SyStrnicmp(sString.zString, "false", sizeof("false")-1) == 0 ){ | ||
255 | return 0; | ||
256 | }else{ | ||
257 | const char *zIn, *zEnd; | ||
258 | zIn = sString.zString; | ||
259 | zEnd = &zIn[sString.nByte]; | ||
260 | while( zIn < zEnd && zIn[0] == '0' ){ | ||
261 | zIn++; | ||
262 | } | ||
263 | return zIn >= zEnd ? 0 : 1; | ||
264 | } | ||
265 | }else if( iFlags & MEMOBJ_NULL ){ | ||
266 | return 0; | ||
267 | }else if( iFlags & MEMOBJ_HASHMAP ){ | ||
268 | jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther; | ||
269 | sxu32 n = pMap->nEntry; | ||
270 | jx9HashmapUnref(pMap); | ||
271 | return n > 0 ? TRUE : FALSE; | ||
272 | }else if(iFlags & MEMOBJ_RES ){ | ||
273 | return pObj->x.pOther != 0; | ||
274 | } | ||
275 | /* NOT REACHED */ | ||
276 | return 0; | ||
277 | } | ||
278 | /* | ||
279 | * If the jx9_value is of type real, try to make it an integer also. | ||
280 | */ | ||
281 | static sxi32 MemObjTryIntger(jx9_value *pObj) | ||
282 | { | ||
283 | sxi64 iVal = MemObjRealToInt(&(*pObj)); | ||
284 | /* Only mark the value as an integer if | ||
285 | ** | ||
286 | ** (1) the round-trip conversion real->int->real is a no-op, and | ||
287 | ** (2) The integer is neither the largest nor the smallest | ||
288 | ** possible integer | ||
289 | ** | ||
290 | ** The second and third terms in the following conditional enforces | ||
291 | ** the second condition under the assumption that addition overflow causes | ||
292 | ** values to wrap around. On x86 hardware, the third term is always | ||
293 | ** true and could be omitted. But we leave it in because other | ||
294 | ** architectures might behave differently. | ||
295 | */ | ||
296 | if( pObj->x.rVal ==(jx9_real)iVal && iVal>SMALLEST_INT64 && iVal<LARGEST_INT64 ){ | ||
297 | pObj->x.iVal = iVal; | ||
298 | pObj->iFlags = MEMOBJ_INT; | ||
299 | } | ||
300 | return SXRET_OK; | ||
301 | } | ||
302 | /* | ||
303 | * Convert a jx9_value to type integer.Invalidate any prior representations. | ||
304 | */ | ||
305 | JX9_PRIVATE sxi32 jx9MemObjToInteger(jx9_value *pObj) | ||
306 | { | ||
307 | if( (pObj->iFlags & MEMOBJ_INT) == 0 ){ | ||
308 | /* Preform the conversion */ | ||
309 | pObj->x.iVal = MemObjIntValue(&(*pObj)); | ||
310 | /* Invalidate any prior representations */ | ||
311 | SyBlobRelease(&pObj->sBlob); | ||
312 | MemObjSetType(pObj, MEMOBJ_INT); | ||
313 | } | ||
314 | return SXRET_OK; | ||
315 | } | ||
316 | /* | ||
317 | * Convert a jx9_value to type real (Try to get an integer representation also). | ||
318 | * Invalidate any prior representations | ||
319 | */ | ||
320 | JX9_PRIVATE sxi32 jx9MemObjToReal(jx9_value *pObj) | ||
321 | { | ||
322 | if((pObj->iFlags & MEMOBJ_REAL) == 0 ){ | ||
323 | /* Preform the conversion */ | ||
324 | pObj->x.rVal = MemObjRealValue(&(*pObj)); | ||
325 | /* Invalidate any prior representations */ | ||
326 | SyBlobRelease(&pObj->sBlob); | ||
327 | MemObjSetType(pObj, MEMOBJ_REAL); | ||
328 | } | ||
329 | return SXRET_OK; | ||
330 | } | ||
331 | /* | ||
332 | * Convert a jx9_value to type boolean.Invalidate any prior representations. | ||
333 | */ | ||
334 | JX9_PRIVATE sxi32 jx9MemObjToBool(jx9_value *pObj) | ||
335 | { | ||
336 | if( (pObj->iFlags & MEMOBJ_BOOL) == 0 ){ | ||
337 | /* Preform the conversion */ | ||
338 | pObj->x.iVal = MemObjBooleanValue(&(*pObj)); | ||
339 | /* Invalidate any prior representations */ | ||
340 | SyBlobRelease(&pObj->sBlob); | ||
341 | MemObjSetType(pObj, MEMOBJ_BOOL); | ||
342 | } | ||
343 | return SXRET_OK; | ||
344 | } | ||
345 | /* | ||
346 | * Convert a jx9_value to type string.Prior representations are NOT invalidated. | ||
347 | */ | ||
348 | JX9_PRIVATE sxi32 jx9MemObjToString(jx9_value *pObj) | ||
349 | { | ||
350 | sxi32 rc = SXRET_OK; | ||
351 | if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){ | ||
352 | /* Perform the conversion */ | ||
353 | SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */ | ||
354 | rc = MemObjStringValue(&pObj->sBlob, &(*pObj)); | ||
355 | MemObjSetType(pObj, MEMOBJ_STRING); | ||
356 | } | ||
357 | return rc; | ||
358 | } | ||
359 | /* | ||
360 | * Nullify a jx9_value.In other words invalidate any prior | ||
361 | * representation. | ||
362 | */ | ||
363 | JX9_PRIVATE sxi32 jx9MemObjToNull(jx9_value *pObj) | ||
364 | { | ||
365 | return jx9MemObjRelease(pObj); | ||
366 | } | ||
367 | /* | ||
368 | * Convert a jx9_value to type array.Invalidate any prior representations. | ||
369 | * According to the JX9 language reference manual. | ||
370 | * For any of the types: integer, float, string, boolean converting a value | ||
371 | * to an array results in an array with a single element with index zero | ||
372 | * and the value of the scalar which was converted. | ||
373 | */ | ||
374 | JX9_PRIVATE sxi32 jx9MemObjToHashmap(jx9_value *pObj) | ||
375 | { | ||
376 | if( (pObj->iFlags & MEMOBJ_HASHMAP) == 0 ){ | ||
377 | jx9_hashmap *pMap; | ||
378 | /* Allocate a new hashmap instance */ | ||
379 | pMap = jx9NewHashmap(pObj->pVm, 0, 0); | ||
380 | if( pMap == 0 ){ | ||
381 | return SXERR_MEM; | ||
382 | } | ||
383 | if( (pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_RES)) == 0 ){ | ||
384 | /* | ||
385 | * According to the JX9 language reference manual. | ||
386 | * For any of the types: integer, float, string, boolean converting a value | ||
387 | * to an array results in an array with a single element with index zero | ||
388 | * and the value of the scalar which was converted. | ||
389 | */ | ||
390 | /* Insert a single element */ | ||
391 | jx9HashmapInsert(pMap, 0/* Automatic index assign */, &(*pObj)); | ||
392 | SyBlobRelease(&pObj->sBlob); | ||
393 | } | ||
394 | /* Invalidate any prior representation */ | ||
395 | MemObjSetType(pObj, MEMOBJ_HASHMAP); | ||
396 | pObj->x.pOther = pMap; | ||
397 | } | ||
398 | return SXRET_OK; | ||
399 | } | ||
400 | /* | ||
401 | * Return a pointer to the appropriate convertion method associated | ||
402 | * with the given type. | ||
403 | * Note on type juggling. | ||
404 | * Accoding to the JX9 language reference manual | ||
405 | * JX9 does not require (or support) explicit type definition in variable | ||
406 | * declaration; a variable's type is determined by the context in which | ||
407 | * the variable is used. That is to say, if a string value is assigned | ||
408 | * to variable $var, $var becomes a string. If an integer value is then | ||
409 | * assigned to $var, it becomes an integer. | ||
410 | */ | ||
411 | JX9_PRIVATE ProcMemObjCast jx9MemObjCastMethod(sxi32 iFlags) | ||
412 | { | ||
413 | if( iFlags & MEMOBJ_STRING ){ | ||
414 | return jx9MemObjToString; | ||
415 | }else if( iFlags & MEMOBJ_INT ){ | ||
416 | return jx9MemObjToInteger; | ||
417 | }else if( iFlags & MEMOBJ_REAL ){ | ||
418 | return jx9MemObjToReal; | ||
419 | }else if( iFlags & MEMOBJ_BOOL ){ | ||
420 | return jx9MemObjToBool; | ||
421 | }else if( iFlags & MEMOBJ_HASHMAP ){ | ||
422 | return jx9MemObjToHashmap; | ||
423 | } | ||
424 | /* NULL cast */ | ||
425 | return jx9MemObjToNull; | ||
426 | } | ||
427 | /* | ||
428 | * Check whether the jx9_value is numeric [i.e: int/float/bool] or looks | ||
429 | * like a numeric number [i.e: if the jx9_value is of type string.]. | ||
430 | * Return TRUE if numeric.FALSE otherwise. | ||
431 | */ | ||
432 | JX9_PRIVATE sxi32 jx9MemObjIsNumeric(jx9_value *pObj) | ||
433 | { | ||
434 | if( pObj->iFlags & ( MEMOBJ_BOOL|MEMOBJ_INT|MEMOBJ_REAL) ){ | ||
435 | return TRUE; | ||
436 | }else if( pObj->iFlags & (MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_RES) ){ | ||
437 | return FALSE; | ||
438 | }else if( pObj->iFlags & MEMOBJ_STRING ){ | ||
439 | SyString sStr; | ||
440 | sxi32 rc; | ||
441 | SyStringInitFromBuf(&sStr, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); | ||
442 | if( sStr.nByte <= 0 ){ | ||
443 | /* Empty string */ | ||
444 | return FALSE; | ||
445 | } | ||
446 | /* Check if the string representation looks like a numeric number */ | ||
447 | rc = SyStrIsNumeric(sStr.zString, sStr.nByte, 0, 0); | ||
448 | return rc == SXRET_OK ? TRUE : FALSE; | ||
449 | } | ||
450 | /* NOT REACHED */ | ||
451 | return FALSE; | ||
452 | } | ||
453 | /* | ||
454 | * Check whether the jx9_value is empty.Return TRUE if empty. | ||
455 | * FALSE otherwise. | ||
456 | * An jx9_value is considered empty if the following are true: | ||
457 | * NULL value. | ||
458 | * Boolean FALSE. | ||
459 | * Integer/Float with a 0 (zero) value. | ||
460 | * An empty string or a stream of 0 (zero) [i.e: "0", "00", "000", ...]. | ||
461 | * An empty array. | ||
462 | * NOTE | ||
463 | * OBJECT VALUE MUST NOT BE MODIFIED. | ||
464 | */ | ||
465 | JX9_PRIVATE sxi32 jx9MemObjIsEmpty(jx9_value *pObj) | ||
466 | { | ||
467 | if( pObj->iFlags & MEMOBJ_NULL ){ | ||
468 | return TRUE; | ||
469 | }else if( pObj->iFlags & MEMOBJ_INT ){ | ||
470 | return pObj->x.iVal == 0 ? TRUE : FALSE; | ||
471 | }else if( pObj->iFlags & MEMOBJ_REAL ){ | ||
472 | return pObj->x.rVal == (jx9_real)0 ? TRUE : FALSE; | ||
473 | }else if( pObj->iFlags & MEMOBJ_BOOL ){ | ||
474 | return !pObj->x.iVal; | ||
475 | }else if( pObj->iFlags & MEMOBJ_STRING ){ | ||
476 | if( SyBlobLength(&pObj->sBlob) <= 0 ){ | ||
477 | return TRUE; | ||
478 | }else{ | ||
479 | const char *zIn, *zEnd; | ||
480 | zIn = (const char *)SyBlobData(&pObj->sBlob); | ||
481 | zEnd = &zIn[SyBlobLength(&pObj->sBlob)]; | ||
482 | while( zIn < zEnd ){ | ||
483 | if( zIn[0] != '0' ){ | ||
484 | break; | ||
485 | } | ||
486 | zIn++; | ||
487 | } | ||
488 | return zIn >= zEnd ? TRUE : FALSE; | ||
489 | } | ||
490 | }else if( pObj->iFlags & MEMOBJ_HASHMAP ){ | ||
491 | jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther; | ||
492 | return pMap->nEntry == 0 ? TRUE : FALSE; | ||
493 | }else if ( pObj->iFlags & (MEMOBJ_RES) ){ | ||
494 | return FALSE; | ||
495 | } | ||
496 | /* Assume empty by default */ | ||
497 | return TRUE; | ||
498 | } | ||
499 | /* | ||
500 | * Convert a jx9_value so that it has types MEMOBJ_REAL or MEMOBJ_INT | ||
501 | * or both. | ||
502 | * Invalidate any prior representations. Every effort is made to force | ||
503 | * the conversion, even if the input is a string that does not look | ||
504 | * completely like a number.Convert as much of the string as we can | ||
505 | * and ignore the rest. | ||
506 | */ | ||
507 | JX9_PRIVATE sxi32 jx9MemObjToNumeric(jx9_value *pObj) | ||
508 | { | ||
509 | if( pObj->iFlags & (MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL) ){ | ||
510 | if( pObj->iFlags & (MEMOBJ_BOOL|MEMOBJ_NULL) ){ | ||
511 | if( pObj->iFlags & MEMOBJ_NULL ){ | ||
512 | pObj->x.iVal = 0; | ||
513 | } | ||
514 | MemObjSetType(pObj, MEMOBJ_INT); | ||
515 | } | ||
516 | /* Already numeric */ | ||
517 | return SXRET_OK; | ||
518 | } | ||
519 | if( pObj->iFlags & MEMOBJ_STRING ){ | ||
520 | sxi32 rc = SXERR_INVALID; | ||
521 | sxu8 bReal = FALSE; | ||
522 | SyString sString; | ||
523 | SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); | ||
524 | /* Check if the given string looks like a numeric number */ | ||
525 | if( sString.nByte > 0 ){ | ||
526 | rc = SyStrIsNumeric(sString.zString, sString.nByte, &bReal, 0); | ||
527 | } | ||
528 | if( bReal ){ | ||
529 | jx9MemObjToReal(&(*pObj)); | ||
530 | }else{ | ||
531 | if( rc != SXRET_OK ){ | ||
532 | /* The input does not look at all like a number, set the value to 0 */ | ||
533 | pObj->x.iVal = 0; | ||
534 | }else{ | ||
535 | /* Convert as much as we can */ | ||
536 | pObj->x.iVal = MemObjStringToInt(&(*pObj)); | ||
537 | } | ||
538 | MemObjSetType(pObj, MEMOBJ_INT); | ||
539 | SyBlobRelease(&pObj->sBlob); | ||
540 | } | ||
541 | }else if(pObj->iFlags & (MEMOBJ_HASHMAP|MEMOBJ_RES)){ | ||
542 | jx9MemObjToInteger(pObj); | ||
543 | }else{ | ||
544 | /* Perform a blind cast */ | ||
545 | jx9MemObjToReal(&(*pObj)); | ||
546 | } | ||
547 | return SXRET_OK; | ||
548 | } | ||
549 | /* | ||
550 | * Try a get an integer representation of the given jx9_value. | ||
551 | * If the jx9_value is not of type real, this function is a no-op. | ||
552 | */ | ||
553 | JX9_PRIVATE sxi32 jx9MemObjTryInteger(jx9_value *pObj) | ||
554 | { | ||
555 | if( pObj->iFlags & MEMOBJ_REAL ){ | ||
556 | /* Work only with reals */ | ||
557 | MemObjTryIntger(&(*pObj)); | ||
558 | } | ||
559 | return SXRET_OK; | ||
560 | } | ||
561 | /* | ||
562 | * Initialize a jx9_value to the null type. | ||
563 | */ | ||
564 | JX9_PRIVATE sxi32 jx9MemObjInit(jx9_vm *pVm, jx9_value *pObj) | ||
565 | { | ||
566 | /* Zero the structure */ | ||
567 | SyZero(pObj, sizeof(jx9_value)); | ||
568 | /* Initialize fields */ | ||
569 | pObj->pVm = pVm; | ||
570 | SyBlobInit(&pObj->sBlob, &pVm->sAllocator); | ||
571 | /* Set the NULL type */ | ||
572 | pObj->iFlags = MEMOBJ_NULL; | ||
573 | return SXRET_OK; | ||
574 | } | ||
575 | /* | ||
576 | * Initialize a jx9_value to the integer type. | ||
577 | */ | ||
578 | JX9_PRIVATE sxi32 jx9MemObjInitFromInt(jx9_vm *pVm, jx9_value *pObj, sxi64 iVal) | ||
579 | { | ||
580 | /* Zero the structure */ | ||
581 | SyZero(pObj, sizeof(jx9_value)); | ||
582 | /* Initialize fields */ | ||
583 | pObj->pVm = pVm; | ||
584 | SyBlobInit(&pObj->sBlob, &pVm->sAllocator); | ||
585 | /* Set the desired type */ | ||
586 | pObj->x.iVal = iVal; | ||
587 | pObj->iFlags = MEMOBJ_INT; | ||
588 | return SXRET_OK; | ||
589 | } | ||
590 | /* | ||
591 | * Initialize a jx9_value to the boolean type. | ||
592 | */ | ||
593 | JX9_PRIVATE sxi32 jx9MemObjInitFromBool(jx9_vm *pVm, jx9_value *pObj, sxi32 iVal) | ||
594 | { | ||
595 | /* Zero the structure */ | ||
596 | SyZero(pObj, sizeof(jx9_value)); | ||
597 | /* Initialize fields */ | ||
598 | pObj->pVm = pVm; | ||
599 | SyBlobInit(&pObj->sBlob, &pVm->sAllocator); | ||
600 | /* Set the desired type */ | ||
601 | pObj->x.iVal = iVal ? 1 : 0; | ||
602 | pObj->iFlags = MEMOBJ_BOOL; | ||
603 | return SXRET_OK; | ||
604 | } | ||
605 | #if 0 | ||
606 | /* | ||
607 | * Initialize a jx9_value to the real type. | ||
608 | */ | ||
609 | JX9_PRIVATE sxi32 jx9MemObjInitFromReal(jx9_vm *pVm, jx9_value *pObj, jx9_real rVal) | ||
610 | { | ||
611 | /* Zero the structure */ | ||
612 | SyZero(pObj, sizeof(jx9_value)); | ||
613 | /* Initialize fields */ | ||
614 | pObj->pVm = pVm; | ||
615 | SyBlobInit(&pObj->sBlob, &pVm->sAllocator); | ||
616 | /* Set the desired type */ | ||
617 | pObj->x.rVal = rVal; | ||
618 | pObj->iFlags = MEMOBJ_REAL; | ||
619 | return SXRET_OK; | ||
620 | } | ||
621 | #endif | ||
622 | /* | ||
623 | * Initialize a jx9_value to the array type. | ||
624 | */ | ||
625 | JX9_PRIVATE sxi32 jx9MemObjInitFromArray(jx9_vm *pVm, jx9_value *pObj, jx9_hashmap *pArray) | ||
626 | { | ||
627 | /* Zero the structure */ | ||
628 | SyZero(pObj, sizeof(jx9_value)); | ||
629 | /* Initialize fields */ | ||
630 | pObj->pVm = pVm; | ||
631 | SyBlobInit(&pObj->sBlob, &pVm->sAllocator); | ||
632 | /* Set the desired type */ | ||
633 | pObj->iFlags = MEMOBJ_HASHMAP; | ||
634 | pObj->x.pOther = pArray; | ||
635 | return SXRET_OK; | ||
636 | } | ||
637 | /* | ||
638 | * Initialize a jx9_value to the string type. | ||
639 | */ | ||
640 | JX9_PRIVATE sxi32 jx9MemObjInitFromString(jx9_vm *pVm, jx9_value *pObj, const SyString *pVal) | ||
641 | { | ||
642 | /* Zero the structure */ | ||
643 | SyZero(pObj, sizeof(jx9_value)); | ||
644 | /* Initialize fields */ | ||
645 | pObj->pVm = pVm; | ||
646 | SyBlobInit(&pObj->sBlob, &pVm->sAllocator); | ||
647 | if( pVal ){ | ||
648 | /* Append contents */ | ||
649 | SyBlobAppend(&pObj->sBlob, (const void *)pVal->zString, pVal->nByte); | ||
650 | } | ||
651 | /* Set the desired type */ | ||
652 | pObj->iFlags = MEMOBJ_STRING; | ||
653 | return SXRET_OK; | ||
654 | } | ||
655 | /* | ||
656 | * Append some contents to the internal buffer of a given jx9_value. | ||
657 | * If the given jx9_value is not of type string, this function | ||
658 | * invalidate any prior representation and set the string type. | ||
659 | * Then a simple append operation is performed. | ||
660 | */ | ||
661 | JX9_PRIVATE sxi32 jx9MemObjStringAppend(jx9_value *pObj, const char *zData, sxu32 nLen) | ||
662 | { | ||
663 | sxi32 rc; | ||
664 | if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){ | ||
665 | /* Invalidate any prior representation */ | ||
666 | jx9MemObjRelease(pObj); | ||
667 | MemObjSetType(pObj, MEMOBJ_STRING); | ||
668 | } | ||
669 | /* Append contents */ | ||
670 | rc = SyBlobAppend(&pObj->sBlob, zData, nLen); | ||
671 | return rc; | ||
672 | } | ||
673 | #if 0 | ||
674 | /* | ||
675 | * Format and append some contents to the internal buffer of a given jx9_value. | ||
676 | * If the given jx9_value is not of type string, this function invalidate | ||
677 | * any prior representation and set the string type. | ||
678 | * Then a simple format and append operation is performed. | ||
679 | */ | ||
680 | JX9_PRIVATE sxi32 jx9MemObjStringFormat(jx9_value *pObj, const char *zFormat, va_list ap) | ||
681 | { | ||
682 | sxi32 rc; | ||
683 | if( (pObj->iFlags & MEMOBJ_STRING) == 0 ){ | ||
684 | /* Invalidate any prior representation */ | ||
685 | jx9MemObjRelease(pObj); | ||
686 | MemObjSetType(pObj, MEMOBJ_STRING); | ||
687 | } | ||
688 | /* Format and append contents */ | ||
689 | rc = SyBlobFormatAp(&pObj->sBlob, zFormat, ap); | ||
690 | return rc; | ||
691 | } | ||
692 | #endif | ||
693 | /* | ||
694 | * Duplicate the contents of a jx9_value. | ||
695 | */ | ||
696 | JX9_PRIVATE sxi32 jx9MemObjStore(jx9_value *pSrc, jx9_value *pDest) | ||
697 | { | ||
698 | jx9_hashmap *pMap = 0; | ||
699 | sxi32 rc; | ||
700 | if( pSrc->iFlags & MEMOBJ_HASHMAP ){ | ||
701 | /* Increment reference count */ | ||
702 | ((jx9_hashmap *)pSrc->x.pOther)->iRef++; | ||
703 | } | ||
704 | if( pDest->iFlags & MEMOBJ_HASHMAP ){ | ||
705 | pMap = (jx9_hashmap *)pDest->x.pOther; | ||
706 | } | ||
707 | SyMemcpy((const void *)&(*pSrc), &(*pDest), sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32))); | ||
708 | rc = SXRET_OK; | ||
709 | if( SyBlobLength(&pSrc->sBlob) > 0 ){ | ||
710 | SyBlobReset(&pDest->sBlob); | ||
711 | rc = SyBlobDup(&pSrc->sBlob, &pDest->sBlob); | ||
712 | }else{ | ||
713 | if( SyBlobLength(&pDest->sBlob) > 0 ){ | ||
714 | SyBlobRelease(&pDest->sBlob); | ||
715 | } | ||
716 | } | ||
717 | if( pMap ){ | ||
718 | jx9HashmapUnref(pMap); | ||
719 | } | ||
720 | return rc; | ||
721 | } | ||
722 | /* | ||
723 | * Duplicate the contents of a jx9_value but do not copy internal | ||
724 | * buffer contents, simply point to it. | ||
725 | */ | ||
726 | JX9_PRIVATE sxi32 jx9MemObjLoad(jx9_value *pSrc, jx9_value *pDest) | ||
727 | { | ||
728 | SyMemcpy((const void *)&(*pSrc), &(*pDest), | ||
729 | sizeof(jx9_value)-(sizeof(jx9_vm *)+sizeof(SyBlob)+sizeof(sxu32))); | ||
730 | if( pSrc->iFlags & MEMOBJ_HASHMAP ){ | ||
731 | /* Increment reference count */ | ||
732 | ((jx9_hashmap *)pSrc->x.pOther)->iRef++; | ||
733 | } | ||
734 | if( SyBlobLength(&pDest->sBlob) > 0 ){ | ||
735 | SyBlobRelease(&pDest->sBlob); | ||
736 | } | ||
737 | if( SyBlobLength(&pSrc->sBlob) > 0 ){ | ||
738 | SyBlobReadOnly(&pDest->sBlob, SyBlobData(&pSrc->sBlob), SyBlobLength(&pSrc->sBlob)); | ||
739 | } | ||
740 | return SXRET_OK; | ||
741 | } | ||
742 | /* | ||
743 | * Invalidate any prior representation of a given jx9_value. | ||
744 | */ | ||
745 | JX9_PRIVATE sxi32 jx9MemObjRelease(jx9_value *pObj) | ||
746 | { | ||
747 | if( (pObj->iFlags & MEMOBJ_NULL) == 0 ){ | ||
748 | if( pObj->iFlags & MEMOBJ_HASHMAP ){ | ||
749 | jx9HashmapUnref((jx9_hashmap *)pObj->x.pOther); | ||
750 | } | ||
751 | /* Release the internal buffer */ | ||
752 | SyBlobRelease(&pObj->sBlob); | ||
753 | /* Invalidate any prior representation */ | ||
754 | pObj->iFlags = MEMOBJ_NULL; | ||
755 | } | ||
756 | return SXRET_OK; | ||
757 | } | ||
758 | /* | ||
759 | * Compare two jx9_values. | ||
760 | * Return 0 if the values are equals, > 0 if pObj1 is greater than pObj2 | ||
761 | * or < 0 if pObj2 is greater than pObj1. | ||
762 | * Type comparison table taken from the JX9 language reference manual. | ||
763 | * Comparisons of $x with JX9 functions Expression | ||
764 | * gettype() empty() is_null() isset() boolean : if($x) | ||
765 | * $x = ""; string TRUE FALSE TRUE FALSE | ||
766 | * $x = null NULL TRUE TRUE FALSE FALSE | ||
767 | * var $x; NULL TRUE TRUE FALSE FALSE | ||
768 | * $x is undefined NULL TRUE TRUE FALSE FALSE | ||
769 | * $x = array(); array TRUE FALSE TRUE FALSE | ||
770 | * $x = false; boolean TRUE FALSE TRUE FALSE | ||
771 | * $x = true; boolean FALSE FALSE TRUE TRUE | ||
772 | * $x = 1; integer FALSE FALSE TRUE TRUE | ||
773 | * $x = 42; integer FALSE FALSE TRUE TRUE | ||
774 | * $x = 0; integer TRUE FALSE TRUE FALSE | ||
775 | * $x = -1; integer FALSE FALSE TRUE TRUE | ||
776 | * $x = "1"; string FALSE FALSE TRUE TRUE | ||
777 | * $x = "0"; string TRUE FALSE TRUE FALSE | ||
778 | * $x = "-1"; string FALSE FALSE TRUE TRUE | ||
779 | * $x = "jx9"; string FALSE FALSE TRUE TRUE | ||
780 | * $x = "true"; string FALSE FALSE TRUE TRUE | ||
781 | * $x = "false"; string FALSE FALSE TRUE TRUE | ||
782 | * Loose comparisons with == | ||
783 | * TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "jx9" "" | ||
784 | * TRUE TRUE FALSE TRUE FALSE TRUE TRUE FALSE TRUE FALSE FALSE TRUE FALSE | ||
785 | * FALSE FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE TRUE FALSE TRUE | ||
786 | * 1 TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE | ||
787 | * 0 FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE FALSE TRUE TRUE | ||
788 | * -1 TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE | ||
789 | * "1" TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE | ||
790 | * "0" FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE | ||
791 | * "-1" TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE | ||
792 | * NULL FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE FALSE TRUE | ||
793 | * array() FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE | ||
794 | * "jx9" TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE | ||
795 | * "" FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE | ||
796 | * Strict comparisons with === | ||
797 | * TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "jx9" "" | ||
798 | * TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE | ||
799 | * FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE | ||
800 | * 1 FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE | ||
801 | * 0 FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE | ||
802 | * -1 FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE | ||
803 | * "1" FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE | ||
804 | * "0" FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE | ||
805 | * "-1" FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE | ||
806 | * NULL FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE | ||
807 | * array() FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE | ||
808 | * "jx9" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE | ||
809 | * "" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE | ||
810 | */ | ||
811 | JX9_PRIVATE sxi32 jx9MemObjCmp(jx9_value *pObj1, jx9_value *pObj2, int bStrict, int iNest) | ||
812 | { | ||
813 | sxi32 iComb; | ||
814 | sxi32 rc; | ||
815 | if( bStrict ){ | ||
816 | sxi32 iF1, iF2; | ||
817 | /* Strict comparisons with === */ | ||
818 | iF1 = pObj1->iFlags; | ||
819 | iF2 = pObj2->iFlags; | ||
820 | if( iF1 != iF2 ){ | ||
821 | /* Not of the same type */ | ||
822 | return 1; | ||
823 | } | ||
824 | } | ||
825 | /* Combine flag together */ | ||
826 | iComb = pObj1->iFlags|pObj2->iFlags; | ||
827 | if( iComb & (MEMOBJ_NULL|MEMOBJ_RES|MEMOBJ_BOOL) ){ | ||
828 | /* Convert to boolean: Keep in mind FALSE < TRUE */ | ||
829 | if( (pObj1->iFlags & MEMOBJ_BOOL) == 0 ){ | ||
830 | jx9MemObjToBool(pObj1); | ||
831 | } | ||
832 | if( (pObj2->iFlags & MEMOBJ_BOOL) == 0 ){ | ||
833 | jx9MemObjToBool(pObj2); | ||
834 | } | ||
835 | return (sxi32)((pObj1->x.iVal != 0) - (pObj2->x.iVal != 0)); | ||
836 | }else if ( iComb & MEMOBJ_HASHMAP ){ | ||
837 | /* Hashmap aka 'array' comparison */ | ||
838 | if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){ | ||
839 | /* Array is always greater */ | ||
840 | return -1; | ||
841 | } | ||
842 | if( (pObj2->iFlags & MEMOBJ_HASHMAP) == 0 ){ | ||
843 | /* Array is always greater */ | ||
844 | return 1; | ||
845 | } | ||
846 | /* Perform the comparison */ | ||
847 | rc = jx9HashmapCmp((jx9_hashmap *)pObj1->x.pOther, (jx9_hashmap *)pObj2->x.pOther, bStrict); | ||
848 | return rc; | ||
849 | }else if ( iComb & MEMOBJ_STRING ){ | ||
850 | SyString s1, s2; | ||
851 | /* Perform a strict string comparison.*/ | ||
852 | if( (pObj1->iFlags&MEMOBJ_STRING) == 0 ){ | ||
853 | jx9MemObjToString(pObj1); | ||
854 | } | ||
855 | if( (pObj2->iFlags&MEMOBJ_STRING) == 0 ){ | ||
856 | jx9MemObjToString(pObj2); | ||
857 | } | ||
858 | SyStringInitFromBuf(&s1, SyBlobData(&pObj1->sBlob), SyBlobLength(&pObj1->sBlob)); | ||
859 | SyStringInitFromBuf(&s2, SyBlobData(&pObj2->sBlob), SyBlobLength(&pObj2->sBlob)); | ||
860 | /* | ||
861 | * Strings are compared using memcmp(). If one value is an exact prefix of the | ||
862 | * other, then the shorter value is less than the longer value. | ||
863 | */ | ||
864 | rc = SyMemcmp((const void *)s1.zString, (const void *)s2.zString, SXMIN(s1.nByte, s2.nByte)); | ||
865 | if( rc == 0 ){ | ||
866 | if( s1.nByte != s2.nByte ){ | ||
867 | rc = s1.nByte < s2.nByte ? -1 : 1; | ||
868 | } | ||
869 | } | ||
870 | return rc; | ||
871 | }else if( iComb & (MEMOBJ_INT|MEMOBJ_REAL) ){ | ||
872 | /* Perform a numeric comparison if one of the operand is numeric(integer or real) */ | ||
873 | if( (pObj1->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){ | ||
874 | jx9MemObjToNumeric(pObj1); | ||
875 | } | ||
876 | if( (pObj2->iFlags & (MEMOBJ_INT|MEMOBJ_REAL)) == 0 ){ | ||
877 | jx9MemObjToNumeric(pObj2); | ||
878 | } | ||
879 | if( (pObj1->iFlags & pObj2->iFlags & MEMOBJ_INT) == 0) { | ||
880 | jx9_real r1, r2; | ||
881 | /* Compare as reals */ | ||
882 | if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){ | ||
883 | jx9MemObjToReal(pObj1); | ||
884 | } | ||
885 | r1 = pObj1->x.rVal; | ||
886 | if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){ | ||
887 | jx9MemObjToReal(pObj2); | ||
888 | } | ||
889 | r2 = pObj2->x.rVal; | ||
890 | if( r1 > r2 ){ | ||
891 | return 1; | ||
892 | }else if( r1 < r2 ){ | ||
893 | return -1; | ||
894 | } | ||
895 | return 0; | ||
896 | }else{ | ||
897 | /* Integer comparison */ | ||
898 | if( pObj1->x.iVal > pObj2->x.iVal ){ | ||
899 | return 1; | ||
900 | }else if( pObj1->x.iVal < pObj2->x.iVal ){ | ||
901 | return -1; | ||
902 | } | ||
903 | return 0; | ||
904 | } | ||
905 | } | ||
906 | /* NOT REACHED */ | ||
907 | SXUNUSED(iNest); | ||
908 | return 0; | ||
909 | } | ||
910 | /* | ||
911 | * Perform an addition operation of two jx9_values. | ||
912 | * The reason this function is implemented here rather than 'vm.c' | ||
913 | * is that the '+' operator is overloaded. | ||
914 | * That is, the '+' operator is used for arithmetic operation and also | ||
915 | * used for operation on arrays [i.e: union]. When used with an array | ||
916 | * The + operator returns the right-hand array appended to the left-hand array. | ||
917 | * For keys that exist in both arrays, the elements from the left-hand array | ||
918 | * will be used, and the matching elements from the right-hand array will | ||
919 | * be ignored. | ||
920 | * This function take care of handling all the scenarios. | ||
921 | */ | ||
922 | JX9_PRIVATE sxi32 jx9MemObjAdd(jx9_value *pObj1, jx9_value *pObj2, int bAddStore) | ||
923 | { | ||
924 | if( ((pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP) == 0 ){ | ||
925 | /* Arithemtic operation */ | ||
926 | jx9MemObjToNumeric(pObj1); | ||
927 | jx9MemObjToNumeric(pObj2); | ||
928 | if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_REAL ){ | ||
929 | /* Floating point arithmetic */ | ||
930 | jx9_real a, b; | ||
931 | if( (pObj1->iFlags & MEMOBJ_REAL) == 0 ){ | ||
932 | jx9MemObjToReal(pObj1); | ||
933 | } | ||
934 | if( (pObj2->iFlags & MEMOBJ_REAL) == 0 ){ | ||
935 | jx9MemObjToReal(pObj2); | ||
936 | } | ||
937 | a = pObj1->x.rVal; | ||
938 | b = pObj2->x.rVal; | ||
939 | pObj1->x.rVal = a+b; | ||
940 | MemObjSetType(pObj1, MEMOBJ_REAL); | ||
941 | /* Try to get an integer representation also */ | ||
942 | MemObjTryIntger(&(*pObj1)); | ||
943 | }else{ | ||
944 | /* Integer arithmetic */ | ||
945 | sxi64 a, b; | ||
946 | a = pObj1->x.iVal; | ||
947 | b = pObj2->x.iVal; | ||
948 | pObj1->x.iVal = a+b; | ||
949 | MemObjSetType(pObj1, MEMOBJ_INT); | ||
950 | } | ||
951 | }else{ | ||
952 | if( (pObj1->iFlags|pObj2->iFlags) & MEMOBJ_HASHMAP ){ | ||
953 | jx9_hashmap *pMap; | ||
954 | sxi32 rc; | ||
955 | if( bAddStore ){ | ||
956 | /* Do not duplicate the hashmap, use the left one since its an add&store operation. | ||
957 | */ | ||
958 | if( (pObj1->iFlags & MEMOBJ_HASHMAP) == 0 ){ | ||
959 | /* Force a hashmap cast */ | ||
960 | rc = jx9MemObjToHashmap(pObj1); | ||
961 | if( rc != SXRET_OK ){ | ||
962 | jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array"); | ||
963 | return rc; | ||
964 | } | ||
965 | } | ||
966 | /* Point to the structure that describe the hashmap */ | ||
967 | pMap = (jx9_hashmap *)pObj1->x.pOther; | ||
968 | }else{ | ||
969 | /* Create a new hashmap */ | ||
970 | pMap = jx9NewHashmap(pObj1->pVm, 0, 0); | ||
971 | if( pMap == 0){ | ||
972 | jx9VmThrowError(pObj1->pVm, 0, JX9_CTX_ERR, "JX9 is running out of memory while creating array"); | ||
973 | return SXERR_MEM; | ||
974 | } | ||
975 | } | ||
976 | if( !bAddStore ){ | ||
977 | if(pObj1->iFlags & MEMOBJ_HASHMAP ){ | ||
978 | /* Perform a hashmap duplication */ | ||
979 | jx9HashmapDup((jx9_hashmap *)pObj1->x.pOther, pMap); | ||
980 | }else{ | ||
981 | if((pObj1->iFlags & MEMOBJ_NULL) == 0 ){ | ||
982 | /* Simple insertion */ | ||
983 | jx9HashmapInsert(pMap, 0, pObj1); | ||
984 | } | ||
985 | } | ||
986 | } | ||
987 | /* Perform the union */ | ||
988 | if(pObj2->iFlags & MEMOBJ_HASHMAP ){ | ||
989 | jx9HashmapUnion(pMap, (jx9_hashmap *)pObj2->x.pOther); | ||
990 | }else{ | ||
991 | if((pObj2->iFlags & MEMOBJ_NULL) == 0 ){ | ||
992 | /* Simple insertion */ | ||
993 | jx9HashmapInsert(pMap, 0, pObj2); | ||
994 | } | ||
995 | } | ||
996 | /* Reflect the change */ | ||
997 | if( pObj1->iFlags & MEMOBJ_STRING ){ | ||
998 | SyBlobRelease(&pObj1->sBlob); | ||
999 | } | ||
1000 | pObj1->x.pOther = pMap; | ||
1001 | MemObjSetType(pObj1, MEMOBJ_HASHMAP); | ||
1002 | } | ||
1003 | } | ||
1004 | return SXRET_OK; | ||
1005 | } | ||
1006 | /* | ||
1007 | * Return a printable representation of the type of a given | ||
1008 | * jx9_value. | ||
1009 | */ | ||
1010 | JX9_PRIVATE const char * jx9MemObjTypeDump(jx9_value *pVal) | ||
1011 | { | ||
1012 | const char *zType = ""; | ||
1013 | if( pVal->iFlags & MEMOBJ_NULL ){ | ||
1014 | zType = "null"; | ||
1015 | }else if( pVal->iFlags & MEMOBJ_INT ){ | ||
1016 | zType = "int"; | ||
1017 | }else if( pVal->iFlags & MEMOBJ_REAL ){ | ||
1018 | zType = "float"; | ||
1019 | }else if( pVal->iFlags & MEMOBJ_STRING ){ | ||
1020 | zType = "string"; | ||
1021 | }else if( pVal->iFlags & MEMOBJ_BOOL ){ | ||
1022 | zType = "bool"; | ||
1023 | }else if( pVal->iFlags & MEMOBJ_HASHMAP ){ | ||
1024 | jx9_hashmap *pMap = (jx9_hashmap *)pVal->x.pOther; | ||
1025 | if( pMap->iFlags & HASHMAP_JSON_OBJECT ){ | ||
1026 | zType = "JSON Object"; | ||
1027 | }else{ | ||
1028 | zType = "JSON Array"; | ||
1029 | } | ||
1030 | }else if( pVal->iFlags & MEMOBJ_RES ){ | ||
1031 | zType = "resource"; | ||
1032 | } | ||
1033 | return zType; | ||
1034 | } | ||
1035 | /* | ||
1036 | * Dump a jx9_value [i.e: get a printable representation of it's type and contents.]. | ||
1037 | * Store the dump in the given blob. | ||
1038 | */ | ||
1039 | JX9_PRIVATE sxi32 jx9MemObjDump( | ||
1040 | SyBlob *pOut, /* Store the dump here */ | ||
1041 | jx9_value *pObj /* Dump this */ | ||
1042 | ) | ||
1043 | { | ||
1044 | sxi32 rc = SXRET_OK; | ||
1045 | const char *zType; | ||
1046 | /* Get value type first */ | ||
1047 | zType = jx9MemObjTypeDump(pObj); | ||
1048 | SyBlobAppend(&(*pOut), zType, SyStrlen(zType)); | ||
1049 | if((pObj->iFlags & MEMOBJ_NULL) == 0 ){ | ||
1050 | SyBlobAppend(&(*pOut), "(", sizeof(char)); | ||
1051 | if( pObj->iFlags & MEMOBJ_HASHMAP ){ | ||
1052 | jx9_hashmap *pMap = (jx9_hashmap *)pObj->x.pOther; | ||
1053 | SyBlobFormat(pOut,"%u ",pMap->nEntry); | ||
1054 | /* Dump hashmap entries */ | ||
1055 | rc = jx9JsonSerialize(pObj,pOut); | ||
1056 | }else{ | ||
1057 | SyBlob *pContents = &pObj->sBlob; | ||
1058 | /* Get a printable representation of the contents */ | ||
1059 | if((pObj->iFlags & MEMOBJ_STRING) == 0 ){ | ||
1060 | MemObjStringValue(&(*pOut), &(*pObj)); | ||
1061 | }else{ | ||
1062 | /* Append length first */ | ||
1063 | SyBlobFormat(&(*pOut), "%u '", SyBlobLength(&pObj->sBlob)); | ||
1064 | if( SyBlobLength(pContents) > 0 ){ | ||
1065 | SyBlobAppend(&(*pOut), SyBlobData(pContents), SyBlobLength(pContents)); | ||
1066 | } | ||
1067 | SyBlobAppend(&(*pOut), "'", sizeof(char)); | ||
1068 | } | ||
1069 | } | ||
1070 | SyBlobAppend(&(*pOut), ")", sizeof(char)); | ||
1071 | } | ||
1072 | #ifdef __WINNT__ | ||
1073 | SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n")-1); | ||
1074 | #else | ||
1075 | SyBlobAppend(&(*pOut), "\n", sizeof(char)); | ||
1076 | #endif | ||
1077 | return rc; | ||
1078 | } | ||