JsVar *jswrap_typedarray_constructor(JsVarDataArrayBufferViewType type, JsVar *arr, JsVarInt byteOffset, JsVarInt length) { JsVar *arrayBuffer = 0; // Only allow use of byteOffset/length if we're passing an ArrayBuffer - NOT A VIEW. bool copyData = false; if (jsvIsArrayBuffer(arr) && arr->varData.arraybuffer.type==ARRAYBUFFERVIEW_ARRAYBUFFER) { arrayBuffer = jsvLockAgain(arr); } else if (jsvIsNumeric(arr)) { length = jsvGetInteger(arr); byteOffset = 0; arrayBuffer = jswrap_arraybuffer_constructor((int)JSV_ARRAYBUFFER_GET_SIZE(type)*length); } else if (jsvIsArray(arr) || jsvIsArrayBuffer(arr)) { length = (JsVarInt)jsvGetLength(arr); byteOffset = 0; arrayBuffer = jswrap_arraybuffer_constructor((int)JSV_ARRAYBUFFER_GET_SIZE(type)*length); copyData = true; // so later on we'll populate this } if (!arrayBuffer) { jsExceptionHere(JSET_ERROR, "Unsupported first argument of type %t\n", arr); return 0; } if (length==0) { length = ((JsVarInt)jsvGetArrayBufferLength(arrayBuffer)-byteOffset) / (JsVarInt)JSV_ARRAYBUFFER_GET_SIZE(type); if (length<0) length=0; } JsVar *typedArr = jsvNewWithFlags(JSV_ARRAYBUFFER); if (typedArr) { typedArr->varData.arraybuffer.type = type; typedArr->varData.arraybuffer.byteOffset = (unsigned short)byteOffset; typedArr->varData.arraybuffer.length = (unsigned short)length; jsvSetFirstChild(typedArr, jsvGetRef(jsvRef(arrayBuffer))); if (copyData) { // if we were given an array, populate this ArrayBuffer JsvIterator it; jsvIteratorNew(&it, arr, JSIF_DEFINED_ARRAY_ElEMENTS); while (jsvIteratorHasElement(&it)) { JsVar *idx = jsvIteratorGetKey(&it); if (jsvIsInt(idx)) { JsVar *val = jsvIteratorGetValue(&it); // TODO: This is horrible! We need to try and iterate properly... jsvArrayBufferSet(typedArr, (size_t)jsvGetInteger(idx), val); jsvUnLock(val); } jsvUnLock(idx); jsvIteratorNext(&it); } jsvIteratorFree(&it); } } jsvUnLock(arrayBuffer); return typedArr; }
/*JSON{ "type" : "method", "class" : "ArrayBufferView", "name" : "set", "generate" : "jswrap_arraybufferview_set", "params" : [ ["arr","JsVar","Floating point index to access"], ["offset","int32","The offset in this array at which to write the values (optional)"] ] } Copy the contents of `array` into this one, mapping `this[x+offset]=array[x];` */ void jswrap_arraybufferview_set(JsVar *parent, JsVar *arr, int offset) { if (!(jsvIsString(arr) || jsvIsArray(arr) || jsvIsArrayBuffer(arr))) { jsExceptionHere(JSET_ERROR, "Expecting first argument to be an array, not %t", arr); return; } JsvIterator itsrc; jsvIteratorNew(&itsrc, arr); JsvArrayBufferIterator itdst; jsvArrayBufferIteratorNew(&itdst, parent, (size_t)offset); bool useInts = !JSV_ARRAYBUFFER_IS_FLOAT(itdst.type) || jsvIsString(arr); while (jsvIteratorHasElement(&itsrc) && jsvArrayBufferIteratorHasElement(&itdst)) { if (useInts) { jsvArrayBufferIteratorSetIntegerValue(&itdst, jsvIteratorGetIntegerValue(&itsrc)); } else { JsVar *value = jsvIteratorGetValue(&itsrc); jsvArrayBufferIteratorSetValue(&itdst, value); jsvUnLock(value); } jsvArrayBufferIteratorNext(&itdst); jsvIteratorNext(&itsrc); } jsvArrayBufferIteratorFree(&itdst); jsvIteratorFree(&itsrc); }
static JsVar *jswrap_waveform_getBuffer(JsVar *waveform, int bufferNumber, bool *is16Bit) { JsVar *buffer = jsvObjectGetChild(waveform, (bufferNumber==0)?"buffer":"buffer2", 0); if (is16Bit) { *is16Bit = false; if (jsvIsArrayBuffer(buffer) && JSV_ARRAYBUFFER_GET_SIZE(buffer->varData.arraybuffer.type)==2) *is16Bit = true; } // plough through to get array buffer data while (jsvIsArrayBuffer(buffer)) { JsVar *s = jsvLock(buffer->firstChild); jsvUnLock(buffer); buffer = s; } assert(jsvIsUndefined(buffer) || jsvIsString(buffer)); return buffer; }
/*JSON{ "type":"property", "class": "Object", "name" : "length", "description" : "Find the length of the object", "generate" : "jswrap_object_length", "return" : ["JsVar", "The value of the string"] }*/ JsVar *jswrap_object_length(JsVar *parent) { if (jsvIsArray(parent)) { return jsvNewFromInteger(jsvGetArrayLength(parent)); } else if (jsvIsArrayBuffer(parent)) { return jsvNewFromInteger((JsVarInt)jsvGetArrayBufferLength(parent)); } else if (jsvIsString(parent)) { return jsvNewFromInteger((JsVarInt)jsvGetStringLength(parent)); } return 0; }
void jsvIteratorNew(JsvIterator *it, JsVar *obj) { if (jsvIsArray(obj) || jsvIsObject(obj) || jsvIsFunction(obj)) { it->type = JSVI_OBJECT; jsvObjectIteratorNew(&it->it.obj, obj); } else if (jsvIsArrayBuffer(obj)) { it->type = JSVI_ARRAYBUFFER; jsvArrayBufferIteratorNew(&it->it.buf, obj, 0); } else if (jsvHasCharacterData(obj)) { it->type = JSVI_STRING; jsvStringIteratorNew(&it->it.str, obj, 0); } else assert(0); }
/*JSON{ "type" : "method", "class" : "ArrayBufferView", "name" : "map", "generate" : "jswrap_arraybufferview_map", "params" : [ ["function","JsVar","Function used to map one item to another"], ["thisArg","JsVar","if specified, the function is called with 'this' set to thisArg (optional)"] ], "return" : ["JsVar","An array containing the results"], "return_object" : "ArrayBufferView" } Return an array which is made from the following: ```A.map(function) = [function(A[0]), function(A[1]), ...]``` **Note:** This returns an ArrayBuffer of the same type it was called on. To get an Array, use `Array.prototype.map` */ JsVar *jswrap_arraybufferview_map(JsVar *parent, JsVar *funcVar, JsVar *thisVar) { if (!jsvIsArrayBuffer(parent)) { jsExceptionHere(JSET_ERROR, "ArrayBufferView.map can only be called on an ArrayBufferView"); return 0; } if (!jsvIsFunction(funcVar)) { jsExceptionHere(JSET_ERROR, "ArrayBufferView.map's first argument should be a function"); return 0; } if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) { jsExceptionHere(JSET_ERROR, "ArrayBufferView.map's second argument should be undefined, or an object"); return 0; } // create ArrayBuffer result JsVarDataArrayBufferViewType arrayBufferType = parent->varData.arraybuffer.type; JsVar *arrayBufferLength = jsvNewFromInteger((JsVarInt)jsvGetArrayBufferLength(parent)); JsVar *array = jswrap_typedarray_constructor(arrayBufferType, arrayBufferLength, 0, 0); jsvUnLock(arrayBufferLength); if (!array) return 0; // now iterate JsvIterator it; // TODO: if we really are limited to ArrayBuffers, this could be an ArrayBufferIterator. jsvIteratorNew(&it, parent); JsvArrayBufferIterator itdst; jsvArrayBufferIteratorNew(&itdst, array, 0); while (jsvIteratorHasElement(&it)) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { JsVarInt idxValue = jsvGetInteger(index); JsVar *args[3], *mapped; args[0] = jsvIteratorGetValue(&it); args[1] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index args[2] = parent; mapped = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args); jsvUnLock(args[0]); jsvUnLock(args[1]); if (mapped) { jsvArrayBufferIteratorSetValue(&itdst, mapped); jsvUnLock(mapped); } } jsvUnLock(index); jsvIteratorNext(&it); jsvArrayBufferIteratorNext(&itdst); } jsvIteratorFree(&it); jsvArrayBufferIteratorFree(&itdst); return array; }
static JsVar *jswrap_waveform_getBuffer(JsVar *waveform, int bufferNumber, bool *is16Bit) { JsVar *buffer = jsvObjectGetChild(waveform, (bufferNumber==0)?"buffer":"buffer2", 0); if (!buffer) return 0; if (is16Bit) { *is16Bit = false; if (jsvIsArrayBuffer(buffer) && JSV_ARRAYBUFFER_GET_SIZE(buffer->varData.arraybuffer.type)==2) *is16Bit = true; } // plough through to get array buffer data JsVar *backingString = jsvGetArrayBufferBackingString(buffer); jsvUnLock(buffer); return backingString; }
/*JSON{ "type" : "property", "class" : "Object", "name" : "length", "generate" : "jswrap_object_length", "return" : ["JsVar","The length of the object"] } Find the length of the object */ JsVar *jswrap_object_length(JsVar *parent) { JsVarInt l; if (jsvIsArray(parent)) { l = jsvGetArrayLength(parent); } else if (jsvIsArrayBuffer(parent)) { l = (JsVarInt)jsvGetArrayBufferLength(parent); } else if (jsvIsString(parent)) { l = (JsVarInt)jsvGetStringLength(parent); } else if (jsvIsFunction(parent)) { JsVar *args = jsvGetFunctionArgumentLength(parent); l = jsvGetArrayLength(args); jsvUnLock(args); } else return 0; return jsvNewFromInteger(l); }
/*JSON{ "type" : "staticmethod", "ifndef" : "SAVE_ON_FLASH", "class" : "E", "name" : "sum", "generate" : "jswrap_espruino_sum", "params" : [ ["arr","JsVar","The array to sum"] ], "return" : ["float","The sum of the given buffer"] } Sum the contents of the given Array, String or ArrayBuffer and return the result */ JsVarFloat jswrap_espruino_sum(JsVar *arr) { if (!(jsvIsString(arr) || jsvIsArray(arr) || jsvIsArrayBuffer(arr))) { jsExceptionHere(JSET_ERROR, "Expecting first argument to be an array, not %t", arr); return NAN; } JsVarFloat sum = 0; JsvIterator itsrc; jsvIteratorNew(&itsrc, arr); while (jsvIteratorHasElement(&itsrc)) { sum += jsvIteratorGetFloatValue(&itsrc); jsvIteratorNext(&itsrc); } jsvIteratorFree(&itsrc); return sum; }
void jsvIteratorNew(JsvIterator *it, JsVar *obj, JsvIteratorFlags flags) { if (jsvIsArray(obj) || jsvIsObject(obj) || jsvIsFunction(obj) || jsvIsGetterOrSetter(obj)) { it->type = JSVI_OBJECT; if (jsvIsArray(obj) && (flags&JSIF_EVERY_ARRAY_ELEMENT)) { it->type = JSVI_FULLARRAY; it->it.obj.index = 0; it->it.obj.var = jsvLockAgain(obj); } jsvObjectIteratorNew(&it->it.obj.it, obj); } else if (jsvIsArrayBuffer(obj)) { it->type = JSVI_ARRAYBUFFER; jsvArrayBufferIteratorNew(&it->it.buf, obj, 0); } else if (jsvHasCharacterData(obj)) { it->type = JSVI_STRING; jsvStringIteratorNew(&it->it.str, obj, 0); } else assert(0); }
JsVar *jswrap_typedarray_constructor(JsVarDataArrayBufferViewType type, JsVar *arr, JsVarInt byteOffset, JsVarInt length) { JsVar *arrayBuffer = 0; if (jsvIsArrayBuffer(arr)) { arrayBuffer = jsvLockAgain(arr); } else if (jsvIsInt(arr)) { length = jsvGetInteger(arr); byteOffset = 0; arrayBuffer = jswrap_arraybuffer_constructor(JSV_ARRAYBUFFER_GET_SIZE(type)*length); } else if (jsvIsArray(arr)) { length = jsvGetArrayLength(arr); byteOffset = 0; arrayBuffer = jswrap_arraybuffer_constructor(JSV_ARRAYBUFFER_GET_SIZE(type)*length); // later on we'll populate this } if (!arrayBuffer) { jsError("Unsupported first argument\n"); return 0; } if (length<=0) length = (JsVarInt)jsvGetArrayBufferLength(arrayBuffer) / JSV_ARRAYBUFFER_GET_SIZE(type); JsVar *typedArr = jsvNewWithFlags(JSV_ARRAYBUFFER); if (typedArr) { typedArr->varData.arraybuffer.type = type; typedArr->varData.arraybuffer.byteOffset = (unsigned short)byteOffset; typedArr->varData.arraybuffer.length = (unsigned short)length; typedArr->firstChild = jsvGetRef(jsvRef(arrayBuffer)); if (jsvIsArray(arr)) { // if we were given an array, populate this ArrayBuffer JsvArrayIterator it; jsvArrayIteratorNew(&it, arr); while (jsvArrayIteratorHasElement(&it)) { JsVar *idx = jsvArrayIteratorGetIndex(&it); if (jsvIsInt(idx)) { JsVar *val = jsvArrayIteratorGetElement(&it); jsvArrayBufferSet(typedArr, jsvGetInteger(idx), val); jsvUnLock(val); } jsvUnLock(idx); jsvArrayIteratorNext(&it); } jsvArrayIteratorFree(&it); } } jsvUnLock(arrayBuffer); return typedArr; }
// -------------------------------------------------------------------------------------------- void jsvArrayBufferIteratorNew(JsvArrayBufferIterator *it, JsVar *arrayBuffer, size_t index) { assert(jsvIsArrayBuffer(arrayBuffer)); it->index = index; it->type = arrayBuffer->varData.arraybuffer.type; it->byteLength = arrayBuffer->varData.arraybuffer.length * JSV_ARRAYBUFFER_GET_SIZE(it->type); it->byteOffset = arrayBuffer->varData.arraybuffer.byteOffset; JsVar *arrayBufferData = jsvGetArrayBufferBackingString(arrayBuffer); it->byteLength += it->byteOffset; // because we'll check if we have more bytes using this it->byteOffset = it->byteOffset + index*JSV_ARRAYBUFFER_GET_SIZE(it->type); if (it->byteOffset>=(it->byteLength+1-JSV_ARRAYBUFFER_GET_SIZE(it->type))) { jsvUnLock(arrayBufferData); it->type = ARRAYBUFFERVIEW_UNDEFINED; return; } jsvStringIteratorNew(&it->it, arrayBufferData, (size_t)it->byteOffset); jsvUnLock(arrayBufferData); it->hasAccessedElement = false; }
/*JSON{ "type" : "staticmethod", "class" : "E", "name" : "interpolate", "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_espruino_interpolate", "params" : [ ["array","JsVar","A Typed Array to interpolate between"], ["index","float","Floating point index to access"] ], "return" : ["float","The result of interpolating between (int)index and (int)(index+1)"] } Interpolate between two adjacent values in the Typed Array */ JsVarFloat jswrap_espruino_interpolate(JsVar *array, JsVarFloat findex) { if (!jsvIsArrayBuffer(array)) return 0; size_t idx = (size_t)findex; JsVarFloat a = findex - (int)idx; if (findex<0) { idx = 0; a = 0; } if (findex>=jsvGetArrayBufferLength(array)-1) { idx = jsvGetArrayBufferLength(array)-1; a = 0; } JsvArrayBufferIterator it; jsvArrayBufferIteratorNew(&it, array, idx); JsVarFloat fa = jsvArrayBufferIteratorGetFloatValue(&it); jsvArrayBufferIteratorNext(&it); JsVarFloat fb = jsvArrayBufferIteratorGetFloatValue(&it); jsvArrayBufferIteratorFree(&it); return fa*(1-a) + fb*a; }
/*JSON{ "type" : "staticmethod", "class" : "E", "name" : "interpolate2d", "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_espruino_interpolate2d", "params" : [ ["array","JsVar","A Typed Array to interpolate between"], ["width","int32","Integer 'width' of 2d array"], ["x","float","Floating point X index to access"], ["y","float","Floating point Y index to access"] ], "return" : ["float","The result of interpolating in 2d between the 4 surrounding cells"] } Interpolate between four adjacent values in the Typed Array, in 2D. */ JsVarFloat jswrap_espruino_interpolate2d(JsVar *array, int width, JsVarFloat x, JsVarFloat y) { if (!jsvIsArrayBuffer(array)) return 0; int yidx = (int)y; JsVarFloat ay = y-yidx; if (y<0) { yidx = 0; ay = 0; } JsVarFloat findex = x + (JsVarFloat)(yidx*width); size_t idx = (size_t)findex; JsVarFloat ax = findex-(int)idx; if (x<0) { idx = (size_t)(yidx*width); ax = 0; } JsvArrayBufferIterator it; jsvArrayBufferIteratorNew(&it, array, idx); JsVarFloat xa,xb; int i; xa = jsvArrayBufferIteratorGetFloatValue(&it); jsvArrayBufferIteratorNext(&it); xb = jsvArrayBufferIteratorGetFloatValue(&it); JsVarFloat ya = xa*(1-ax) + xb*ax; for (i=1;i<width;i++) jsvArrayBufferIteratorNext(&it); xa = jsvArrayBufferIteratorGetFloatValue(&it); jsvArrayBufferIteratorNext(&it); xb = jsvArrayBufferIteratorGetFloatValue(&it); jsvArrayBufferIteratorFree(&it); JsVarFloat yb = xa*(1-ax) + xb*ax; return ya*(1-ay) + yb*ay; }
/** Iterate over the contents of the content of a variable, calling callback for each. Contents may be: * numeric -> output * a string -> output each character * array/arraybuffer -> call itself on each element * {data:..., count:...} -> call itself object.count times, on object.data * {callback:...} -> call the given function, call itself on return value */ bool jsvIterateCallback( JsVar *data, jsvIterateCallbackFn callback, void *callbackData ) { bool ok = true; // Handle the data being a single numeric. if (jsvIsNumeric(data)) { callback((int)jsvGetInteger(data), callbackData); } // Handle the data being an object. else if (jsvIsObject(data)) { JsVar *callbackVar = jsvObjectGetChild(data, "callback", 0); if (jsvIsFunction(callbackVar)) { JsVar *result = jspExecuteFunction(callbackVar,0,0,NULL); jsvUnLock(callbackVar); if (result) { bool r = jsvIterateCallback(result, callback, callbackData); jsvUnLock(result); return r; } return true; } jsvUnLock(callbackVar); JsVar *countVar = jsvObjectGetChild(data, "count", 0); JsVar *dataVar = jsvObjectGetChild(data, "data", 0); if (countVar && dataVar && jsvIsNumeric(countVar)) { int n = (int)jsvGetInteger(countVar); while (ok && n-- > 0) { ok = jsvIterateCallback(dataVar, callback, callbackData); } } else { jsExceptionHere(JSET_TYPEERROR, "If specifying an object, it must be of the form {data : ..., count : N} or {callback : fn} - got %j", data); ok = false; } jsvUnLock2(countVar, dataVar); } // Handle the data being a string else if (jsvIsString(data)) { JsvStringIterator it; jsvStringIteratorNew(&it, data, 0); while (jsvStringIteratorHasChar(&it) && ok) { char ch = jsvStringIteratorGetChar(&it); callback(ch, callbackData); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); } // Handle the data being an array buffer else if (jsvIsArrayBuffer(data)) { JsvArrayBufferIterator it; jsvArrayBufferIteratorNew(&it, data, 0); if (JSV_ARRAYBUFFER_GET_SIZE(it.type) == 1 && !JSV_ARRAYBUFFER_IS_SIGNED(it.type)) { JsvStringIterator *sit = &it.it; // faster for single byte arrays - read using the string iterator. while (jsvStringIteratorHasChar(sit)) { callback((int)(unsigned char)jsvStringIteratorGetChar(sit), callbackData); jsvStringIteratorNextInline(sit); } } else { while (jsvArrayBufferIteratorHasElement(&it)) { callback((int)jsvArrayBufferIteratorGetIntegerValue(&it), callbackData); jsvArrayBufferIteratorNext(&it); } } jsvArrayBufferIteratorFree(&it); } // Handle the data being iterable else if (jsvIsIterable(data)) { JsvIterator it; jsvIteratorNew(&it, data, JSIF_EVERY_ARRAY_ELEMENT); while (jsvIteratorHasElement(&it) && ok) { JsVar *el = jsvIteratorGetValue(&it); ok = jsvIterateCallback(el, callback, callbackData); jsvUnLock(el); jsvIteratorNext(&it); } jsvIteratorFree(&it); } else { jsExceptionHere(JSET_TYPEERROR, "Expecting a number or something iterable, got %t", data); ok = false; } return ok; }
/** * \brief Iterate over the contents of the content of a variable, calling callback for each. * Contents may be: * * numeric -> output * * a string -> output each character * * array/arraybuffer -> call itself on each element * object -> call itself object.count times, on object.data */ bool jsvIterateCallback( JsVar *data, // The data to iterate over. void (*callback)(int item, void *callbackData), // The callback function invoke. void *callbackData // Data to be passed to the callback function ) { bool ok = true; // Handle the data being a single numeric. if (jsvIsNumeric(data)) { callback((int)jsvGetInteger(data), callbackData); } // Handle the data being an object. else if (jsvIsObject(data)) { JsVar *countVar = jsvObjectGetChild(data, "count", 0); JsVar *dataVar = jsvObjectGetChild(data, "data", 0); if (countVar && dataVar && jsvIsNumeric(countVar)) { int n = (int)jsvGetInteger(countVar); while (ok && n-- > 0) { ok = jsvIterateCallback(dataVar, callback, callbackData); } } else { jsWarn("If specifying an object, it must be of the form {data : ..., count : N}"); } jsvUnLock2(countVar, dataVar); } // Handle the data being a string else if (jsvIsString(data)) { JsvStringIterator it; jsvStringIteratorNew(&it, data, 0); while (jsvStringIteratorHasChar(&it) && ok) { char ch = jsvStringIteratorGetChar(&it); callback(ch, callbackData); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); } // Handle the data being an array buffer else if (jsvIsArrayBuffer(data)) { JsvArrayBufferIterator it; jsvArrayBufferIteratorNew(&it, data, 0); if (JSV_ARRAYBUFFER_GET_SIZE(it.type) == 1 && !JSV_ARRAYBUFFER_IS_SIGNED(it.type)) { // faster for single byte arrays. while (jsvArrayBufferIteratorHasElement(&it)) { callback((int)(unsigned char)jsvStringIteratorGetChar(&it.it), callbackData); jsvArrayBufferIteratorNext(&it); } } else { while (jsvArrayBufferIteratorHasElement(&it)) { callback((int)jsvArrayBufferIteratorGetIntegerValue(&it), callbackData); jsvArrayBufferIteratorNext(&it); } } jsvArrayBufferIteratorFree(&it); } // Handle the data being iterable else if (jsvIsIterable(data)) { JsvIterator it; jsvIteratorNew(&it, data); while (jsvIteratorHasElement(&it) && ok) { JsVar *el = jsvIteratorGetValue(&it); ok = jsvIterateCallback(el, callback, callbackData); jsvUnLock(el); jsvIteratorNext(&it); } jsvIteratorFree(&it); } else { jsWarn("Expecting a number or something iterable, got %t", data); ok = false; } return ok; }
void jsfGetJSONWithCallback(JsVar *var, JSONFlags flags, const char *whitespace, vcbprintf_callback user_callback, void *user_data) { JSONFlags nflags = flags + JSON_INDENT; // if we add a newline, make sure we indent any subsequent JSON more if (!whitespace) whitespace=" "; if (jsvIsUndefined(var)) { cbprintf(user_callback, user_data, "undefined"); } else { // Use IS_RECURSING flag to stop recursion if (var->flags & JSV_IS_RECURSING) { cbprintf(user_callback, user_data, " ... "); return; } var->flags |= JSV_IS_RECURSING; if (jsvIsArray(var)) { JsVarInt length = jsvGetArrayLength(var); bool limited = (flags&JSON_LIMIT) && (length>(JsVarInt)JSON_LIMIT_AMOUNT); bool needNewLine = false; cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"[ ":"["); JsVarInt lastIndex = -1; bool numeric = true; bool first = true; JsvObjectIterator it; jsvObjectIteratorNew(&it, var); while (lastIndex+1<length && numeric && !jspIsInterrupted()) { JsVar *key = jsvObjectIteratorGetKey(&it); if (!jsvObjectIteratorHasValue(&it) || jsvIsNumeric(key)) { JsVarInt index = jsvObjectIteratorHasValue(&it) ? jsvGetInteger(key) : length-1; JsVar *item = jsvObjectIteratorGetValue(&it); while (lastIndex < index) { lastIndex++; if (!limited || lastIndex<(JsVarInt)JSON_LIMITED_AMOUNT || lastIndex>=length-(JsVarInt)JSON_LIMITED_AMOUNT) { if (!first) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); first = false; if (limited && lastIndex==length-(JsVarInt)JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); bool newNeedsNewLine = ((flags&JSON_SOME_NEWLINES) && jsonNeedsNewLine(item)); if (flags&JSON_ALL_NEWLINES) { needNewLine = true; newNeedsNewLine = true; } if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, whitespace, user_callback, user_data); needNewLine = false; } if (lastIndex == index) jsfGetJSONWithCallback(item, nflags, whitespace, user_callback, user_data); else cbprintf(user_callback, user_data, (flags&JSON_NO_UNDEFINED)?"null":"undefined"); needNewLine = newNeedsNewLine; } } jsvUnLock(item); jsvObjectIteratorNext(&it); } else { numeric = false; } jsvUnLock(key); } // non-numeric - but NOT for standard JSON if ((flags&JSON_PRETTY)) jsfGetJSONForObjectItWithCallback(&it, flags, whitespace, nflags, user_callback, user_data, first); jsvObjectIteratorFree(&it); if (needNewLine) jsonNewLine(flags, whitespace, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" ]":"]"); } else if (jsvIsArrayBuffer(var)) { JsvArrayBufferIterator it; bool allZero = true; jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it)) { if (jsvArrayBufferIteratorGetFloatValue(&it)!=0) allZero = false; jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); bool asArray = flags&JSON_ARRAYBUFFER_AS_ARRAY; if (allZero && !asArray) { cbprintf(user_callback, user_data, "new %s(%d)", jswGetBasicObjectName(var), jsvGetArrayBufferLength(var)); } else { const char *aname = jswGetBasicObjectName(var); /* You can't do `new ArrayBuffer([1,2,3])` so we have to output * `new Uint8Array([1,2,3]).buffer`! */ bool isBasicArrayBuffer = strcmp(aname,"ArrayBuffer")==0; if (isBasicArrayBuffer) { aname="Uint8Array"; } cbprintf(user_callback, user_data, asArray?"[":"new %s([", aname); if (flags&JSON_ALL_NEWLINES) jsonNewLine(nflags, whitespace, user_callback, user_data); size_t length = jsvGetArrayBufferLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); // no newlines needed for array buffers as they only contain simple stuff jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it) && !jspIsInterrupted()) { if (!limited || it.index<JSON_LIMITED_AMOUNT || it.index>=length-JSON_LIMITED_AMOUNT) { if (it.index>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (flags&JSON_ALL_NEWLINES) jsonNewLine(nflags, whitespace, user_callback, user_data); if (limited && it.index==length-JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); JsVar *item = jsvArrayBufferIteratorGetValue(&it); jsfGetJSONWithCallback(item, nflags, whitespace, user_callback, user_data); jsvUnLock(item); } jsvArrayBufferIteratorNext(&it); } if (flags&JSON_ALL_NEWLINES) jsonNewLine(flags, whitespace, user_callback, user_data); jsvArrayBufferIteratorFree(&it); cbprintf(user_callback, user_data, asArray?"]":"])"); if (isBasicArrayBuffer && !asArray) cbprintf(user_callback, user_data, ".buffer"); } } else if (jsvIsObject(var)) { IOEventFlags device = (flags & JSON_SHOW_DEVICES) ? jsiGetDeviceFromClass(var) : EV_NONE; if (device!=EV_NONE) { cbprintf(user_callback, user_data, "%s", jshGetDeviceString(device)); } else { bool showContents = true; if (flags & JSON_SHOW_OBJECT_NAMES) { JsVar *proto = jsvObjectGetChild(var, JSPARSE_INHERITS_VAR, 0); if (jsvHasChildren(proto)) { JsVar *constr = jsvObjectGetChild(proto, JSPARSE_CONSTRUCTOR_VAR, 0); if (constr) { JsVar *p = jsvGetIndexOf(execInfo.root, constr, true); if (p) cbprintf(user_callback, user_data, "%v: ", p); jsvUnLock2(p,constr); /* We had the constructor - now if there was a non-default toString function * we'll execute it and print the result */ JsVar *toStringFn = jspGetNamedField(var, "toString", false); if (toStringFn && toStringFn->varData.native.ptr != (void (*)(void))jswrap_object_toString) { // Function found and it's not the default one - execute it JsVar *result = jspExecuteFunction(toStringFn,var,0,0); cbprintf(user_callback, user_data, "%v", result); jsvUnLock(result); showContents = false; // we already printed something } jsvUnLock(toStringFn); } } jsvUnLock(proto); } if (showContents) { JsvObjectIterator it; jsvObjectIteratorNew(&it, var); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"{ ":"{"); bool needNewLine = jsfGetJSONForObjectItWithCallback(&it, flags, whitespace, nflags, user_callback, user_data, true); jsvObjectIteratorFree(&it); if (needNewLine) jsonNewLine(flags, whitespace, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" }":"}"); } } } else if (jsvIsFunction(var)) { if (flags & JSON_IGNORE_FUNCTIONS) { cbprintf(user_callback, user_data, "undefined"); } else { cbprintf(user_callback, user_data, "function "); jsfGetJSONForFunctionWithCallback(var, nflags, user_callback, user_data); } } else if (jsvIsString(var) && !jsvIsName(var)) { if ((flags&JSON_LIMIT) && jsvGetStringLength(var)>JSON_LIMIT_STRING_AMOUNT) { // if the string is too big, split it and put dots in the middle JsVar *var1 = jsvNewFromStringVar(var, 0, JSON_LIMITED_STRING_AMOUNT); JsVar *var2 = jsvNewFromStringVar(var, jsvGetStringLength(var)-JSON_LIMITED_STRING_AMOUNT, JSON_LIMITED_STRING_AMOUNT); cbprintf(user_callback, user_data, "%q%s%q", var1, JSON_LIMIT_TEXT, var2); jsvUnLock2(var1, var2); } else { cbprintf(user_callback, user_data, "%q", var); } } else { cbprintf(user_callback, user_data, "%v", var); } var->flags &= ~JSV_IS_RECURSING; } }
void jsfGetJSONWithCallback(JsVar *var, JSONFlags flags, vcbprintf_callback user_callback, void *user_data) { JSONFlags nflags = flags + JSON_INDENT; // if we add a newline, make sure we indent any subsequent JSON more if (jsvIsUndefined(var)) { cbprintf(user_callback, user_data, "undefined"); } else { // Use IS_RECURSING flag to stop recursion if (var->flags & JSV_IS_RECURSING) { cbprintf(user_callback, user_data, " ... "); return; } var->flags |= JSV_IS_RECURSING; if (jsvIsArray(var)) { size_t length = (size_t)jsvGetArrayLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); bool needNewLine = false; size_t i; cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"[ ":"["); for (i=0;i<length && !jspIsInterrupted();i++) { if (!limited || i<JSON_LIMITED_AMOUNT || i>=length-JSON_LIMITED_AMOUNT) { if (i>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (limited && i==length-JSON_LIMITED_AMOUNT) { if (needNewLine) jsonNewLine(nflags, user_callback, user_data); cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); } JsVar *item = jsvGetArrayItem(var, (JsVarInt)i); if (jsvIsUndefined(item) && (flags&JSON_NO_UNDEFINED)) item = jsvNewWithFlags(JSV_NULL); bool newNeedsNewLine = (flags&JSON_NEWLINES) && jsonNeedsNewLine(item); if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, user_callback, user_data); needNewLine = false; } jsfGetJSONWithCallback(item, nflags, user_callback, user_data); needNewLine = newNeedsNewLine; jsvUnLock(item); } } if (needNewLine) jsonNewLine(flags, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" ]":"]"); } else if (jsvIsArrayBuffer(var)) { JsvArrayBufferIterator it; bool allZero = true; jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it)) { if (jsvArrayBufferIteratorGetFloatValue(&it)!=0) allZero = false; jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); if (allZero) { cbprintf(user_callback, user_data, "new %s(%d)", jswGetBasicObjectName(var), jsvGetArrayBufferLength(var)); } else { cbprintf(user_callback, user_data, "new %s([", jswGetBasicObjectName(var)); size_t length = jsvGetArrayBufferLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); // no newlines needed for array buffers as they only contain simple stuff jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it) && !jspIsInterrupted()) { if (!limited || it.index<JSON_LIMITED_AMOUNT || it.index>=length-JSON_LIMITED_AMOUNT) { if (it.index>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (limited && it.index==length-JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); JsVar *item = jsvArrayBufferIteratorGetValue(&it); jsfGetJSONWithCallback(item, nflags, user_callback, user_data); jsvUnLock(item); } jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); cbprintf(user_callback, user_data, "])"); } } else if (jsvIsObject(var)) { IOEventFlags device = (flags & JSON_SHOW_DEVICES) ? jsiGetDeviceFromClass(var) : EV_NONE; if (device!=EV_NONE) { cbprintf(user_callback, user_data, "%s", jshGetDeviceString(device)); } else { bool first = true; bool needNewLine = false; size_t sinceNewLine = 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, var); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"{ ":"{"); while (jsvObjectIteratorHasValue(&it) && !jspIsInterrupted()) { JsVar *index = jsvObjectIteratorGetKey(&it); JsVar *item = jsvObjectIteratorGetValue(&it); bool hidden = jsvIsInternalObjectKey(index) || ((flags & JSON_IGNORE_FUNCTIONS) && jsvIsFunction(item)) || ((flags&JSON_NO_UNDEFINED) && jsvIsUndefined(item)); if (!hidden) { sinceNewLine++; if (!first) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); bool newNeedsNewLine = (flags&JSON_NEWLINES) && jsonNeedsNewLine(item); if ((flags&JSON_NEWLINES) && sinceNewLine>JSON_ITEMS_ON_LINE_OBJECT) needNewLine = true; if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, user_callback, user_data); needNewLine = false; sinceNewLine = 0; } cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"%q: ":"%q:", index); if (first) first = false; jsfGetJSONWithCallback(item, nflags, user_callback, user_data); needNewLine = newNeedsNewLine; } jsvUnLock(index); jsvUnLock(item); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); if (needNewLine) jsonNewLine(flags, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" }":"}"); } } else if (jsvIsFunction(var)) { if (flags & JSON_IGNORE_FUNCTIONS) { cbprintf(user_callback, user_data, "undefined"); } else { cbprintf(user_callback, user_data, "function "); jsfGetJSONForFunctionWithCallback(var, nflags, user_callback, user_data); } } else if (jsvIsString(var) && !jsvIsName(var)) { if ((flags&JSON_LIMIT) && jsvGetStringLength(var)>JSON_LIMIT_STRING_AMOUNT) { // if the string is too big, split it and put dots in the middle JsVar *var1 = jsvNewFromStringVar(var, 0, JSON_LIMITED_STRING_AMOUNT); JsVar *var2 = jsvNewFromStringVar(var, jsvGetStringLength(var)-JSON_LIMITED_STRING_AMOUNT, JSON_LIMITED_STRING_AMOUNT); cbprintf(user_callback, user_data, "%q%s%q", var1, JSON_LIMIT_TEXT, var2); jsvUnLock(var1); jsvUnLock(var2); } else { cbprintf(user_callback, user_data, "%q", var); } } else { cbprintf(user_callback, user_data, "%v", var); } var->flags &= ~JSV_IS_RECURSING; } }