Skip to content

Commit

Permalink
ESP8266/others: Fix ArrayBuffers with 12 bit JsVars (previously they …
Browse files Browse the repository at this point in the history
…overflowed)
  • Loading branch information
gfwilliams committed Dec 19, 2023
1 parent ee8bc67 commit 7e2c680
Show file tree
Hide file tree
Showing 12 changed files with 55 additions and 40 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
Ensure we error if an unsupported byteOffset is supplied to types array constructor (fix #2439)
I2C: Add I2C.readReg to combine I2C.writeTo+readFrom in an easy/fast way
Microbit2: Set up SCK/LRCK to unused pins for neopixel to ensure neopixel will work ok
ESP8266/others: Fix ArrayBuffers with 12 bit JsVars (previously they overflowed)

2v19 : Fix Object.values/entries for numeric keys after 2v18 regression (fix #2375)
nRF52: for SD>5 use static buffers for advertising and scan response data (#2367)
Expand Down
10 changes: 10 additions & 0 deletions src/jsnative.c
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,16 @@ JsVar *jsnCallFunction(void *function, JsnArgumentType argumentSpecifier, JsVar

// -----------------------------------------------------------------------------------------

// compile time sanity tests

char const sanity_test_arraybuffer_type_one_byte[sizeof(JsVarDataArrayBufferViewType) == 1 ? 1 : -1];
char const sanity_test_arraybuffer_in_jsvar[sizeof(JsVarDataArrayBufferView) <= JSVAR_DATA_ARRAYBUFFER_LEN ? 1 : -1];
char const sanity_test_native_in_jsvar[sizeof(JsVarDataNative) <= JSVAR_DATA_ARRAYBUFFER_LEN ? 1 : -1];
char const sanity_test_nativestr_in_jsvar[sizeof(JsVarDataNativeStr) <= JSVAR_DATA_ARRAYBUFFER_LEN ? 1 : -1];


// runtime sanity tests

JsVarFloat sanity_pi() { return 3.141592; }
int32_t sanity_int_pass(int32_t hello) { return (hello*10)+5; }
int32_t sanity_int_flt_int(int32_t a, JsVarFloat b, int32_t c) {
Expand Down
11 changes: 11 additions & 0 deletions src/jsutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,17 @@ unsigned short int int_sqrt32(unsigned int x) {
return res;
}

// Reverse the order of bytes in an array, in place
void reverseBytes(char *data, int len) {
int halflen = len>>1;
int j = len-1;
for (int i=0;i<halflen;i++,j--) {
char t = data[i];
data[i] = data[j];
data[j] = t;
}
}

#ifdef ESPR_UNICODE_SUPPORT

/// Returns true if this character denotes the start of a UTF8 sequence
Expand Down
5 changes: 5 additions & 0 deletions src/jsutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ See comments after JsVar in jsvar.c for more info.

/// Max length for a JSV_STRING, JsVar.varData.ref.refs (see comments under JsVar decl in jsvar.h)
#define JSVAR_DATA_STRING_LEN (JSVAR_DATA_STRING_NAME_LEN + ((JSVARREF_BITS*3 + JSVARREFCOUNT_PACK_BITS)>>3))
/// Max length for an arraybuffer since it uses firstChild
#define JSVAR_DATA_ARRAYBUFFER_LEN (JSVAR_DATA_STRING_NAME_LEN + ((JSVARREF_BITS*2)>>3))
/// Max length for a JSV_STRINGEXT, JsVar.varData.ref.lastChild (see comments under JsVar decl in jsvar.h)
#define JSVAR_DATA_STRING_MAX_LEN (JSVAR_DATA_STRING_NAME_LEN + ((JSVARREF_BITS*3 + JSVARREFCOUNT_PACK_BITS + JSVARREFCOUNT_BITS)>>3))

Expand Down Expand Up @@ -619,6 +621,9 @@ bool calculateParity(uint8_t v);
/// quick integer square root
unsigned short int int_sqrt32(unsigned int x);

// Reverse the order of bytes in an array, in place
void reverseBytes(char *data, int len);

/** get the amount of free stack we have, in bytes */
size_t jsuGetFreeStack();

Expand Down
4 changes: 2 additions & 2 deletions src/jsvar.c
Original file line number Diff line number Diff line change
Expand Up @@ -2389,7 +2389,7 @@ JsVar *jsvGetArrayBufferBackingString(JsVar *arrayBuffer, uint32_t *offset) {
JsVar *jsvArrayBufferGet(JsVar *arrayBuffer, size_t idx) {
JsvArrayBufferIterator it;
jsvArrayBufferIteratorNew(&it, arrayBuffer, idx);
JsVar *v = jsvArrayBufferIteratorGetValue(&it);
JsVar *v = jsvArrayBufferIteratorGetValue(&it, false/*little endian*/);
jsvArrayBufferIteratorFree(&it);
return v;
}
Expand All @@ -2398,7 +2398,7 @@ JsVar *jsvArrayBufferGet(JsVar *arrayBuffer, size_t idx) {
void jsvArrayBufferSet(JsVar *arrayBuffer, size_t idx, JsVar *value) {
JsvArrayBufferIterator it;
jsvArrayBufferIteratorNew(&it, arrayBuffer, idx);
jsvArrayBufferIteratorSetValue(&it, value);
jsvArrayBufferIteratorSetValue(&it, value, false/*little endian*/);
jsvArrayBufferIteratorFree(&it);
}

Expand Down
3 changes: 1 addition & 2 deletions src/jsvar.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ typedef enum {
ARRAYBUFFERVIEW_FLOAT = 32,
ARRAYBUFFERVIEW_CLAMPED = 64, // As in Uint8ClampedArray - clamp to the acceptable bounds
ARRAYBUFFERVIEW_ARRAYBUFFER = 1 | 128, ///< Basic ArrayBuffer type
ARRAYBUFFERVIEW_BIG_ENDIAN = 256, ///< access as big endian (normally little)
ARRAYBUFFERVIEW_UINT8 = 1,
ARRAYBUFFERVIEW_INT8 = 1 | ARRAYBUFFERVIEW_SIGNED,
ARRAYBUFFERVIEW_UINT16 = 2,
Expand Down Expand Up @@ -247,7 +246,7 @@ typedef struct JsVarStruct {
| Offset | Size | Name | STRING | STR_EXT | NAME_STR | NAME_INT | INT | DOUBLE | OBJ/FUNC/ARRAY | ARRAYBUFFER | NATIVE_STR | FLAT_STR |
| | | | | | | | | | | | FLASH_STR | |
| 16b | | | | | | | | | | | FLASH_STR | |
|--------|------|---------|--------|----------|----------|----------|------|---------|----------------|-------------|------------|----------|
| 0 - 3 | 4 | varData | data | data | data | data | data | data | nativePtr | size | ptr | charLen |
| 4 - 5 | ? | next | data | data | next | next | - | data | argTypes | format | len | - |
Expand Down
41 changes: 16 additions & 25 deletions src/jsvariterator.c
Original file line number Diff line number Diff line change
Expand Up @@ -558,16 +558,9 @@ static void jsvArrayBufferIteratorGetValueData(JsvArrayBufferIterator *it, char
if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return;
assert(!it->hasAccessedElement); // we just haven't implemented this case yet
int i,dataLen = (int)JSV_ARRAYBUFFER_GET_SIZE(it->type);
if (it->type & ARRAYBUFFERVIEW_BIG_ENDIAN) {
for (i=dataLen-1;i>=0;i--) {
data[i] = jsvStringIteratorGetChar(&it->it);
if (dataLen!=1) jsvStringIteratorNext(&it->it);
}
} else {
for (i=0;i<dataLen;i++) {
data[i] = jsvStringIteratorGetChar(&it->it);
if (dataLen!=1) jsvStringIteratorNext(&it->it);
}
for (i=0;i<dataLen;i++) {
data[i] = jsvStringIteratorGetChar(&it->it);
if (dataLen!=1) jsvStringIteratorNext(&it->it);
}
if (dataLen!=1) it->hasAccessedElement = true;
}
Expand All @@ -592,15 +585,17 @@ static JsVarFloat jsvArrayBufferIteratorDataToFloat(JsvArrayBufferIterator *it,
return v;
}

JsVar *jsvArrayBufferIteratorGetValue(JsvArrayBufferIterator *it) {
JsVar *jsvArrayBufferIteratorGetValue(JsvArrayBufferIterator *it, bool bigEndian) {
if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return 0;
char data[8];
jsvArrayBufferIteratorGetValueData(it, data);
if (bigEndian)
reverseBytes(data, JSV_ARRAYBUFFER_GET_SIZE(it->type));
if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) {
return jsvNewFromFloat(jsvArrayBufferIteratorDataToFloat(it, data));
} else {
JsVarInt i = jsvArrayBufferIteratorDataToInt(it, data);
if ((it->type & ~ARRAYBUFFERVIEW_BIG_ENDIAN) == ARRAYBUFFERVIEW_UINT32)
if (it->type == ARRAYBUFFERVIEW_UINT32)
return jsvNewFromLongInteger((long long)(uint32_t)i);
return jsvNewFromInteger(i);
}
Expand All @@ -609,7 +604,7 @@ JsVar *jsvArrayBufferIteratorGetValue(JsvArrayBufferIterator *it) {
JsVar *jsvArrayBufferIteratorGetValueAndRewind(JsvArrayBufferIterator *it) {
JsvStringIterator oldIt;
jsvStringIteratorClone(&oldIt, &it->it);
JsVar *v = jsvArrayBufferIteratorGetValue(it);
JsVar *v = jsvArrayBufferIteratorGetValue(it, false/*little endian*/);
jsvStringIteratorFree(&it->it);
it->it = oldIt;
it->hasAccessedElement = false;
Expand Down Expand Up @@ -675,7 +670,7 @@ void jsvArrayBufferIteratorSetIntegerValue(JsvArrayBufferIterator *it, JsVarInt
if (dataLen!=1) it->hasAccessedElement = true;
}

void jsvArrayBufferIteratorSetValue(JsvArrayBufferIterator *it, JsVar *value) {
void jsvArrayBufferIteratorSetValue(JsvArrayBufferIterator *it, JsVar *value, bool bigEndian) {
if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return;
assert(!it->hasAccessedElement); // we just haven't implemented this case yet
char data[8];
Expand All @@ -687,16 +682,12 @@ void jsvArrayBufferIteratorSetValue(JsvArrayBufferIterator *it, JsVar *value) {
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 (bigEndian)
reverseBytes(data, dataLen);

for (i=0;i<dataLen;i++) {
jsvStringIteratorSetChar(&it->it, data[i]);
if (dataLen!=1) jsvStringIteratorNext(&it->it);
}
if (dataLen!=1) it->hasAccessedElement = true;
}
Expand All @@ -712,7 +703,7 @@ void jsvArrayBufferIteratorSetByteValue(JsvArrayBufferIterator *it, char c) {
void jsvArrayBufferIteratorSetValueAndRewind(JsvArrayBufferIterator *it, JsVar *value) {
JsvStringIterator oldIt;
jsvStringIteratorClone(&oldIt, &it->it);
jsvArrayBufferIteratorSetValue(it, value);
jsvArrayBufferIteratorSetValue(it, value, false/*little endian*/);
jsvStringIteratorFree(&it->it);
jsvStringIteratorClone(&it->it, &oldIt);
jsvStringIteratorFree(&oldIt);
Expand Down
4 changes: 2 additions & 2 deletions src/jsvariterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,11 @@ void jsvArrayBufferIteratorClone(JsvArrayBufferIterator *dstit, JsvArrayBufferIt
/** ArrayBuffers have the slightly odd side-effect that you can't write an element
* once you have read it. That's why we have jsvArrayBufferIteratorGetValueAndRewind
* which allows this, but is slower. */
JsVar *jsvArrayBufferIteratorGetValue(JsvArrayBufferIterator *it);
JsVar *jsvArrayBufferIteratorGetValue(JsvArrayBufferIterator *it, bool bigEndian);
JsVar *jsvArrayBufferIteratorGetValueAndRewind(JsvArrayBufferIterator *it);
JsVarInt jsvArrayBufferIteratorGetIntegerValue(JsvArrayBufferIterator *it);
JsVarFloat jsvArrayBufferIteratorGetFloatValue(JsvArrayBufferIterator *it);
void jsvArrayBufferIteratorSetValue(JsvArrayBufferIterator *it, JsVar *value);
void jsvArrayBufferIteratorSetValue(JsvArrayBufferIterator *it, JsVar *value, bool bigEndian);
void jsvArrayBufferIteratorSetValueAndRewind(JsvArrayBufferIterator *it, JsVar *value);
void jsvArrayBufferIteratorSetIntegerValue(JsvArrayBufferIterator *it, JsVarInt value);
void jsvArrayBufferIteratorSetByteValue(JsvArrayBufferIterator *it, char c); ///< special case for when we know we're writing to a byte array
Expand Down
4 changes: 2 additions & 2 deletions src/jswrap_arraybuffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ void jswrap_arraybufferview_set(JsVar *parent, JsVar *arr, int offset) {
jsvArrayBufferIteratorSetIntegerValue(&itdst, jsvIteratorGetIntegerValue(&itsrc));
} else {
JsVar *value = jsvIteratorGetValue(&itsrc);
jsvArrayBufferIteratorSetValue(&itdst, value);
jsvArrayBufferIteratorSetValue(&itdst, value, false/*little endian*/);
jsvUnLock(value);
}
jsvArrayBufferIteratorNext(&itdst);
Expand Down Expand Up @@ -723,7 +723,7 @@ JsVar *jswrap_arraybufferview_map(JsVar *parent, JsVar *funcVar, JsVar *thisVar)
mapped = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args);
jsvUnLockMany(2,args);
if (mapped) {
jsvArrayBufferIteratorSetValue(&itdst, mapped);
jsvArrayBufferIteratorSetValue(&itdst, mapped, false/*little endian*/);
jsvUnLock(mapped);
}
}
Expand Down
8 changes: 3 additions & 5 deletions src/jswrap_dataview.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ JsVar *jswrap_dataview_constructor(JsVar *buffer, int byteOffset, int byteLength
return dataview;
}


JsVar *jswrap_dataview_get(JsVar *dataview, JsVarDataArrayBufferViewType type, int byteOffset, bool littleEndian) {
JsVar *buffer = jsvObjectGetChildIfExists(dataview, "buffer");
if (!jsvIsArrayBuffer(buffer)) {
Expand All @@ -80,13 +79,13 @@ JsVar *jswrap_dataview_get(JsVar *dataview, JsVarDataArrayBufferViewType type, i
byteOffset += jsvGetIntegerAndUnLock(jsvObjectGetChildIfExists(dataview, "byteOffset"));
JsVarInt length = JSV_ARRAYBUFFER_GET_SIZE(type);
// TODO: range error based on byteLength?
if (!littleEndian) type |= ARRAYBUFFERVIEW_BIG_ENDIAN;

JsVar *arr = jswrap_typedarray_constructor(type, buffer, byteOffset, length);
jsvUnLock(buffer);
if (!arr) return 0;
JsvArrayBufferIterator it;
jsvArrayBufferIteratorNew(&it, arr, 0);
JsVar *value = jsvArrayBufferIteratorGetValue(&it);
JsVar *value = jsvArrayBufferIteratorGetValue(&it, !littleEndian);
jsvArrayBufferIteratorFree(&it);
jsvUnLock(arr);
return value;
Expand All @@ -100,13 +99,12 @@ void jswrap_dataview_set(JsVar *dataview, JsVarDataArrayBufferViewType type, int
byteOffset += jsvGetIntegerAndUnLock(jsvObjectGetChildIfExists(dataview, "byteOffset"));
JsVarInt length = JSV_ARRAYBUFFER_GET_SIZE(type);
// TODO: range error based on byteLength?
if (!littleEndian) type |= ARRAYBUFFERVIEW_BIG_ENDIAN;
JsVar *arr = jswrap_typedarray_constructor(type, buffer, byteOffset, length);
jsvUnLock(buffer);
if (!arr) return;
JsvArrayBufferIterator it;
jsvArrayBufferIteratorNew(&it, arr, 0);
jsvArrayBufferIteratorSetValue(&it, value);
jsvArrayBufferIteratorSetValue(&it, value, !littleEndian);
jsvArrayBufferIteratorFree(&it);
jsvUnLock(arr);
}
Expand Down
2 changes: 1 addition & 1 deletion src/jswrap_espruino.c
Original file line number Diff line number Diff line change
Expand Up @@ -1751,7 +1751,7 @@ void jswrap_espruino_mapInPlace(JsVar *from, JsVar *to, JsVar *map, JsVarInt bit
assert(jsvIsArrayBuffer(map));
v2 = jsvArrayBufferGet(map, (size_t)v);
}
jsvArrayBufferIteratorSetValue(&itTo, v2);
jsvArrayBufferIteratorSetValue(&itTo, v2, false/*little endian*/);
jsvUnLock(v2);
} else { // no map - push right through
jsvArrayBufferIteratorSetIntegerValue(&itTo, v);
Expand Down
2 changes: 1 addition & 1 deletion src/jswrap_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ void jsfGetJSONWithCallback(JsVar *var, JsVar *varName, JSONFlags flags, const c
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);
JsVar *item = jsvArrayBufferIteratorGetValue(&it, false/*little endian*/);
jsfGetJSONWithCallback(item, NULL, nflags, whitespace, user_callback, user_data);
jsvUnLock(item);
}
Expand Down

0 comments on commit 7e2c680

Please sign in to comment.