void jsvArrayBufferIteratorNext(JsvArrayBufferIterator *it) { it->index++; it->byteOffset += JSV_ARRAYBUFFER_GET_SIZE(it->type); if (!it->hasAccessedElement) { unsigned int dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type); while (dataLen--) jsvStringIteratorNext(&it->it); } else it->hasAccessedElement = false; }
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; }
void jsvArrayBufferIteratorSetValue(JsvArrayBufferIterator *it, JsVar *value) { if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return; assert(!it->hasAccessedElement); // we just haven't implemented this case yet char data[8]; int i,dataLen = (int)JSV_ARRAYBUFFER_GET_SIZE(it->type); if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) { jsvArrayBufferIteratorFloatToData(data, (unsigned)dataLen, it->type, jsvGetFloat(value)); } else { jsvArrayBufferIteratorIntToData(data, (unsigned)dataLen, it->type, jsvGetInteger(value)); } if (it->type & ARRAYBUFFERVIEW_BIG_ENDIAN) { for (i=dataLen-1;i>=0;i--) { jsvStringIteratorSetChar(&it->it, data[i]); if (dataLen!=1) jsvStringIteratorNext(&it->it); } } else { for (i=0;i<dataLen;i++) { jsvStringIteratorSetChar(&it->it, data[i]); if (dataLen!=1) jsvStringIteratorNext(&it->it); } } if (dataLen!=1) it->hasAccessedElement = true; }
void jsvArrayBufferIteratorSetValue(JsvArrayBufferIterator *it, JsVar *value) { if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return; assert(!it->hasAccessedElement); // we just haven't implemented this case yet char data[8]; unsigned int i,dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type); if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) { JsVarFloat v = jsvGetFloat(value); ; if (dataLen==4) { float f = (float)v; memcpy(data,&f,dataLen); } else if (dataLen==8) { double f = (double)v; memcpy(data,&f,dataLen); } else assert(0); } else { JsVarInt v = jsvGetInteger(value); // we don't care about sign when writing - as it gets truncated if (dataLen==1) { char c = (char)v; memcpy(data,&c,dataLen); } else if (dataLen==2) { short c = (short)v; memcpy(data,&c,dataLen); } else if (dataLen==4) { int c = (int)v; memcpy(data,&c,dataLen); } else if (dataLen==8) { long long c = (long long)v; memcpy(data,&c,dataLen); } else assert(0); } for (i=0;i<dataLen;i++) { jsvStringIteratorSetChar(&it->it, data[i]); if (dataLen!=1) jsvStringIteratorNext(&it->it); } if (dataLen!=1) it->hasAccessedElement = true; }
void jsvArrayBufferIteratorSetByteValue(JsvArrayBufferIterator *it, char c) { if (JSV_ARRAYBUFFER_GET_SIZE(it->type)!=1) { assert(0); return; } jsvStringIteratorSetChar(&it->it, c); }
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; }
static JsVarFloat jsvArrayBufferIteratorDataToFloat(JsvArrayBufferIterator *it, char *data) { unsigned int dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type); JsVarFloat v = 0; if (dataLen==4) v = *(float*)data; else if (dataLen==8) v = *(double*)data; else assert(0); return v; }
// -------------------------------------------------------------------------------------------- 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; }
static void jsvArrayBufferIteratorGetValueData(JsvArrayBufferIterator *it, char *data) { if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return; assert(!it->hasAccessedElement); // we just haven't implemented this case yet unsigned int i,dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type); for (i=0;i<dataLen;i++) { data[i] = jsvStringIteratorGetChar(&it->it); if (dataLen!=1) jsvStringIteratorNext(&it->it); } if (dataLen!=1) it->hasAccessedElement = true; }
static JsVarInt jsvArrayBufferIteratorDataToInt(JsvArrayBufferIterator *it, char *data) { unsigned int dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type); unsigned int bits = 8*dataLen; JsVarInt mask = (JsVarInt)((1ULL << bits)-1); JsVarInt v = *(int*)data; v = v & mask; if (JSV_ARRAYBUFFER_IS_SIGNED(it->type) && (v&(JsVarInt)(1UL<<(bits-1)))) v |= ~mask; return v; }
static JsVarInt jsvArrayBufferIteratorDataToInt(JsvArrayBufferIterator *it, char *data) { unsigned int dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type); JsVarInt v = 0; if (dataLen==1) v = *(int8_t*)data; else if (dataLen==2) v = *(short*)data; else if (dataLen==4) v = *(int*)data; else assert(0); if ((!JSV_ARRAYBUFFER_IS_SIGNED(it->type))) v = v & (JsVarInt)((1UL << (8*dataLen))-1); return v; }
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; }
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; }
void jsvArrayBufferIteratorSetIntegerValue(JsvArrayBufferIterator *it, JsVarInt v) { if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return; assert(!it->hasAccessedElement); // we just haven't implemented this case yet char data[8]; unsigned int i,dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type); if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) { jsvArrayBufferIteratorFloatToData(data, dataLen, it->type, (JsVarFloat)v); } else { jsvArrayBufferIteratorIntToData(data, dataLen, it->type, v); } for (i=0;i<dataLen;i++) { jsvStringIteratorSetChar(&it->it, data[i]); if (dataLen!=1) jsvStringIteratorNext(&it->it); } if (dataLen!=1) it->hasAccessedElement = true; }
bool jsvArrayBufferIteratorHasElement(JsvArrayBufferIterator *it) { if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return false; if (it->hasAccessedElement) return true; return it->byteOffset <= (it->byteLength-JSV_ARRAYBUFFER_GET_SIZE(it->type)); }
/** * \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; }
/** 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; }