summaryrefslogtreecommitdiffstats
path: root/common/unqlite/jx9_memobj.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/unqlite/jx9_memobj.c')
-rw-r--r--common/unqlite/jx9_memobj.c1078
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 */
34static 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 */
69JX9_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 */
102static 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 */
117static 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 */
151static 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 */
198static 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 */
232static 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 */
281static 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 */
305JX9_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 */
320JX9_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 */
334JX9_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 */
348JX9_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 */
363JX9_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 */
374JX9_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 */
411JX9_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 */
432JX9_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 */
465JX9_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 */
507JX9_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 */
553JX9_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 */
564JX9_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 */
578JX9_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 */
593JX9_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 */
609JX9_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 */
625JX9_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 */
640JX9_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 */
661JX9_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 */
680JX9_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 */
696JX9_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 */
726JX9_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 */
745JX9_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 */
811JX9_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 */
922JX9_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 */
1010JX9_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 */
1039JX9_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}