From d1c615eaa208a4e7c92b661f8f7674e92f19aedf Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Wed, 7 Aug 2024 23:11:24 -0700 Subject: [PATCH] QuickJS: added buffer module. --- src/qjs.h | 2 + src/qjs_buffer.c | 1845 +++++++++++++++++++++++++++++++++---- src/test/njs_unit_test.c | 817 ---------------- test/buffer.t.js | 815 +++++++++++++++- test/harness/runTsuite.js | 3 + 5 files changed, 2465 insertions(+), 1017 deletions(-) diff --git a/src/qjs.h b/src/qjs.h index 71e23d788..00e9296aa 100644 --- a/src/qjs.h +++ b/src/qjs.h @@ -16,6 +16,7 @@ #include #include #include +#include #if defined(__GNUC__) && (__GNUC__ >= 8) #pragma GCC diagnostic push @@ -43,6 +44,7 @@ JSContext *qjs_new_context(JSRuntime *rt, _Bool eval); JSValue qjs_buffer_alloc(JSContext *ctx, size_t size); +JSValue qjs_buffer_create(JSContext *ctx, u_char *start, size_t size); JSValue qjs_buffer_chb_alloc(JSContext *ctx, njs_chb_t *chain); typedef int (*qjs_buffer_encode_t)(JSContext *ctx, const njs_str_t *src, diff --git a/src/qjs_buffer.c b/src/qjs_buffer.c index 166eb970e..83764e023 100644 --- a/src/qjs_buffer.c +++ b/src/qjs_buffer.c @@ -6,23 +6,75 @@ #include +#define INT24_MAX 0x7FFFFF +#define INT24_MIN (-0x800000) +#define INT40_MAX 0x7FFFFFFFFFLL +#define INT40_MIN (-0x8000000000LL) +#define INT48_MAX 0x7FFFFFFFFFFFLL +#define INT48_MIN (-0x800000000000LL) +#define UINT24_MAX 0xFFFFFFLL +#define UINT40_MAX 0xFFFFFFFFFFLL +#define UINT48_MAX 0xFFFFFFFFFFFFLL + +#define qjs_buffer_magic(size, sign, little) \ + ((size << 2) | (sign << 1) | little) + static JSValue qjs_buffer(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); +static JSValue qjs_buffer_ctor(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv); +static JSValue qjs_bufferobj_alloc(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int ignored); +static JSValue qjs_buffer_byte_length(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue qjs_buffer_compare(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue qjs_buffer_concat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue qjs_buffer_fill(JSContext *ctx, JSValueConst buffer, + JSValueConst fill, JSValueConst encode, uint64_t offset, uint64_t end); static JSValue qjs_buffer_from(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_is_buffer(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); +static JSValue qjs_buffer_is_encoding(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue qjs_buffer_prototype_compare(JSContext *ctx, + JSValueConst this_val, int argc, JSValueConst *argv); +static JSValue qjs_buffer_prototype_copy(JSContext *ctx, + JSValueConst this_val, int argc, JSValueConst *argv); +static JSValue qjs_buffer_prototype_equals(JSContext *ctx, + JSValueConst this_val, int argc, JSValueConst *argv); +static JSValue qjs_buffer_prototype_fill(JSContext *ctx, + JSValueConst this_val, int argc, JSValueConst *argv); +static JSValue qjs_buffer_prototype_includes(JSContext *ctx, + JSValueConst this_val, int argc, JSValueConst *argv); +static JSValue qjs_buffer_prototype_index_of(JSContext *ctx, + JSValueConst this_val, int argc, JSValueConst *argv, int last); +static JSValue qjs_buffer_prototype_read_float(JSContext *ctx, + JSValueConst this_val, int argc, JSValueConst *argv, int magic); +static JSValue qjs_buffer_prototype_read_int(JSContext *ctx, + JSValueConst this_val, int argc, JSValueConst *argv, int magic); +static JSValue qjs_buffer_prototype_swap(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int size); static JSValue qjs_buffer_prototype_to_json(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_prototype_to_string(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); +static JSValue qjs_buffer_prototype_write(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue qjs_buffer_prototype_write_int(JSContext *ctx, + JSValueConst this_val, int argc, JSValueConst *argv, int magic); +static JSValue qjs_buffer_prototype_write_float(JSContext *ctx, + JSValueConst this_val, int argc, JSValueConst *argv, int magic); static JSValue qjs_buffer_from_string(JSContext *ctx, JSValueConst str, JSValueConst encoding); static JSValue qjs_buffer_from_typed_array(JSContext *ctx, JSValueConst obj, size_t offset, size_t size, size_t bytes, int float32); -static JSValue qjs_buffer_from_array_buffer(JSContext *ctx, u_char *buf, - size_t size, JSValueConst offset, JSValueConst length); static JSValue qjs_buffer_from_object(JSContext *ctx, JSValueConst obj); +static JSValue qjs_buffer_compare_array(JSContext *ctx, JSValue val1, + JSValue val2, JSValueConst target_start, JSValueConst target_end, + JSValueConst source_start, JSValueConst source_end); static int qjs_base64_encode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst); static size_t qjs_base64_encode_length(JSContext *ctx, const njs_str_t *src); @@ -38,7 +90,8 @@ static int qjs_hex_encode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst); static size_t qjs_hex_encode_length(JSContext *ctx, const njs_str_t *src); static int qjs_hex_decode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst); static size_t qjs_hex_decode_length(JSContext *ctx, const njs_str_t *src); -static JSValue qjs_new_uint8_array(JSContext *ctx, size_t size); +static JSValue qjs_new_uint8_array(JSContext *ctx, int argc, + JSValueConst *argv); static JSModuleDef *qjs_buffer_init(JSContext *ctx, const char *name); @@ -103,14 +156,103 @@ static const JSCFunctionListEntry qjs_buffer_export[] = { static const JSCFunctionListEntry qjs_buffer_props[] = { + JS_CFUNC_MAGIC_DEF("alloc", 3, qjs_bufferobj_alloc, 0), + JS_CFUNC_MAGIC_DEF("allocUnsafe", 3, qjs_bufferobj_alloc, 1), + JS_CFUNC_DEF("byteLength", 2, qjs_buffer_byte_length), + JS_CFUNC_DEF("compare", 6, qjs_buffer_compare), + JS_CFUNC_DEF("concat", 1, qjs_buffer_concat), JS_CFUNC_DEF("from", 3, qjs_buffer_from), JS_CFUNC_DEF("isBuffer", 1, qjs_buffer_is_buffer), + JS_CFUNC_DEF("isEncoding", 1, qjs_buffer_is_encoding), }; static const JSCFunctionListEntry qjs_buffer_proto[] = { + JS_CFUNC_DEF("compare", 5, qjs_buffer_prototype_compare), + JS_CFUNC_DEF("copy", 5, qjs_buffer_prototype_copy), + JS_CFUNC_DEF("equals", 1, qjs_buffer_prototype_equals), + JS_CFUNC_DEF("fill", 4, qjs_buffer_prototype_fill), + JS_CFUNC_DEF("includes", 3, qjs_buffer_prototype_includes), + JS_CFUNC_MAGIC_DEF("indexOf", 3, qjs_buffer_prototype_index_of, 0), + JS_CFUNC_MAGIC_DEF("lastIndexOf", 3, qjs_buffer_prototype_index_of, 1), + JS_CFUNC_MAGIC_DEF("readFloatLE", 1, qjs_buffer_prototype_read_float, + qjs_buffer_magic(4, 1, 1)), + JS_CFUNC_MAGIC_DEF("readFloatBE", 1, qjs_buffer_prototype_read_float, + qjs_buffer_magic(4, 1, 0)), + JS_CFUNC_MAGIC_DEF("readDoubleLE", 1, qjs_buffer_prototype_read_float, + qjs_buffer_magic(8, 1, 1)), + JS_CFUNC_MAGIC_DEF("readDoubleBE", 1, qjs_buffer_prototype_read_float, + qjs_buffer_magic(8, 1, 0)), + JS_CFUNC_MAGIC_DEF("readInt8", 1, qjs_buffer_prototype_read_int, + qjs_buffer_magic(1, 1, 1)), + JS_CFUNC_MAGIC_DEF("readUInt8", 1, qjs_buffer_prototype_read_int, + qjs_buffer_magic(1, 0, 1)), + JS_CFUNC_MAGIC_DEF("readInt16LE", 1, qjs_buffer_prototype_read_int, + qjs_buffer_magic(2, 1, 1)), + JS_CFUNC_MAGIC_DEF("readUInt16LE", 1, qjs_buffer_prototype_read_int, + qjs_buffer_magic(2, 0, 1)), + JS_CFUNC_MAGIC_DEF("readInt16BE", 1, qjs_buffer_prototype_read_int, + qjs_buffer_magic(2, 1, 0)), + JS_CFUNC_MAGIC_DEF("readUInt16BE", 1, qjs_buffer_prototype_read_int, + qjs_buffer_magic(2, 0, 0)), + JS_CFUNC_MAGIC_DEF("readInt32LE", 1, qjs_buffer_prototype_read_int, + qjs_buffer_magic(4, 1, 1)), + JS_CFUNC_MAGIC_DEF("readUInt32LE", 1, qjs_buffer_prototype_read_int, + qjs_buffer_magic(4, 0, 1)), + JS_CFUNC_MAGIC_DEF("readInt32BE", 1, qjs_buffer_prototype_read_int, + qjs_buffer_magic(4, 1, 0)), + JS_CFUNC_MAGIC_DEF("readUInt32BE", 1, qjs_buffer_prototype_read_int, + qjs_buffer_magic(4, 0, 0)), + JS_CFUNC_MAGIC_DEF("readIntLE", 2, qjs_buffer_prototype_read_int, + qjs_buffer_magic(0, 1, 1)), + JS_CFUNC_MAGIC_DEF("readUIntLE", 2, qjs_buffer_prototype_read_int, + qjs_buffer_magic(0, 0, 1)), + JS_CFUNC_MAGIC_DEF("readIntBE", 2, qjs_buffer_prototype_read_int, + qjs_buffer_magic(0, 1, 0)), + JS_CFUNC_MAGIC_DEF("readUIntBE", 2, qjs_buffer_prototype_read_int, + qjs_buffer_magic(0, 0, 0)), + JS_CFUNC_MAGIC_DEF("swap16", 0, qjs_buffer_prototype_swap, 2), + JS_CFUNC_MAGIC_DEF("swap32", 0, qjs_buffer_prototype_swap, 4), + JS_CFUNC_MAGIC_DEF("swap64", 0, qjs_buffer_prototype_swap, 8), JS_CFUNC_DEF("toJSON", 0, qjs_buffer_prototype_to_json), JS_CFUNC_DEF("toString", 1, qjs_buffer_prototype_to_string), + JS_CFUNC_DEF("write", 4, qjs_buffer_prototype_write), + JS_CFUNC_MAGIC_DEF("writeInt8", 1, qjs_buffer_prototype_write_int, + qjs_buffer_magic(1, 1, 1)), + JS_CFUNC_MAGIC_DEF("writeUInt8", 1, qjs_buffer_prototype_write_int, + qjs_buffer_magic(1, 0, 1)), + JS_CFUNC_MAGIC_DEF("writeInt16LE", 1, qjs_buffer_prototype_write_int, + qjs_buffer_magic(2, 1, 1)), + JS_CFUNC_MAGIC_DEF("writeUInt16LE", 1, qjs_buffer_prototype_write_int, + qjs_buffer_magic(2, 0, 1)), + JS_CFUNC_MAGIC_DEF("writeInt16BE", 1, qjs_buffer_prototype_write_int, + qjs_buffer_magic(2, 1, 0)), + JS_CFUNC_MAGIC_DEF("writeUInt16BE", 1, qjs_buffer_prototype_write_int, + qjs_buffer_magic(2, 0, 0)), + JS_CFUNC_MAGIC_DEF("writeInt32LE", 1, qjs_buffer_prototype_write_int, + qjs_buffer_magic(4, 1, 1)), + JS_CFUNC_MAGIC_DEF("writeUInt32LE", 1, qjs_buffer_prototype_write_int, + qjs_buffer_magic(4, 0, 1)), + JS_CFUNC_MAGIC_DEF("writeInt32BE", 1, qjs_buffer_prototype_write_int, + qjs_buffer_magic(4, 1, 0)), + JS_CFUNC_MAGIC_DEF("writeUInt32BE", 1, qjs_buffer_prototype_write_int, + qjs_buffer_magic(4, 0, 0)), + JS_CFUNC_MAGIC_DEF("writeIntLE", 2, qjs_buffer_prototype_write_int, + qjs_buffer_magic(0, 1, 1)), + JS_CFUNC_MAGIC_DEF("writeUIntLE", 2, qjs_buffer_prototype_write_int, + qjs_buffer_magic(0, 0, 1)), + JS_CFUNC_MAGIC_DEF("writeIntBE", 2, qjs_buffer_prototype_write_int, + qjs_buffer_magic(0, 1, 0)), + JS_CFUNC_MAGIC_DEF("writeUIntBE", 2, qjs_buffer_prototype_write_int, + qjs_buffer_magic(0, 0, 0)), + JS_CFUNC_MAGIC_DEF("writeFloatLE", 2, qjs_buffer_prototype_write_float, + qjs_buffer_magic(4, 1, 1)), + JS_CFUNC_MAGIC_DEF("writeFloatBE", 2, qjs_buffer_prototype_write_float, + qjs_buffer_magic(4, 1, 0)), + JS_CFUNC_MAGIC_DEF("writeDoubleLE", 2, qjs_buffer_prototype_write_float, + qjs_buffer_magic(8, 1, 1)), + JS_CFUNC_MAGIC_DEF("writeDoubleBE", 2, qjs_buffer_prototype_write_float, + qjs_buffer_magic(8, 1, 0)), }; @@ -201,6 +343,288 @@ qjs_buffer(JSContext *ctx, JSValueConst this_val, int argc, } +static JSValue +qjs_buffer_ctor(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + JSValue ret, proto; + + ret = qjs_new_uint8_array(ctx, argc, argv); + if (JS_IsException(ret)) { + return ret; + } + + proto = JS_GetClassProto(ctx, qjs_buffer_class_id); + JS_SetPrototype(ctx, ret, proto); + JS_FreeValue(ctx, proto); + + return ret; +} + + +static JSValue +qjs_bufferobj_alloc(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv, int ignored) +{ + JSValue buffer, ret; + uint32_t size; + + if (!JS_IsNumber(argv[0])) { + return JS_ThrowTypeError(ctx, "The \"size\" argument must be of type" + " number"); + } + + if (JS_ToUint32(ctx, &size, argv[0])) { + return JS_EXCEPTION; + } + + buffer = qjs_buffer_alloc(ctx, size); + if (JS_IsException(buffer)) { + return buffer; + } + + if (!JS_IsUndefined(argv[1])) { + ret = qjs_buffer_fill(ctx, buffer, argv[1], argv[2], 0, size); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, buffer); + return ret; + } + } + + return buffer; +} + + +static JSValue +qjs_buffer_byte_length(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + size_t size; + JSValue ret; + njs_str_t src; + const qjs_buffer_encoding_t *encoding; + + if (JS_GetArrayBuffer(ctx, &size, argv[0]) != NULL) { + return JS_NewInt32(ctx, size); + } + + ret = JS_GetTypedArrayBuffer(ctx, argv[0], NULL, &size, NULL); + if (!JS_IsException(ret)) { + JS_FreeValue(ctx, ret); + return JS_NewInt32(ctx, size); + } + + if (!JS_IsString(argv[0])) { + return JS_ThrowTypeError(ctx, "first argument is not a string " + "or Buffer-like object"); + } + + encoding = qjs_buffer_encoding(ctx, argv[1], 1); + if (encoding == NULL) { + return JS_EXCEPTION; + } + + src.start = (u_char *) JS_ToCStringLen(ctx, &src.length, argv[0]); + + if (encoding->decode_length != NULL) { + size = encoding->decode_length(ctx, &src); + + } else { + size = src.length; + } + + JS_FreeCString(ctx, (char *) src.start); + + return JS_NewInt32(ctx, size); +} + + +static JSValue +qjs_buffer_compare(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + return qjs_buffer_compare_array(ctx, argv[0], argv[1], argv[2], argv[3], + argv[4], argv[5]); +} + + +static JSValue +qjs_buffer_concat(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + u_char *p; + size_t n; + JSValue list, length, val, ret, buffer; + uint32_t i, len, list_len; + njs_str_t buf, dst; + + list = argv[0]; + + if (!JS_IsArray(ctx, list)) { + return JS_ThrowTypeError(ctx, + "\"list\" argument must be an instance of Array"); + } + + length = JS_GetPropertyStr(ctx, list, "length"); + if (JS_IsException(length)) { + return JS_EXCEPTION; + } + + len = 0; + if (JS_ToUint32(ctx, &list_len, length)) { + JS_FreeValue(ctx, length); + return JS_EXCEPTION; + } + + JS_FreeValue(ctx, length); + + if (JS_IsUndefined(argv[1])) { + for (i = 0; i < list_len; i++) { + val = JS_GetPropertyUint32(ctx, list, i); + if (JS_IsException(val)) { + return JS_EXCEPTION; + } + + ret = qjs_typed_array_data(ctx, val, &buf); + JS_FreeValue(ctx, val); + if (JS_IsException(ret)) { + return JS_ThrowTypeError(ctx, "\"list[%d]\" argument must be an" + " instance of Buffer or Uint8Array", i); + } + + if ((SIZE_MAX - len) < buf.length) { + return JS_ThrowTypeError(ctx, + "Total size of buffers is too large"); + } + + len += buf.length; + } + + } else { + if (JS_ToUint32(ctx, &len, argv[1])) { + return JS_EXCEPTION; + } + } + + buffer = qjs_buffer_alloc(ctx, len); + if (JS_IsException(buffer)) { + return JS_EXCEPTION; + } + + ret = qjs_typed_array_data(ctx, buffer, &dst); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, buffer); + return JS_EXCEPTION; + } + + p = dst.start; + + for (i = 0; len != 0 && i < list_len; i++) { + val = JS_GetPropertyUint32(ctx, list, i); + if (JS_IsException(val)) { + JS_FreeValue(ctx, buffer); + return JS_EXCEPTION; + } + + ret = qjs_typed_array_data(ctx, val, &buf); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, buffer); + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + + JS_FreeValue(ctx, val); + + n = njs_min((size_t) len, buf.length); + p = njs_cpymem(p, buf.start, n); + + len -= n; + } + + if (len != 0) { + njs_memzero(p, len); + } + + return buffer; +} + + +static JSValue +qjs_buffer_fill(JSContext *ctx, JSValueConst buffer, JSValueConst fill, + JSValueConst encode, uint64_t offset, uint64_t end) +{ + JSValue ret, fill_buf; + uint32_t n; + njs_str_t dst, src; + + ret = qjs_typed_array_data(ctx, buffer, &dst); + if (JS_IsException(ret)) { + return ret; + } + + if (end > dst.length) { + return JS_ThrowRangeError(ctx, "\"end\" is out of range"); + } + + if (offset >= end) { + return buffer; + } + + if (JS_IsNumber(fill)) { + if (JS_ToUint32(ctx, &n, fill)) { + return JS_EXCEPTION; + } + + memset(dst.start + offset, n & 0xff, end - offset); + return buffer; + } + + fill_buf = JS_UNDEFINED; + + if (JS_IsString(fill)) { + fill_buf = qjs_buffer_from_string(ctx, fill, encode); + if (JS_IsException(fill_buf)) { + return fill_buf; + } + + fill = fill_buf; + } + + ret = qjs_typed_array_data(ctx, fill, &src); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, fill_buf); + return ret; + } + + if (src.length == 0) { + memset(dst.start + offset, 0, end - offset); + JS_FreeValue(ctx, fill_buf); + return buffer; + } + + if (src.start >= (dst.start + dst.length) + || dst.start >= (dst.start + dst.length)) + { + while (offset < end) { + n = njs_min(src.length, end - offset); + memcpy(dst.start + offset, src.start, n); + offset += n; + } + + } else { + while (offset < end) { + n = njs_min(src.length, end - offset); + memmove(dst.start + offset, src.start, n); + offset += n; + } + } + + JS_FreeValue(ctx, fill_buf); + + return buffer; +} + + static JSValue qjs_buffer_from(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -230,188 +654,1223 @@ qjs_buffer_from(JSContext *ctx, JSValueConst this_val, int argc, return ctor; } - name = JS_GetPropertyStr(ctx, ctor, "name"); - if (JS_IsException(name)) { - JS_FreeValue(ctx, ret); - return name; - } + name = JS_GetPropertyStr(ctx, ctor, "name"); + if (JS_IsException(name)) { + JS_FreeValue(ctx, ret); + return name; + } + + JS_FreeValue(ctx, ctor); + str = JS_ToCString(ctx, name); + + if (strncmp(str, "Float32Array", 12) == 0) { + float32 = 1; + } + + JS_FreeCString(ctx, str); + JS_FreeValue(ctx, name); + } + + return qjs_buffer_from_typed_array(ctx, ret, off, size, bytes, float32); + + } else if ((buf = JS_GetArrayBuffer(ctx, &size, argv[0])) != NULL) { + return qjs_buffer_ctor(ctx, JS_UNDEFINED, argc, argv); + + } else if (JS_IsObject(argv[0])) { + obj = argv[0]; + valueOf = JS_GetPropertyStr(ctx, obj, "valueOf"); + if (JS_IsException(valueOf)) { + return valueOf; + } + + if (JS_IsFunction(ctx, valueOf)) { + ret = JS_Call(ctx, valueOf, obj, 0, NULL); + JS_FreeValue(ctx, valueOf); + if (JS_IsException(ret)) { + return ret; + } + + if (JS_IsString(ret)) { + obj = ret; + ret = qjs_buffer_from_string(ctx, obj, argv[1]); + JS_FreeValue(ctx, obj); + return ret; + } + + if (JS_IsObject(ret) + && JS_VALUE_GET_PTR(ret) != JS_VALUE_GET_PTR(obj)) + { + obj = ret; + ret = qjs_buffer_from_object(ctx, obj); + JS_FreeValue(ctx, obj); + return ret; + } + + JS_FreeValue(ctx, ret); + } + + return qjs_buffer_from_object(ctx, obj); + } + + JS_ThrowTypeError(ctx, "first argument is not a string " + "or Buffer-like object"); + return JS_EXCEPTION; +} + + +static JSValue +qjs_buffer_is_buffer(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue proto, buffer_proto, ret; + + proto = JS_GetPrototype(ctx, argv[0]); + buffer_proto = JS_GetClassProto(ctx, qjs_buffer_class_id); + + ret = JS_NewBool(ctx, JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT && + JS_VALUE_GET_OBJ(buffer_proto) == JS_VALUE_GET_OBJ(proto)); + + JS_FreeValue(ctx, buffer_proto); + JS_FreeValue(ctx, proto); + + return ret; +} + + +static JSValue +qjs_buffer_is_encoding(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_NewBool(ctx, qjs_buffer_encoding(ctx, argv[0], 0) != NULL); +} + + +static JSValue +qjs_buffer_array_range(JSContext *ctx, njs_str_t *array, JSValueConst start, + JSValueConst end, const char *name) +{ + int64_t num_start, num_end; + + num_start = 0; + + if (!JS_IsUndefined(start)) { + if (JS_ToInt64(ctx, &num_start, start)) { + return JS_EXCEPTION; + } + } + + if (num_start < 0 || (size_t) num_start > array->length) { + return JS_ThrowRangeError(ctx, "\"%sStart\" is out of range: %ld", + name, num_start); + } + + num_end = array->length; + + if (!JS_IsUndefined(end)) { + if (JS_ToInt64(ctx, &num_end, end)) { + return JS_EXCEPTION; + } + } + + if (num_end < 0 || (size_t) num_end > array->length) { + return JS_ThrowRangeError(ctx, "\"%sEnd\" is out of range: %ld", + name, num_end); + } + + if (num_start > num_end) { + num_end = num_start; + } + + array->start += num_start; + array->length = num_end - num_start; + + return JS_UNDEFINED; +} + + +static JSValue +qjs_buffer_compare_array(JSContext *ctx, JSValue val1, JSValue val2, + JSValueConst target_start, JSValueConst target_end, + JSValueConst source_start, JSValueConst source_end) +{ + int rc; + size_t size; + JSValue ret; + njs_str_t src, target; + + ret = qjs_typed_array_data(ctx, val1, &src); + if (JS_IsException(ret)) { + return ret; + } + + ret = qjs_typed_array_data(ctx, val2, &target); + if (JS_IsException(ret)) { + return ret; + } + + ret = qjs_buffer_array_range(ctx, &src, source_start, source_end, "source"); + if (JS_IsException(ret)) { + return ret; + } + + ret = qjs_buffer_array_range(ctx, &target, target_start, target_end, + "target"); + if (JS_IsException(ret)) { + return ret; + } + + size = njs_min(src.length, target.length); + + rc = memcmp(src.start, target.start, size); + + if (rc != 0) { + return JS_NewInt32(ctx, (rc < 0) ? -1 : 1); + } + + if (target.length > src.length) { + rc = -1; + + } else if (target.length < src.length) { + rc = 1; + } + + return JS_NewInt32(ctx, rc); +} + + +static JSValue +qjs_buffer_prototype_compare(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + return qjs_buffer_compare_array(ctx, this_val, argv[0], argv[1], argv[2], + argv[3], argv[4]); +} + + +static JSValue +qjs_buffer_prototype_copy(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + size_t size; + JSValue ret; + njs_str_t src, target; + + ret = qjs_typed_array_data(ctx, this_val, &src); + if (JS_IsException(ret)) { + return ret; + } + + ret = qjs_typed_array_data(ctx, argv[0], &target); + if (JS_IsException(ret)) { + return ret; + } + + ret = qjs_buffer_array_range(ctx, &target, argv[1], JS_UNDEFINED, "target"); + if (JS_IsException(ret)) { + return ret; + } + + ret = qjs_buffer_array_range(ctx, &src, argv[2], argv[3], "source"); + if (JS_IsException(ret)) { + return ret; + } + + size = njs_min(src.length, target.length); + + if (src.start >= (target.start + size) + || target.start >= (src.start + size)) + { + memcpy(target.start, src.start, size); + + } else { + memmove(target.start, src.start, size); + } + + return JS_NewInt32(ctx, size); +} + + +static JSValue +qjs_buffer_prototype_equals(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + JSValue ret; + + ret = qjs_buffer_compare_array(ctx, this_val, argv[0], JS_UNDEFINED, + JS_UNDEFINED, JS_UNDEFINED, JS_UNDEFINED); + if (JS_IsException(ret)) { + return ret; + } + + return JS_NewBool(ctx, JS_VALUE_GET_INT(ret) == 0); +} + + +static JSValue +qjs_buffer_prototype_fill(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + JSValue ret, encode; + uint64_t offset, end; + njs_str_t dst; + + offset = 0; + encode = argv[3]; + + ret = qjs_typed_array_data(ctx, this_val, &dst); + if (JS_IsException(ret)) { + return ret; + } + + end = dst.length; + + if (!JS_IsUndefined(argv[1])) { + if (JS_IsString(argv[0]) && JS_IsString(argv[1])) { + encode = argv[1]; + goto fill; + } + + if (JS_ToIndex(ctx, &offset, argv[1])) { + return JS_EXCEPTION; + } + } + + if (!JS_IsUndefined(argv[2])) { + if (JS_IsString(argv[0]) && JS_IsString(argv[2])) { + encode = argv[2]; + goto fill; + } + + if (JS_ToIndex(ctx, &end, argv[2])) { + return JS_EXCEPTION; + } + } + +fill: + + ret = qjs_buffer_fill(ctx, this_val, argv[0], encode, offset, end); + if (JS_IsException(ret)) { + return ret; + } + + JS_DupValue(ctx, ret); + + return ret; +} + + +static JSValue +qjs_buffer_prototype_includes(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + JSValue ret; + + ret = qjs_buffer_prototype_index_of(ctx, this_val, argc, argv, 0); + if (JS_IsException(ret)) { + return ret; + } + + return JS_NewBool(ctx, JS_VALUE_GET_INT(ret) != -1); +} + + +static JSValue +qjs_buffer_prototype_index_of(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv, int last) +{ + JSValue ret, buffer, encode, value; + int64_t from, to, increment, length, i; + uint32_t byte; + njs_str_t self, str; + const qjs_buffer_encoding_t *encoding; + + ret = qjs_typed_array_data(ctx, this_val, &self); + if (JS_IsException(ret)) { + return ret; + } + + length = self.length; + + if (length == 0) { + return JS_NewInt32(ctx, -1); + } + + if (last) { + from = length - 1; + to = -1; + increment = -1; + + } else { + from = 0; + to = length; + increment = 1; + } + + encode = argv[2]; + + if (!JS_IsUndefined(argv[1])) { + if (JS_IsString(argv[0]) && JS_IsString(argv[1])) { + encode = argv[1]; + goto encoding; + } + + if (JS_ToInt64(ctx, &from, argv[1])) { + return JS_EXCEPTION; + } + + if (last) { + if (from >= 0) { + from = njs_min(from, length - 1); + + } else if (from < 0) { + from += length; + } + + if (from <= to) { + return JS_NewInt32(ctx, -1); + } + + } else { + if (from < 0) { + from += length; + + if (from < 0) { + from = 0; + } + } + + if (from >= to) { + return JS_NewInt32(ctx, -1); + } + } + } + + if (JS_IsNumber(argv[0])) { + if (JS_ToUint32(ctx, &byte, argv[0])) { + return JS_EXCEPTION; + } + + for (i = from; i != to; i += increment) { + if (self.start[i] == (uint8_t) byte) { + return JS_NewInt32(ctx, i); + } + } + + return JS_NewInt32(ctx, -1); + } + +encoding: + + buffer = JS_UNDEFINED; + value = argv[0]; + + if (JS_IsString(value)) { + encoding = qjs_buffer_encoding(ctx, encode, 1); + if (encoding == NULL) { + return JS_EXCEPTION; + } + + buffer = qjs_buffer_from_string(ctx, value, encode); + if (JS_IsException(buffer)) { + return buffer; + } + + value = buffer; + } + + ret = qjs_typed_array_data(ctx, value, &str); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, buffer); + return JS_ThrowTypeError(ctx, "\"value\" argument is not a string " + "or Buffer-like object"); + } + + if (str.length == 0) { + JS_FreeValue(ctx, buffer); + return JS_NewInt32(ctx, (last) ? length : 0); + } + + if (str.length > (size_t) length) { + JS_FreeValue(ctx, buffer); + return JS_NewInt32(ctx, -1); + } + + if (last) { + from -= str.length - 1; + from = njs_max(from, 0); + + } else { + to -= str.length - 1; + to = njs_min(to, length); + } + + for (i = from; i != to; i += increment) { + if (memcmp(&self.start[i], str.start, str.length) == 0) { + JS_FreeValue(ctx, buffer); + return JS_NewInt32(ctx, i); + } + } + + JS_FreeValue(ctx, buffer); + return JS_NewInt32(ctx, -1); +} + + +static JSValue +qjs_buffer_prototype_read_float(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + double v; + JSValue ret; + uint32_t u32; + uint64_t u64, index, size; + njs_str_t self; + njs_bool_t little, swap; + njs_conv_f32_t conv_f32; + njs_conv_f64_t conv_f64; + + ret = qjs_typed_array_data(ctx, this_val, &self); + if (JS_IsException(ret)) { + return ret; + } + + if (JS_ToIndex(ctx, &index, argv[0])) { + return JS_EXCEPTION; + } + + size = magic >> 2; + + if (size + index > self.length) { + return JS_ThrowRangeError(ctx, "index %lu is outside the bound of the" + " buffer", index); + } + + little = magic & 1; + swap = little; + +#if NJS_HAVE_LITTLE_ENDIAN + swap = !swap; +#endif + + switch (size) { + case 4: + u32 = *((uint32_t *) &self.start[index]); + + if (swap) { + u32 = njs_bswap_u32(u32); + } + + conv_f32.u = u32; + v = conv_f32.f; + break; + + case 8: + default: + u64 = *((uint64_t *) &self.start[index]); + + if (swap) { + u64 = njs_bswap_u64(u64); + } + + conv_f64.u = u64; + v = conv_f64.f; + break; + } + + return JS_NewFloat64(ctx, v); +} + + +static JSValue +qjs_buffer_prototype_read_int(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue ret; + uint32_t u32; + uint64_t u64, index, size; + njs_str_t self; + njs_bool_t little, swap, sign; + + ret = qjs_typed_array_data(ctx, this_val, &self); + if (JS_IsException(ret)) { + return ret; + } + + if (JS_ToIndex(ctx, &index, argv[0])) { + return JS_EXCEPTION; + } + + size = magic >> 2; + + if (!size) { + if (!JS_IsNumber(argv[1])) { + return JS_ThrowTypeError(ctx, "\"byteLength\" is not a number"); + } + + if (JS_ToIndex(ctx, &size, argv[1])) { + return JS_EXCEPTION; + } + + if (size > 6) { + return JS_ThrowRangeError(ctx, "\"byteLength\" must be <= 6"); + } + } + + if (size + index > self.length) { + return JS_ThrowRangeError(ctx, "index %lu is outside the bound of the" + " buffer", index); + } + + sign = (magic >> 1) & 1; + little = magic & 1; + swap = little; + +#if NJS_HAVE_LITTLE_ENDIAN + swap = !swap; +#endif + + switch (size) { + case 1: + if (sign) { + return JS_NewInt32(ctx, (int8_t) self.start[index]); + } + + return JS_NewUint32(ctx, self.start[index]); + + case 2: + u32 = njs_get_u16(&self.start[index]); + + if (swap) { + u32 = njs_bswap_u16(u32); + } + + if (sign) { + /* Sign extension. */ + u32 |= (u32 & (INT16_MAX + 1ULL)) * UINT32_MAX; + + return JS_NewInt32(ctx, (int16_t) u32); + } + + return JS_NewUint32(ctx, u32); + + case 3: + + if (little) { + u32 = (self.start[index + 2] << 16) + | (self.start[index + 1] << 8) + | self.start[index]; + + } else { + u32 = (self.start[index] << 16) + | (self.start[index + 1] << 8) + | self.start[index + 2]; + } + + if (sign) { + /* Sign extension. */ + u32 |= (u32 & (INT24_MAX + 1ULL)) * UINT32_MAX; + + return JS_NewInt32(ctx, (int32_t) u32); + } + + return JS_NewUint32(ctx, u32); + + case 4: + u32 = njs_get_u32(&self.start[index]); + + if (swap) { + u32 = njs_bswap_u32(u32); + } + + if (sign) { + /* Sign extension. */ + u32 |= (u32 & (INT32_MAX + 1ULL)) * UINT32_MAX; + + return JS_NewInt32(ctx, (int32_t) u32); + } + + return JS_NewUint32(ctx, u32); + + case 5: + if (little) { + u64 = ((uint64_t) self.start[index + 4] << 32) + | ((uint64_t) self.start[index + 3] << 24) + | (self.start[index + 2] << 16) + | (self.start[index + 1] << 8) + | self.start[index]; + + } else { + u64 = ((uint64_t) self.start[index] << 32) + | ((uint64_t) self.start[index + 1] << 24) + | (self.start[index + 2] << 16) + | (self.start[index + 3] << 8) + | self.start[index + 4]; + } + + if (sign) { + /* Sign extension. */ + u64 |= (u64 & (INT40_MAX + 1ULL)) * UINT64_MAX; + + return JS_NewFloat64(ctx, (int64_t) u64); + } + + return JS_NewFloat64(ctx, u64); + + case 6: + default: + if (little) { + u64 = ((uint64_t) self.start[index + 5] << 40) + | ((uint64_t) self.start[index + 4] << 32) + | ((uint64_t) self.start[index + 3] << 24) + | (self.start[index + 2] << 16) + | (self.start[index + 1] << 8) + | self.start[index]; + + } else { + u64 = ((uint64_t) self.start[index] << 40) + | ((uint64_t) self.start[index + 1] << 32) + | ((uint64_t) self.start[index + 2] << 24) + | (self.start[index + 3] << 16) + | (self.start[index + 4] << 8) + | self.start[index + 5]; + } + + if (sign) { + /* Sign extension. */ + u64 |= (u64 & (INT48_MAX + 1ULL)) * UINT64_MAX; + + return JS_NewFloat64(ctx, (int64_t) u64); + } + + return JS_NewFloat64(ctx, u64); + } +} + + +static JSValue +qjs_buffer_prototype_to_json(JSContext *ctx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + int rc; + JSValue obj, data, ret; + njs_str_t src; + njs_uint_t i; + + ret = qjs_typed_array_data(ctx, this_val, &src); + if (JS_IsException(ret)) { + return ret; + } + + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + return obj; + } + + data = JS_NewArray(ctx); + if (JS_IsException(data)) { + JS_FreeValue(ctx, obj); + return data; + } + + rc = JS_DefinePropertyValueStr(ctx, obj, "type", + JS_NewString(ctx, "Buffer"), + JS_PROP_ENUMERABLE); + if (rc == -1) { + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, data); + return ret; + } + + rc = JS_DefinePropertyValueStr(ctx, obj, "data", data, JS_PROP_ENUMERABLE); + if (rc == -1) { + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, data); + return ret; + } + + for (i = 0; i < src.length; i++) { + rc = JS_SetPropertyUint32(ctx, data, i, JS_NewInt32(ctx, src.start[i])); + if (rc == -1) { + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, data); + return ret; + } + } + + return obj; +} + + +static JSValue +qjs_buffer_prototype_swap(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int size) +{ + uint8_t *p, *end; + JSValue ret; + njs_str_t self; + + ret = qjs_typed_array_data(ctx, this_val, &self); + if (JS_IsException(ret)) { + return ret; + } + + if ((self.length % size) != 0) { + return JS_ThrowRangeError(ctx, "Buffer size must be a multiple " + "of %d-bits", (int) (size << 3)); + } + + p = self.start; + end = p + self.length; + + switch (size) { + case 2: + for (; p < end; p += 2) { + njs_set_u16(p, njs_bswap_u16(njs_get_u16(p))); + } + + break; + + case 4: + for (; p < end; p += 4) { + njs_set_u32(p, njs_bswap_u32(njs_get_u32(p))); + } + + break; + + case 8: + default: + for (; p < end; p += 8) { + njs_set_u64(p, njs_bswap_u64(njs_get_u64(p))); + } + } + + JS_DupValue(ctx, this_val); + + return this_val; +} + + +static JSValue +qjs_buffer_prototype_to_string(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue ret; + njs_str_t src, data; + const qjs_buffer_encoding_t *encoding; + + ret = qjs_typed_array_data(ctx, this_val, &src); + if (JS_IsException(ret)) { + return JS_ThrowTypeError(ctx, "method toString() called on incompatible" + " object"); + } + + if (JS_IsUndefined(argv[0]) || src.length == 0) { + return JS_NewStringLen(ctx, (char *) src.start, src.length); + } + + encoding = qjs_buffer_encoding(ctx, argv[0], 1); + if (njs_slow_path(encoding == NULL)) { + return JS_EXCEPTION; + } + + if (encoding->encode_length == NULL) { + return JS_NewStringLen(ctx, (char *) src.start, src.length); + } + + data.length = encoding->encode_length(ctx, &src); + data.start = js_malloc(ctx, data.length); + if (njs_slow_path(data.start == NULL)) { + JS_ThrowOutOfMemory(ctx); + return JS_EXCEPTION; + } + + if (encoding->encode(ctx, &src, &data) != 0) { + js_free(ctx, data.start); + JS_ThrowTypeError(ctx, "failed to encode buffer"); + return JS_EXCEPTION; + } + + ret = JS_NewStringLen(ctx, (char *) data.start, data.length); + + js_free(ctx, data.start); + + return ret; +} + + +static JSValue +qjs_buffer_prototype_write(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue ret, buffer, encode; + uint64_t offset, max_length; + njs_str_t self, src; + const uint8_t *p, *end, *prev; + const qjs_buffer_encoding_t *encoding; + + ret = qjs_typed_array_data(ctx, this_val, &self); + if (JS_IsException(ret)) { + return ret; + } + + offset = 0; + max_length = self.length; + encode = argv[3]; + + if (!JS_IsUndefined(argv[1])) { + if (JS_IsString(argv[0]) && JS_IsString(argv[1])) { + encode = argv[1]; + goto write; + } + + if (JS_ToIndex(ctx, &offset, argv[1])) { + return JS_EXCEPTION; + } + + max_length = self.length - offset; + } + + if (!JS_IsUndefined(argv[2])) { + if (JS_IsString(argv[0]) && JS_IsString(argv[2])) { + encode = argv[2]; + goto write; + } + + if (JS_ToIndex(ctx, &max_length, argv[2])) { + return JS_EXCEPTION; + } + } + +write: + + encoding = qjs_buffer_encoding(ctx, encode, 1); + if (encoding == NULL) { + return JS_EXCEPTION; + } + + buffer = qjs_buffer_from_string(ctx, argv[0], encode); + if (JS_IsException(buffer)) { + return buffer; + } + + (void) qjs_typed_array_data(ctx, buffer, &src); + + if (offset > self.length) { + JS_FreeValue(ctx, buffer); + return JS_ThrowRangeError(ctx, "\"offset\" is out of range"); + } + + if (src.length == 0) { + JS_FreeValue(ctx, buffer); + return JS_NewInt32(ctx, 0); + } + + if (max_length > self.length - offset) { + JS_FreeValue(ctx, buffer); + return JS_ThrowRangeError(ctx, "\"length\" is out of range"); + } + + max_length = njs_min(max_length, src.length); + + if (encoding->decode == NULL) { + /* Avoid writing incomplete UTF-8 characters. */ + p = prev = src.start; + end = p + max_length; + + while (p < end) { + p = njs_utf8_next(p, src.start + src.length); + if (p <= end) { + prev = p; + } + } + + max_length = prev - src.start; + } + + memcpy(&self.start[offset], src.start, max_length); + + JS_FreeValue(ctx, buffer); + + return JS_NewInt32(ctx, max_length); +} + + +static JSValue +qjs_buffer_prototype_write_int(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue ret; + int64_t i64; + uint32_t u32; + uint64_t index, size; + njs_str_t self; + njs_bool_t little, swap, sign; + + ret = qjs_typed_array_data(ctx, this_val, &self); + if (JS_IsException(ret)) { + return ret; + } + + if (JS_ToIndex(ctx, &index, argv[1])) { + return JS_EXCEPTION; + } + + size = magic >> 2; + + if (!size) { + if (JS_ToIndex(ctx, &size, argv[2])) { + return JS_EXCEPTION; + } + + if (size > 6) { + return JS_ThrowRangeError(ctx, "\"byteLength\" must be <= 6"); + } + } + + if (size + index > self.length) { + return JS_ThrowRangeError(ctx, "index %lu is outside the bound of the" + " buffer", index); + } + + little = magic & 1; + sign = (magic >> 1) & 1; + swap = little; + +#if NJS_HAVE_LITTLE_ENDIAN + swap = !swap; +#endif - JS_FreeValue(ctx, ctor); - str = JS_ToCString(ctx, name); + if (JS_ToInt64(ctx, &i64, argv[0])) { + return JS_EXCEPTION; + } - if (strncmp(str, "Float32Array", 12) == 0) { - float32 = 1; + switch (size) { + case 1: + if (sign) { + if (i64 < INT8_MIN || i64 > INT8_MAX) { + return JS_ThrowRangeError(ctx, "value is outside the range of" + " representable values"); } - JS_FreeCString(ctx, str); - JS_FreeValue(ctx, name); + } else { + if (i64 < 0 || i64 > UINT8_MAX) { + return JS_ThrowRangeError(ctx, "value is outside the range of" + " representable values"); + } } - return qjs_buffer_from_typed_array(ctx, ret, off, size, bytes, float32); - - } else if ((buf = JS_GetArrayBuffer(ctx, &size, argv[0])) != NULL) { - return qjs_buffer_from_array_buffer(ctx, buf, size, argv[1], argv[2]); + self.start[index] = (uint8_t) i64; + break; - } else if (JS_IsObject(argv[0])) { - obj = argv[0]; - valueOf = JS_GetPropertyStr(ctx, obj, "valueOf"); - if (JS_IsException(valueOf)) { - return valueOf; - } + case 2: + u32 = (uint16_t) i64; - if (JS_IsFunction(ctx, valueOf)) { - ret = JS_Call(ctx, valueOf, obj, 0, NULL); - JS_FreeValue(ctx, valueOf); - if (JS_IsException(ret)) { - return ret; + if (sign) { + if (i64 < INT16_MIN || i64 > INT16_MAX) { + return JS_ThrowRangeError(ctx, "value is outside the range of" + " representable values"); } - if (JS_IsString(ret)) { - obj = ret; - ret = qjs_buffer_from_string(ctx, obj, argv[1]); - JS_FreeValue(ctx, obj); - return ret; + } else { + if (i64 < 0 || i64 > UINT16_MAX) { + return JS_ThrowRangeError(ctx, "value is outside the range of" + " representable values"); } + } - if (JS_IsObject(ret) - && JS_VALUE_GET_PTR(ret) != JS_VALUE_GET_PTR(obj)) - { - obj = ret; - ret = qjs_buffer_from_object(ctx, obj); - JS_FreeValue(ctx, obj); - return ret; + if (swap) { + u32 = njs_bswap_u16(u32); + } + + njs_set_u16(&self.start[index], u32); + break; + + case 3: + if (sign) { + if (i64 < INT24_MIN || i64 > INT24_MAX) { + return JS_ThrowRangeError(ctx, "value is outside the range of" + " representable values"); } - JS_FreeValue(ctx, ret); + } else { + if (i64 < 0 || i64 > UINT24_MAX) { + return JS_ThrowRangeError(ctx, "value is outside the range of" + " representable values"); + } } - return qjs_buffer_from_object(ctx, obj); - } + if (little) { + self.start[index] = i64; i64 >>= 8; + self.start[index + 1] = i64; i64 >>= 8; + self.start[index + 2] = i64; - JS_ThrowTypeError(ctx, "first argument is not a string " - "or Buffer-like object"); - return JS_EXCEPTION; -} + } else { + self.start[index + 2] = i64; i64 >>= 8; + self.start[index + 1] = i64; i64 >>= 8; + self.start[index] = i64; + } + break; -static JSValue -qjs_buffer_is_buffer(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue proto, buffer_proto, ret; + case 4: + u32 = i64; - proto = JS_GetPrototype(ctx, argv[0]); - buffer_proto = JS_GetClassProto(ctx, qjs_buffer_class_id); + if (sign) { + if (i64 < INT32_MIN || i64 > INT32_MAX) { + return JS_ThrowRangeError(ctx, "value is outside the range of" + " representable values"); + } - ret = JS_NewBool(ctx, JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT && - JS_VALUE_GET_OBJ(buffer_proto) == JS_VALUE_GET_OBJ(proto)); + } else { + if (i64 < 0 || i64 > UINT32_MAX) { + return JS_ThrowRangeError(ctx, "value is outside the range of" + " representable values"); + } + } - JS_FreeValue(ctx, buffer_proto); - JS_FreeValue(ctx, proto); + if (swap) { + u32 = njs_bswap_u32(u32); + } - return ret; -} + njs_set_u32(&self.start[index], u32); + break; + case 5: + if (sign) { + if (i64 < INT40_MIN || i64 > INT40_MAX) { + return JS_ThrowRangeError(ctx, "value is outside the range of" + " representable values"); + } -static JSValue -qjs_buffer_prototype_to_json(JSContext *ctx, JSValueConst this_val, int argc, - JSValueConst *argv) -{ - int rc; - JSValue obj, data, ret; - njs_str_t src; - njs_uint_t i; + } else { + if (i64 < 0 || i64 > UINT40_MAX) { + return JS_ThrowRangeError(ctx, "value is outside the range of" + " representable values"); + } + } - ret = qjs_typed_array_data(ctx, this_val, &src); - if (JS_IsException(ret)) { - return ret; - } + if (little) { + self.start[index] = i64; i64 >>= 8; + self.start[index + 1] = i64; i64 >>= 8; + self.start[index + 2] = i64; i64 >>= 8; + self.start[index + 3] = i64; i64 >>= 8; + self.start[index + 4] = i64; - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) { - return obj; - } + } else { + self.start[index + 4] = i64; i64 >>= 8; + self.start[index + 3] = i64; i64 >>= 8; + self.start[index + 2] = i64; i64 >>= 8; + self.start[index + 1] = i64; i64 >>= 8; + self.start[index] = i64; + } - data = JS_NewArray(ctx); - if (JS_IsException(data)) { - JS_FreeValue(ctx, obj); - return data; - } + break; - rc = JS_DefinePropertyValueStr(ctx, obj, "type", - JS_NewString(ctx, "Buffer"), - JS_PROP_ENUMERABLE); - if (rc == -1) { - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, data); - return ret; - } + case 6: + default: + if (sign) { + if (i64 < INT48_MIN || i64 > INT48_MAX) { + return JS_ThrowRangeError(ctx, "value is outside the range of" + " representable values"); + } - rc = JS_DefinePropertyValueStr(ctx, obj, "data", data, JS_PROP_ENUMERABLE); - if (rc == -1) { - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, data); - return ret; - } + } else { + if (i64 < 0 || i64 > UINT48_MAX) { + return JS_ThrowRangeError(ctx, "value is outside the range of" + " representable values"); + } + } - for (i = 0; i < src.length; i++) { - rc = JS_SetPropertyUint32(ctx, data, i, JS_NewInt32(ctx, src.start[i])); - if (rc == -1) { - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, data); - return ret; + if (little) { + self.start[index] = i64; i64 >>= 8; + self.start[index + 1] = i64; i64 >>= 8; + self.start[index + 2] = i64; i64 >>= 8; + self.start[index + 3] = i64; i64 >>= 8; + self.start[index + 4] = i64; i64 >>= 8; + self.start[index + 5] = i64; + + } else { + self.start[index + 5] = i64; i64 >>= 8; + self.start[index + 4] = i64; i64 >>= 8; + self.start[index + 3] = i64; i64 >>= 8; + self.start[index + 2] = i64; i64 >>= 8; + self.start[index + 1] = i64; i64 >>= 8; + self.start[index] = i64; } + + break; } - return obj; + return JS_NewInt32(ctx, size + index); } - static JSValue -qjs_buffer_prototype_to_string(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) +qjs_buffer_prototype_write_float(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) { - JSValue ret; - njs_str_t src, data; - const qjs_buffer_encoding_t *encoding; - - ret = qjs_typed_array_data(ctx, this_val, &src); + double v; + JSValue ret; + uint32_t u32; + uint64_t u64, index, size; + njs_str_t self; + njs_bool_t little, swap; + njs_conv_f32_t conv_f32; + njs_conv_f64_t conv_f64; + + ret = qjs_typed_array_data(ctx, this_val, &self); if (JS_IsException(ret)) { return ret; } - if (JS_IsUndefined(argv[0]) || src.length == 0) { - return JS_NewStringLen(ctx, (char *) src.start, src.length); + if (JS_ToFloat64(ctx, &v, argv[0])) { + return JS_EXCEPTION; } - encoding = qjs_buffer_encoding(ctx, argv[0], 1); - if (njs_slow_path(encoding == NULL)) { + if (JS_ToIndex(ctx, &index, argv[1])) { return JS_EXCEPTION; } - if (encoding->encode_length == NULL) { - return JS_NewStringLen(ctx, (char *) src.start, src.length); - } + size = magic >> 2; - data.length = encoding->encode_length(ctx, &src); - data.start = js_malloc(ctx, data.length); - if (njs_slow_path(data.start == NULL)) { - JS_ThrowOutOfMemory(ctx); - return JS_EXCEPTION; + if (size + index > self.length) { + return JS_ThrowRangeError(ctx, "index %lu is outside the bound of the" + " buffer", index); } - if (encoding->encode(ctx, &src, &data) != 0) { - js_free(ctx, data.start); - JS_ThrowTypeError(ctx, "failed to encode buffer"); - return JS_EXCEPTION; - } + little = magic & 1; + swap = little; - ret = JS_NewStringLen(ctx, (char *) data.start, data.length); +#if NJS_HAVE_LITTLE_ENDIAN + swap = !swap; +#endif - js_free(ctx, data.start); + switch (size) { + case 4: + conv_f32.f = (float) v; - return ret; + if (swap) { + conv_f32.u = njs_bswap_u32(conv_f32.u); + } + + u32 = conv_f32.u; + memcpy(&self.start[index], &u32, size); + break; + + case 8: + default: + conv_f64.f = v; + + if (swap) { + conv_f64.u = njs_bswap_u64(conv_f64.u); + } + + u64 = conv_f64.u; + memcpy(&self.start[index], &u64, size); + break; + } + + return JS_NewInt32(ctx, size + index); } @@ -424,6 +1883,11 @@ qjs_buffer_from_string(JSContext *ctx, JSValueConst str, njs_str_t src, dst; const qjs_buffer_encoding_t *encoding; + if (!JS_IsString(str)) { + JS_ThrowTypeError(ctx, "first argument is not a string"); + return JS_EXCEPTION; + } + encoding = qjs_buffer_encoding(ctx, enc, 1); if (njs_slow_path(encoding == NULL)) { return JS_EXCEPTION; @@ -552,57 +2016,6 @@ qjs_buffer_from_typed_array(JSContext *ctx, JSValueConst arr_buf, return buffer; } -static JSValue -qjs_buffer_from_array_buffer(JSContext *ctx, u_char *buf, size_t size, - JSValueConst offset, JSValueConst length) -{ - JSValue buffer, ret; - int64_t len; - uint64_t off; - njs_str_t dst; - - if (JS_ToIndex(ctx, &off, offset)) { - return JS_EXCEPTION; - } - - if ((size_t) off > size) { - JS_ThrowRangeError(ctx, "\"offset\" is outside of buffer bounds"); - return JS_EXCEPTION; - } - - if (JS_IsUndefined(length)) { - len = size - off; - - } else { - if (JS_ToInt64(ctx, &len, length)) { - return JS_EXCEPTION; - } - - if (len < 0) { - len = 0; - } - - if ((size_t) (off + len) > size) { - JS_ThrowRangeError(ctx, "\"length\" is outside of buffer bounds"); - return JS_EXCEPTION; - } - } - - buffer = qjs_buffer_alloc(ctx, len); - if (JS_IsException(buffer)) { - return buffer; - } - - ret = qjs_typed_array_data(ctx, buffer, &dst); - if (JS_IsException(ret)) { - return ret; - } - - memcpy(dst.start, buf + off, len); - - return buffer; -} - static JSValue qjs_buffer_from_object(JSContext *ctx, JSValueConst obj) @@ -872,7 +2285,7 @@ qjs_base64_decode_length(JSContext *ctx, const njs_str_t *src) static int qjs_base64url_encode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst) { - qjs_base64_encode_core(dst, src, qjs_basis64url_enc, 1); + qjs_base64_encode_core(dst, src, qjs_basis64url_enc, 0); return 0; } @@ -1004,9 +2417,11 @@ qjs_hex_encode_length(JSContext *ctx, const njs_str_t *src) JSValue qjs_buffer_alloc(JSContext *ctx, size_t size) { - JSValue ret, proto; + JSValue ret, proto, value; + + value = JS_NewInt64(ctx, size); - ret = qjs_new_uint8_array(ctx, size); + ret = qjs_new_uint8_array(ctx, 1, &value); if (JS_IsException(ret)) { return ret; } @@ -1019,6 +2434,29 @@ qjs_buffer_alloc(JSContext *ctx, size_t size) } + +JSValue +qjs_buffer_create(JSContext *ctx, u_char *start, size_t size) +{ + JSValue buffer, ret; + njs_str_t dst; + + buffer = qjs_buffer_alloc(ctx, size); + if (JS_IsException(buffer)) { + return buffer; + } + + ret = qjs_typed_array_data(ctx, buffer, &dst); + if (JS_IsException(ret)) { + return ret; + } + + memcpy(dst.start, start, size); + + return buffer; +} + + JSValue qjs_buffer_chb_alloc(JSContext *ctx, njs_chb_t *chain) { @@ -1047,24 +2485,20 @@ qjs_buffer_chb_alloc(JSContext *ctx, njs_chb_t *chain) static JSValue -qjs_new_uint8_array(JSContext *ctx, size_t size) +qjs_new_uint8_array(JSContext *ctx, int argc, JSValueConst *argv) { - JSValue ret, value; - - value = JS_NewInt64(ctx, size); + JSValue ret; #ifdef NJS_HAVE_QUICKJS_NEW_TYPED_ARRAY - ret = JS_NewTypedArray(ctx, 1, &value, JS_TYPED_ARRAY_UINT8); + ret = JS_NewTypedArray(ctx, argc, argv, JS_TYPED_ARRAY_UINT8); #else JSValue ctor; ctor = JS_GetClassProto(ctx, qjs_uint8_array_ctor_id); - ret = JS_CallConstructor(ctx, ctor, 1, &value); + ret = JS_CallConstructor(ctx, ctor, argc, argv); JS_FreeValue(ctx, ctor); #endif - JS_FreeValue(ctx, value); - return ret; } @@ -1073,18 +2507,19 @@ static int qjs_buffer_builtin_init(JSContext *ctx) { int rc; - JSValue global_obj, buffer, proto, ctor, ta, ta_proto; + JSAtom species_atom; + JSValue global_obj, buffer, proto, ctor, ta, ta_proto, symbol, species; JSClassID u8_ta_class_id; JS_NewClassID(&qjs_buffer_class_id); JS_NewClass(JS_GetRuntime(ctx), qjs_buffer_class_id, &qjs_buffer_class); + global_obj = JS_GetGlobalObject(ctx); + proto = JS_NewObject(ctx); JS_SetPropertyFunctionList(ctx, proto, qjs_buffer_proto, njs_nitems(qjs_buffer_proto)); - global_obj = JS_GetGlobalObject(ctx); - ctor = JS_GetPropertyStr(ctx, global_obj, "Uint8Array"); #ifndef NJS_HAVE_QUICKJS_NEW_TYPED_ARRAY @@ -1110,15 +2545,29 @@ qjs_buffer_builtin_init(JSContext *ctx) JS_SetClassProto(ctx, qjs_buffer_class_id, proto); - buffer = JS_NewCFunction2(ctx, qjs_buffer, "Buffer", 0, - JS_CFUNC_generic, 0); + buffer = JS_NewCFunction2(ctx, qjs_buffer, "Buffer", 3, + JS_CFUNC_constructor, 0); if (JS_IsException(buffer)) { return -1; } + JS_SetConstructor(ctx, buffer, proto); + JS_SetPropertyFunctionList(ctx, buffer, qjs_buffer_props, njs_nitems(qjs_buffer_props)); + symbol = JS_GetPropertyStr(ctx, global_obj, "Symbol"); + species = JS_GetPropertyStr(ctx, symbol, "species"); + JS_FreeValue(ctx, symbol); + species_atom = JS_ValueToAtom(ctx, species); + JS_FreeValue(ctx, species); + + ctor = JS_NewCFunction2(ctx, qjs_buffer_ctor, "Buffer species ctor", 3, + JS_CFUNC_constructor, 0); + + JS_SetProperty(ctx, buffer, species_atom, ctor); + JS_FreeAtom(ctx, species_atom); + rc = JS_SetPropertyStr(ctx, global_obj, "Buffer", buffer); if (rc == -1) { return -1; diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index c6979c132..10ee6c1ef 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -21015,817 +21015,6 @@ static njs_unit_test_t njs_querystring_module_test[] = }; -static njs_unit_test_t njs_buffer_module_test[] = -{ - { njs_str("new Buffer();"), - njs_str("TypeError: Buffer is not a constructor") }, - - { njs_str("var buf = Buffer.alloc();"), - njs_str("TypeError: \"size\" argument must be of type number") }, - - { njs_str("var buf = Buffer.alloc('best buffer');"), - njs_str("TypeError: \"size\" argument must be of type number") }, - - { njs_str("var buf = Buffer.alloc(-1);"), - njs_str("RangeError: invalid size") }, - - { njs_str("var buf = Buffer.alloc(4); njs.dump(buf)"), - njs_str("Buffer [0,0,0,0]") }, - - { njs_str("var buf = Buffer.alloc(4, 88); buf"), - njs_str("XXXX") }, - - { njs_str("var buf = Buffer.alloc(4, 945); njs.dump(buf)"), - njs_str("Buffer [177,177,177,177]") }, - - { njs_str("var buf = Buffer.alloc(4, -1); njs.dump(buf)"), - njs_str("Buffer [255,255,255,255]") }, - - { njs_str("var buf = Buffer.alloc(4, -1, 'utf-128'); njs.dump(buf)"), - njs_str("Buffer [255,255,255,255]") }, - - { njs_str("var buf = Buffer.alloc(10, 'α'); buf"), - njs_str("ααααα") }, - - { njs_str("var buf = Buffer.alloc(4, 'α'); njs.dump(buf)"), - njs_str("Buffer [206,177,206,177]") }, - - { njs_str("var buf = Buffer.alloc(2, 'ααααα'); njs.dump(buf)"), - njs_str("Buffer [206,177]") }, - - { njs_str("var buf = Buffer.alloc(1, 'α'); njs.dump(buf)"), - njs_str("Buffer [206]") }, - - { njs_str("var buf = Buffer.alloc(4, 'ZXZpbA==', 'base64'); buf"), - njs_str("evil") }, - - { njs_str("var buf = Buffer.alloc(8, 'ZXZpbA==', 'base64'); buf"), - njs_str("evilevil") }, - - { njs_str("var buf = Buffer.alloc(8, 'evil', 'utf-128'); buf"), - njs_str("TypeError: \"utf-128\" encoding is not supported") }, - - { njs_str("var foo = new Uint8Array(10).fill(88);" - "var buf = Buffer.alloc(8, foo); buf"), - njs_str("XXXXXXXX") }, - - { njs_str("[1,2,10,20].every(v => {" - " var src = new Uint16Array(v).fill(0xB1CE);" - " var buf = Buffer.alloc(10, src);" - " return buf.toString() === " njs_evar("'ααααα'", "'�αααα�'") - "})"), - njs_str("true") }, - - { njs_str("var foo = Buffer.alloc(10, 'α');" - "var buf = Buffer.alloc(4, foo); buf"), - njs_str("αα") }, - - { njs_str("var buf = Buffer.allocUnsafe(10).fill('α'); buf"), - njs_str("ααααα") }, - - { njs_str("var buf = Buffer.allocUnsafe(-1)"), - njs_str("RangeError: invalid size") }, - - { njs_str("[" - " ['6576696c', 'hex', 4]," - " ['6576696', 'hex', 3]," - " ['', 'hex', 0]," - " ['', 'base64', 0]," - " ['ZXZpbA==', 'base64', 4]," - " ['ZXZpbA', 'base64url', 4]," - " ['ααααα', undefined, 10]," - "].every(args => Buffer.byteLength(args[0], args[1]) == args[2])"), - njs_str("true") }, - - { njs_str("var foo = new Uint8Array(5);" - "foo[0] = 1; foo[1] = 2; foo[2] = 3; foo[3] = 4; foo[4] = 5;" - "foo = foo.subarray(1, 3);" - "var buf = Buffer.from(foo); njs.dump(buf)"), - njs_str("Buffer [2,3]") }, - - { njs_str("['utf8', 'utf-8', 'hex', 'base64', 'base64url', 'utf-88', '1hex']" - ".map(v=>Buffer.isEncoding(v))"), - njs_str("true,true,true,true,true,false,false") }, - - { njs_str("[" - " ['ABC', 'ABCD', -1]," - " ['ABCD', 'ABC', 1]," - " ['ABC', 'ACB', -1]," - " ['ACB', 'ABC', 1]," - " ['ABC', 'ABC', 0]," - " ['', 'ABC', -1]," - " ['', '', 0]," - "].every(args => {" - " if (Buffer.compare(Buffer.from(args[0]), Buffer.from(args[1])) != args[2]) {" - " throw new TypeError(" - " `Buffer.compare(Buffer.from(${args[0]}), Buffer.from(${args[1]})) != ${args[2]}`);" - " }" - " return true;" - "})"), - njs_str("true") }, - - { njs_str("[" - " ['ABC', 'ABCD', -1]," - " ['ABCD', 'ABC', 1]," - " ['ABC', 'ACB', -1]," - " ['ACB', 'ABC', 1]," - " ['ABC', 'ABC', 0]," - " ['', 'ABC', -1]," - " ['', '', 0]," - "].every(args => {" - " if (Buffer.from(args[0]).compare(Buffer.from(args[1])) != args[2]) {" - " throw new TypeError(" - " `Buffer.from(${args[0]}).compare(Buffer.from(${args[1]})) != ${args[2]}`);" - " }" - " return true;" - "})"), - njs_str("true") }, - - { njs_str("var buf = Buffer.from('ABCD');" - "[" - " [0,3,0,2, -1]," - " [0,2,0,3, 1]," - " [3,4,3,4, 0]," - " [undefined, undefined, undefined, undefined, 0]," - " [-1, undefined, undefined, undefined, 'invalid index']," - " [0, -1, undefined, undefined, 'invalid index']," - " [0, 0, -1, undefined, 'invalid index']," - " [0, 0, 0, -1, 'invalid index']," - "]" - ".every(as => {" - " try {" - " if (buf.compare(buf, as[0], as[1], as[2], as[3]) != as[4]) {" - " throw new TypeError(" - " `buf.compare(${as[0]}, ${as[1]}, ${as[2]}, ${as[3]}) != ${as[4]}`);" - " }" - " } catch (e) { return e.message == as[4]}" - " return true;" - "})"), - njs_str("true") }, - - { njs_str("var buf1 = Buffer.from('ABCD');" - "var buf2 = Buffer.from('ABCD');" - "buf1.compare(buf2, 5)"), - njs_str("RangeError: \"targetStart\" is out of range: 5") }, - - { njs_str("var buf1 = Buffer.from('ABCD');" - "var buf2 = Buffer.from('ABCD');" - "buf1.compare(buf2, 0, 3, 5)"), - njs_str("RangeError: \"sourceStart\" is out of range: 5") }, - - { njs_str("var arr = new Uint8Array(4);" - "arr[0] = 0x41; arr[1] = 0x42; arr[2] = 0x43; arr[3] = 0x44;" - "arr = arr.subarray(1, 4);" - "var buf = Buffer.from('ABCD');" - "buf.compare(arr, 0, 3, 1, 4)"), - njs_str("0") }, - - { njs_str("['123', 'abc', '124', '', 'AB', 'ABCD']" - ".map(v=>Buffer.from(v)).sort(Buffer.compare).map(v=>v.toString())"), - njs_str(",123,124,AB,ABCD,abc") }, - - { njs_str("Buffer.compare(Buffer.alloc(1), 'text')"), - njs_str("TypeError: \"target\" argument must be an instance of Buffer or Uint8Array") }, - - { njs_str("Buffer.compare('text', Buffer.from('ACB'))"), - njs_str("TypeError: \"source\" argument must be an instance of Buffer or Uint8Array") }, - - { njs_str("Buffer.concat()"), - njs_str("TypeError: \"list\" argument must be an instance of Array") }, - - { njs_str("Buffer.concat([])"), - njs_str("") }, - - { njs_str("Buffer.concat([new Uint16Array(10)])"), - njs_str("TypeError: \"list[0]\" argument must be an instance of Buffer or Uint8Array") }, - - { njs_str("Buffer.concat([new Uint8Array(2), new Uint8Array(1)]).fill('abc')"), - njs_str("abc") }, - - { njs_str("Buffer.concat([Buffer.from('AB'), Buffer.from('CD')])"), - njs_str("ABCD") }, - - { njs_str("Buffer.concat([new Uint8Array(2), new Uint8Array(1)], 2).fill('abc')"), - njs_str("ab") }, - - { njs_str("Buffer.concat([new Uint8Array(2), new Uint8Array(1)], 6).fill('abc')"), - njs_str("abcabc") }, - - { njs_str("Buffer.concat([Buffer.from('ABCD').slice(2,4), Buffer.from('ABCD').slice(0,2)])"), - njs_str("CDAB") }, - - { njs_str(njs_declare_sparse_array("list", 2) - "list[0] = Buffer.from('ABCD').slice(2,4);" - "list[1] = Buffer.from('ABCD').slice(0,2);" - "Buffer.concat(list);"), - njs_str("CDAB") }, - - { njs_str(njs_declare_sparse_array("list", 2) - "list[0] = new Uint8Array(2); list[1] = new Uint8Array(3);" - "Buffer.concat(list).fill('ab');"), - njs_str("ababa") }, - - { njs_str("Buffer.concat([], '123')"), - njs_str("TypeError: \"length\" argument must be of type number") }, - - { njs_str("Buffer.concat([], -1)"), - njs_str("RangeError: \"length\" is out of range") }, - - { njs_str("var buf = Buffer.from('α'); buf[1]"), - njs_str("177") }, - - { njs_str("var buf = Buffer.from('α'); buf[1] = 1; njs.dump(buf)"), - njs_str("Buffer [206,1]") }, - - { njs_str("var arrBuf = new ArrayBuffer(16);" - "var buf = Buffer.from(arrBuf); buf.buffer === arrBuf"), - njs_str("true") }, - - { njs_str("[" - " [[0], 4, '65,66,67,68,0,0,0,0,0,0']," - " [[5], 4, '0,0,0,0,0,65,66,67,68,0']," - " [[8], 2, '0,0,0,0,0,0,0,0,65,66']," - " [[8,2,4], 2, '0,0,0,0,0,0,0,0,67,68']," - " [[10], 0, '0,0,0,0,0,0,0,0,0,0']," - "]" - ".every(args => {" - " var buf1 = Buffer.from('ABCD');" - " var buf2 = Buffer.alloc(10, 0);" - " var as = args[0];" - " var length = buf1.copy(buf2, as[0], as[1], as[2]);" - "" - " if (length != args[1]) {" - " throw new TypeError(`buf1.copy(buf2, ${as[0]}, ${as[1]}, ${as[2]}): ${length} != ${args[1]}`)" - " }" - "" - " if (njs.dump(buf2) != `Buffer [${args[2]}]`) {" - " throw new TypeError(" - " `buf1.copy(buf2, ${as[0]}, ${as[1]}, ${as[2]}): ${njs.dump(buf2)} != Buffer [${args[2]}]`);" - " }" - " return true;" - "})"), - njs_str("true") }, - - { njs_str("[" - " [[0], 'ABCDEF']," - " [[0,2], 'CDEFEF']," - " [[0,2,6], 'CDEFEF']," - " [[1,2,4], 'ACDDEF']," - " [[1,2,3], 'ACCDEF']" - "]" - ".every(args => {" - " var buf = Buffer.from('ABCDEF');" - " var as = args[0];" - " buf.copy(buf, as[0], as[1], as[2]);" - "" - " if (buf.toString() != args[1]) {" - " throw new TypeError(" - " `buf.copy(buf, ${as[0]}, ${as[1]}, ${as[2]}): buf.toString() != ${args[1]}`);" - " }" - " return true;" - "})"), - njs_str("true") }, - - { njs_str("var buf1 = Buffer.from('ABCD');" - "var buf2 = Buffer.alloc(10, 0);" - "buf1.copy(buf2, -1)"), - njs_str("RangeError: invalid index") }, - - { njs_str("var buf1 = Buffer.from('ABCD');" - "var buf2 = Buffer.alloc(10, 0);" - "buf1.copy(buf2, 0, -1)"), - njs_str("RangeError: invalid index") }, - - { njs_str("var buf1 = Buffer.from('ABCD');" - "var buf2 = Buffer.alloc(10, 0);" - "buf1.copy(buf2, 0, 5)"), - njs_str("RangeError: \"sourceStart\" is out of range: 5") }, - - { njs_str("var arr = new Uint8Array(4);" - "arr[0] = 0x41; arr[1] = 0x42; arr[2] = 0x43; arr[3] = 0x44;" - "arr = arr.subarray(1, 4);" - "var buf1 = Buffer.from(arr);" - "var buf2 = Buffer.alloc(10, 0);" - "var length = buf1.copy(buf2, 1, 1, 2); [length, njs.dump(buf2)]"), - njs_str("1,Buffer [0,67,0,0,0,0,0,0,0,0]") }, - - { njs_str("var arr = new Uint8Array(4);" - "arr[0] = 0x41; arr[1] = 0x42; arr[2] = 0x43; arr[3] = 0x44;" - "arr = arr.subarray(1, 4);" - "var buf1 = Buffer.from(arr);" - "var buf2 = Buffer.alloc(10, 0);" - "var length = buf1.copy(buf2, 1, 1, 2); [length, njs.dump(buf2)]"), - njs_str("1,Buffer [0,67,0,0,0,0,0,0,0,0]") }, - - { njs_str("[" - " ['ABC', 'ABCD', false]," - " ['ABCD', 'ABC', false]," - " ['ABC', 'ACB', false]," - " ['ACB', 'ABC', false]," - " ['ABC', 'ABC', true]," - " ['', 'ABC', false]," - " ['', '', true]," - "].every(args => {" - " if (Buffer.from(args[0]).equals(Buffer.from(args[1])) != args[2]) {" - " throw new TypeError(" - " `Buffer.from(${args[0]}).compare(Buffer.from(${args[1]})) != ${args[2]}`);" - " }" - " return true;" - "})"), - njs_str("true") }, - - { njs_str("Buffer.from([1,2]).equals(new ArrayBuffer(1))"), - njs_str("TypeError: \"target\" argument must be an instance of Buffer or Uint8Array") }, - - { njs_str("Buffer.from([1,2]).equals(1)"), - njs_str("TypeError: \"target\" argument must be an instance of Buffer or Uint8Array") }, - - { njs_str("var buf = Buffer.alloc(4);" - "buf.fill('ZXZpbA==', 'base64')"), - njs_str("evil") }, - - { njs_str("var buf = Buffer.alloc(4);" - "buf.fill('6576696c', 'hex')"), - njs_str("evil") }, - - { njs_str("var buf = Buffer.alloc(4);" - "buf.fill('ZXZpbA==', '')"), - njs_str("TypeError: \"\" encoding is not supported") }, - - { njs_str("var buf = Buffer.alloc(8);" - "buf.fill('6576696c', 'hex')"), - njs_str("evilevil") }, - - { njs_str("var buf = Buffer.alloc(10);" - "buf.fill('evil')"), - njs_str("evilevilev") }, - - { njs_str("var buf = Buffer.allocUnsafe(5);" - "buf[3] = 1;" - "buf.fill(''); njs.dump(buf)"), - njs_str("Buffer [0,0,0,0,0]") }, - - { njs_str("var arr = new Uint8Array(4);" - "arr[0] = 0x41; arr[1] = 0x42; arr[2] = 0x43; arr[3] = 0x44;" - "arr = arr.subarray(1, 4);" - "var buf = Buffer.allocUnsafe(6);" - "buf.fill(arr); njs.dump(buf)"), - njs_str("Buffer [66,67,68,66,67,68]") }, - - { njs_str("var buf = Buffer.alloc(6, 'ABCDEF');" - "buf.fill(buf, 2, 6)"), - njs_str("ABABCD") }, - - { njs_str("Buffer.alloc(6).fill(0x41)"), - njs_str("AAAAAA") }, - - { njs_str("Buffer.alloc(6).fill({valueOf(){return 0x42}})"), - njs_str("BBBBBB") }, - - { njs_str("njs.dump(Buffer.alloc(3).fill(-1))"), - njs_str("Buffer [255,255,255]") }, - - { njs_str("[NaN, Infinity, -Infinity, undefined, null, {}]" - ".every(v => njs.dump(Buffer.alloc(3).fill(v)) == 'Buffer [0,0,0]')"), - njs_str("true") }, - - { njs_str("njs.dump(Buffer.alloc(6).fill({valueOf(){throw 'Oops'}}, 4,3))"), - njs_str("Buffer [0,0,0,0,0,0]") }, - - { njs_str("njs.dump(Buffer.alloc(6).fill({valueOf(){throw 'Oops'}}, 3,3))"), - njs_str("Buffer [0,0,0,0,0,0]") }, - - { njs_str("njs.dump(Buffer.alloc(6).fill({valueOf(){throw 'Oops'}}, 2,3))"), - njs_str("Oops") }, - - { njs_str("njs.dump(Buffer.alloc(5).fill('α'))"), - njs_str("Buffer [206,177,206,177,206]") }, - - { njs_str("Buffer.alloc(4).fill('ABCD', -1)"), - njs_str("RangeError: invalid index") }, - - { njs_str("Buffer.alloc(4).fill('ABCD', 5)"), - njs_str("RangeError: \"offset\" is out of range") }, - - { njs_str("Buffer.alloc(4).fill('ABCD', 0, -1)"), - njs_str("RangeError: invalid index") }, - - { njs_str("Buffer.alloc(4).fill('ABCD', 0, 5)"), - njs_str("RangeError: \"end\" is out of range") }, - - { njs_str("Buffer.alloc(513).fill('A'.repeat(512)).length"), - njs_str("513") }, - - { njs_str("njs.dump(Buffer.alloc(4).fill((new Uint8Array(5)).fill(1)))"), - njs_str("Buffer [1,1,1,1]") }, - - { njs_str("var src = new Uint8Array(10).fill(255);" - "var u8 = new Uint8Array(src.buffer, 1, 8);" - "u8.set([1,2,3,4,5,6,7,8]);" - "njs.dump(Buffer.alloc(9).fill(u8))"), - njs_str("Buffer [1,2,3,4,5,6,7,8,1]") }, - - { njs_str("Buffer.alloc(513).fill((new Uint8Array(512)).fill(1)).length"), - njs_str("513") }, - - { njs_str("Buffer.alloc(4).fill('ABCD', undefined, undefined, 'utf-128')"), - njs_str("TypeError: \"utf-128\" encoding is not supported") }, - - { njs_str("var buf1 = Buffer.from('ABCD').subarray(1, 3);" - "buf1.fill('B', 1, 2); njs.dump(buf1)"), - njs_str("Buffer [66,66]") }, - - { njs_str("var buf1 = Buffer.from('ABCD').subarray(1, 3);" - "buf1.fill(0x42, 1, 2); njs.dump(buf1)"), - njs_str("Buffer [66,66]") }, - - { njs_str("var buf1 = Buffer.from('ABCD').subarray(1, 2);" - "var buf2 = Buffer.from('ABCD').subarray(1, 3);" - "buf2.fill(buf1, 1, 2); njs.dump(buf2)"), - njs_str("Buffer [66,66]") }, - - { njs_str("var buf = Buffer.from('ABCD');" - "['BC', 'CB', 'ABCD', 'ABCDE', ''].map(v=>buf.indexOf(v))"), - njs_str("1,-1,0,-1,0") }, - - { njs_str("var buf = Buffer.from('ABCD');" - "[0,5,-2,-1].map(v=>buf.indexOf('C', v))"), - njs_str("2,-1,2,-1") }, - - { njs_str("var buf = Buffer.from('evil');" - "buf.indexOf('ZXZpbA==', undefined, 'base64')"), - njs_str("0") }, - - { njs_str("var buf = Buffer.from('evil');" - "buf.indexOf('6576696c', undefined, 'hex')"), - njs_str("0") }, - - { njs_str("var buf = Buffer.from('ABCD');" - "buf.indexOf('C', undefined, 'utf-128')"), - njs_str("TypeError: \"utf-128\" encoding is not supported") }, - - { njs_str("var buf = Buffer.from('ABCDABC');" - "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.indexOf(Buffer.from(v)))"), - njs_str("1,-1,0,-1,0,2,-1") }, - - { njs_str("var buf = Buffer.from('ABCDABC');" - "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.includes(Buffer.from(v)))"), - njs_str("true,false,true,false,true,true,false") }, - - { njs_str("var buf = Buffer.from('ZABCDABC').subarray(1);" - "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.indexOf(Buffer.from(v)))"), - njs_str("1,-1,0,-1,0,2,-1") }, - - { njs_str("var buf = Buffer.from('ABCD');" - "buf.indexOf(0x43)"), - njs_str("2") }, - - { njs_str("var buf = Buffer.from('ABCD');" - "buf.indexOf(0x43, -2)"), - njs_str("2") }, - - { njs_str("var buf = Buffer.from('ABCD');" - "buf.indexOf(0x43, -1)"), - njs_str("-1") }, - - { njs_str("var buf1 = Buffer.from('ABCD');" - "var buf2 = Buffer.from('XXCX').subarray(2, 3);" - "buf1.indexOf(buf2)"), - njs_str("2") }, - - { njs_str("var buf1 = Buffer.from('ABCD').subarray(1, 4);" - "buf1.indexOf(0x43)"), - njs_str("1") }, - - { njs_str("var buf1 = Buffer.from('ABCD').subarray(1, 4);" - "buf1.indexOf('C')"), - njs_str("1") }, - - { njs_str("var buf = Buffer.from('ABCDABC');" - "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.lastIndexOf(v))"), - njs_str("5,-1,0,-1,7,6,-1") }, - - { njs_str("var buf = Buffer.from('ABCDABC');" - "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.lastIndexOf(Buffer.from(v)))"), - njs_str("5,-1,0,-1,7,6,-1") }, - - { njs_str("var buf = Buffer.from('ZABCDABC').subarray(1);" - "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.lastIndexOf(v))"), - njs_str("5,-1,0,-1,7,6,-1") }, - - { njs_str("var buf = Buffer.from('ZABCDABC').subarray(1);" - "['BC', 'CB', 'ABCD', 'ABCDE', '', 'C', 'ABCDABCD'].map(v=>buf.lastIndexOf(Buffer.from(v)))"), - njs_str("5,-1,0,-1,7,6,-1") }, - - { njs_str("var buf = Buffer.from('CABCD');" - "[2,-2,1,-10,10,-5,-4,0].map(v=>buf.lastIndexOf('C', v))"), - njs_str("0,3,0,-1,3,0,0,0") }, - - { njs_str("var buf = Buffer.from('CABCD');" - "[2,-2,1,-10,10,-5,-4,0].map(v=>buf.lastIndexOf(Buffer.from('CZ').subarray(0,1), v))"), - njs_str("0,3,0,-1,3,0,0,0") }, - - { njs_str("var buf = Buffer.from('CABCD');" - "buf.lastIndexOf(0x43)"), - njs_str("3") }, - - { njs_str("var buf = Buffer.from('CABCD');" - "[2,1,0,4,5,-1,-5].map(v=>buf.lastIndexOf(0x43, v))"), - njs_str("0,0,0,3,3,3,0") }, - - { njs_str("var buf1 = Buffer.from('ACBCD').subarray(1, 4);" - "var buf2 = Buffer.from('C');" - "buf1.lastIndexOf(buf2)"), - njs_str("2") }, - - { njs_str("var buf1 = Buffer.from('XXCXX').subarray(2,3);" - "buf1.lastIndexOf(Buffer.from('X'))"), - njs_str("-1") }, - - { njs_str("var buf = Buffer.from('ACBCD').subarray(1, 4);" - "buf.lastIndexOf(0x43)"), - njs_str("2") }, - - { njs_str("var buf = Buffer.from('ACBCD').subarray(1, 4);" - "buf.lastIndexOf('C')"), - njs_str("2") }, - - { njs_str("Buffer.from('abcdef').lastIndexOf('abc', 1)"), - njs_str("0") }, - - { njs_str("['swap16', 'swap32', 'swap64'].every(method => {" - " var buf = Buffer.from([]);" - " buf[method]();" - " return njs.dump(buf) === 'Buffer []';" - "})"), - njs_str("true") }, - - { njs_str("['swap16', 'swap32', 'swap64'].every(method => {" - " var buf = Buffer.from([1,2,3]);" - " try { buf[method]() } " - " catch(e) {return e.message === `Buffer size must be a multiple of ${method.substr(4)}-bits`};" - "})"), - njs_str("true") }, - - { njs_str("['swap16', 'swap32', 'swap64'].map(method => {" - " var buf = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]);" - " buf[method]();" - " return njs.dump(buf);" - "})"), - njs_str("Buffer [2,1,4,3,6,5,8,7]," - "Buffer [4,3,2,1,8,7,6,5]," - "Buffer [8,7,6,5,4,3,2,1]") }, - - { njs_str("['swap16', 'swap32', 'swap64'].map(method => {" - " var u8 = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8]);" - " var buf = Buffer.from(u8.buffer, 1);" - " buf[method]();" - " return njs.dump(buf);" - "})"), - njs_str("Buffer [2,1,4,3,6,5,8,7]," - "Buffer [4,3,2,1,8,7,6,5]," - "Buffer [8,7,6,5,4,3,2,1]") }, - - { njs_str("[" - " [['base64'], 'ZXZpbA==']," - " [['base64url'], 'ZXZpbA']," - " [['hex'], '6576696c']," - " [[undefined,1,3], 'vi']," - " [[undefined,5], '']," - " [[undefined,undefined,5], 'evil']," - " [[undefined,undefined,undefined], 'evil']," - "].every(args => {" - " var buf = Buffer.from('evil');" - " var as = args[0];" - " if (buf.toString(as[0], as[1], as[2]) != args[1]) {" - " throw new TypeError(" - " `buf.toString(${as[0]}, ${as[1]}, ${as[2]}) != ${args[1]}`);" - " }" - " return true;" - "})"), - njs_str("true") }, - - { njs_str("var buf = Buffer.allocUnsafe(4);" - "var len = buf.write('ZXZpbA==', 'base64'); [len, buf]"), - njs_str("4,evil") }, - - { njs_str("var buf = Buffer.allocUnsafe(4);" - "var len = buf.write('ZXZpbA==', undefined, 'base64'); [len, buf]"), - njs_str("4,evil") }, - - { njs_str("var buf = Buffer.allocUnsafe(4);" - "var len = buf.write('ZXZpbA==', undefined, undefined, 'base64'); [len, buf]"), - njs_str("4,evil") }, - - { njs_str("Buffer.allocUnsafe(4).write()"), - njs_str("TypeError: first argument must be a string") }, - - { njs_str("Buffer.allocUnsafe(4).write({a: 1})"), - njs_str("TypeError: first argument must be a string") }, - - { njs_str("Buffer.alloc(4).write('evil', 4, 1);"), - njs_str("RangeError: \"offset\" is out of range") }, - - { njs_str("Buffer.alloc(4).write('evil', -1);"), - njs_str("RangeError: invalid index") }, - - { njs_str("var buf = Buffer.alloc(4);" - "var len = buf.write('evil', 3, 1); [len, njs.dump(buf)]"), - njs_str("1,Buffer [0,0,0,101]") }, - - { njs_str("var buf = Buffer.alloc(4);" - "var len = buf.write('evil', 0, 5); [len, buf]"), - njs_str("4,evil") }, - - { njs_str("Buffer.alloc(4).write('evil', undefined, -1);"), - njs_str("RangeError: invalid index") }, - - { njs_str("var buf = Buffer.from([0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xF9, 0xf8]);" - "[1,2,3,4,5,6].map(byte => [buf.readUIntLE(0, byte), buf.readUIntLE(1, byte)])"), - njs_str("250,251," - "64506,64763," - "16579578,16645371," - "4261215226,4278058235," - "1095182908410,1099494718715," - "281470647991290,274877890034939") }, - - { njs_str("var buf = Buffer.from([0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xF9, 0xf8]);" - "[1,2,3,4,5,6].map(byte => [buf.readIntLE(0, byte), buf.readIntLE(1, byte)])"), - njs_str("-6,-5," - "-1030,-773," - "-197638,-131845," - "-33752070,-16909061," - "-4328719366,-16909061," - "-4328719366,-6597086675717") }, - - { njs_str("var buf = Buffer.from([0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xF9, 0xf8]);" - "[1,2,3,4,5,6].map(byte => [buf.readUIntBE(0, byte), buf.readUIntBE(1, byte)])"), - njs_str("250,251," - "64251,64508," - "16448508,16514301," - "4210818301,4227661310," - "1077969485310,1082281295615," - "275960188239615,277064011677689") }, - - { njs_str("var buf = Buffer.from([0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xF9, 0xf8]);" - "[1,2,3,4,5,6].map(byte => [buf.readIntBE(0, byte), buf.readIntBE(1, byte)])"), - njs_str("-6,-5," - "-1285,-1028," - "-328708,-262915," - "-84148995,-67305986," - "-21542142466,-17230332161," - "-5514788471041,-4410965032967") }, - - { njs_str("var buf = Buffer.from([0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xF9, 0xf8]);" - "function t(sign, endianness, offset) { " - " return [1,2,4].every(size => {" - " var method = `read${sign}Int${size * 8}`;" - " if (size > 1) { method += endianness};" - " var gmethod = `read${sign}Int${endianness}`;" - " var gv = buf[gmethod](offset, size);" - " var sv = buf[method](offset);" - " if (gv != sv) {throw Error(`${gmethod}(${offset},${size}):${gv} != ${method}(${offset}):${sv}`)}" - " return true;" - " });" - "}; " - "t('U', 'LE', 0) && t('U', 'LE', 1)" - "&& t('', 'LE', 0) && t('', 'LE', 1)" - "&& t('U', 'BE', 0) && t('U', 'BE', 1)" - "&& t('', 'BE', 0) && t('', 'BE', 1)"), - njs_str("true") }, - - { njs_str("var buf = Buffer.alloc(9);" - "function t(sign, endianness, offset) { " - " return [1,2,4].every(size => {" - " var rgmethod = `read${sign}Int${endianness}`;" - " var wgmethod = `write${sign}Int${endianness}`;" - " var rmethod = `read${sign}Int${size * 8}`;" - " var wmethod = `write${sign}Int${size * 8}`;" - " if (size > 1) { rmethod += endianness; wmethod += endianness; };" - " var v = 0x7abbccddeeff & (size * 8 - 1);" - "" - " var ret = buf[wgmethod](v, offset, size);" - " if(ret !== offset + size) {" - " throw Error(`${wgmethod} returned ${ret}, need ${offset + size}`);" - " }" - "" - " var gv = buf[rgmethod](offset, size);" - "" - " buf.fill(0);" - " buf[wmethod](v, offset);" - " var sv = buf[rmethod](offset);" - " if (gv != sv) {throw Error(`${wmethod}(${v}, ${offset}):${sv} != ${wgmethod}(${v},${offset}):${gv}`)}" - " return true;" - " });" - "}; " - "t('U', 'LE', 0) && t('U', 'LE', 1)" - "&& t('', 'LE', 0) && t('', 'LE', 1)" - "&& t('U', 'BE', 0) && t('U', 'BE', 1)" - "&& t('', 'BE', 0) && t('', 'BE', 1)"), - njs_str("true") }, - - { njs_str(njs_buffer_byte_map("writeUIntLE", "+", 1)), - njs_str("Buffer [128,0,0,0,0,0]," - "Buffer [0,64,0,0,0,0]," - "Buffer [0,0,32,0,0,0]," - "Buffer [0,0,0,16,0,0]," - "Buffer [0,0,0,0,8,0]," - "Buffer [0,0,0,0,0,4]") }, - - { njs_str(njs_buffer_byte_map("writeUIntBE", "+", 1)), - njs_str("Buffer [128,0,0,0,0,0]," - "Buffer [64,0,0,0,0,0]," - "Buffer [32,0,0,0,0,0]," - "Buffer [16,0,0,0,0,0]," - "Buffer [8,0,0,0,0,0]," - "Buffer [4,0,0,0,0,0]") }, - - { njs_str(njs_buffer_byte_map("writeIntLE", "-", 2)), - njs_str("Buffer [192,0,0,0,0,0]," - "Buffer [0,224,0,0,0,0]," - "Buffer [0,0,240,0,0,0]," - "Buffer [0,0,0,248,0,0]," - "Buffer [0,0,0,0,252,0]," - "Buffer [0,0,0,0,0,254]") }, - - { njs_str(njs_buffer_byte_map("writeIntBE", "-", 2)), - njs_str("Buffer [192,0,0,0,0,0]," - "Buffer [224,0,0,0,0,0]," - "Buffer [240,0,0,0,0,0]," - "Buffer [248,0,0,0,0,0]," - "Buffer [252,0,0,0,0,0]," - "Buffer [254,0,0,0,0,0]") }, - - { njs_str("Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]).readDoubleBE()"), - njs_str("8.20788039913184e-304") }, - - { njs_str("Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]).readDoubleLE()"), - njs_str("5.447603722011605e-270") }, - - { njs_str("Buffer.from([1, 2, 3, 4]).readFloatBE()"), - njs_str("2.387939260590663e-38") }, - - { njs_str("Buffer.from([1, 2, 3, 4]).readFloatLE()"), - njs_str("1.539989614439558e-36") }, - - { njs_str("Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]).readDoubleBE(1)"), - njs_str("RangeError: index 1 is outside the bound of the buffer") }, - - { njs_str("Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]).readDoubleLE(1)"), - njs_str("RangeError: index 1 is outside the bound of the buffer") }, - - { njs_str("Buffer.from([1, 2, 3, 4]).readFloatBE(1)"), - njs_str("RangeError: index 1 is outside the bound of the buffer") }, - - { njs_str("Buffer.from([1, 2, 3, 4]).readFloatLE(1)"), - njs_str("RangeError: index 1 is outside the bound of the buffer") }, - - { njs_str("var buf = Buffer.allocUnsafe(8);" - "buf.writeDoubleBE(123.456); njs.dump(buf)"), - njs_str("Buffer [64,94,221,47,26,159,190,119]") }, - - { njs_str("var buf = Buffer.allocUnsafe(8);" - "buf.writeDoubleLE(123.456); njs.dump(buf)"), - njs_str("Buffer [119,190,159,26,47,221,94,64]") }, - - { njs_str("var buf = Buffer.allocUnsafe(4);" - "buf.writeFloatBE(123.456); njs.dump(buf)"), - njs_str("Buffer [66,246,233,121]") }, - - { njs_str("var buf = Buffer.allocUnsafe(4);" - "buf.writeFloatLE(123.456); njs.dump(buf)"), - njs_str("Buffer [121,233,246,66]") }, - - { njs_str("var buf = Buffer.allocUnsafe(8).writeDoubleBE(123.456, 1)"), - njs_str("RangeError: index 1 is outside the bound of the buffer") }, - - { njs_str("var buf = Buffer.allocUnsafe(8).writeDoubleLE(123.456, 1)"), - njs_str("RangeError: index 1 is outside the bound of the buffer") }, - - { njs_str("var buf = Buffer.allocUnsafe(4).writeFloatBE(123.456, 1)"), - njs_str("RangeError: index 1 is outside the bound of the buffer") }, - - { njs_str("var buf = Buffer.allocUnsafe(4).writeFloatLE(123.456, 1)"), - njs_str("RangeError: index 1 is outside the bound of the buffer") }, - - { njs_str("var buffer = require('buffer');" - "buffer.Buffer.alloc(5).fill('ABC')"), - njs_str("ABCAB") }, - - { njs_str("var buffer = require('buffer');" - "typeof buffer.kMaxLength === 'number' "), - njs_str("true") }, - - { njs_str("var buffer = require('buffer');" - "typeof buffer.constants.MAX_LENGTH === 'number' "), - njs_str("true") }, - - { njs_str("var buffer = require('buffer');" - "typeof buffer.constants.MAX_STRING_LENGTH === 'number' "), - njs_str("true") }, -}; - - static njs_unit_test_t njs_webcrypto_test[] = { /* Statistic test @@ -24891,12 +24080,6 @@ static njs_test_suite_t njs_suites[] = njs_nitems(njs_querystring_module_test), njs_unit_test }, - { njs_str("buffer module"), - { .repeat = 1, .unsafe = 1 }, - njs_buffer_module_test, - njs_nitems(njs_buffer_module_test), - njs_unit_test }, - { njs_str("externals"), { .externals = 1, .repeat = 1, .unsafe = 1 }, njs_externals_test, diff --git a/test/buffer.t.js b/test/buffer.t.js index 1d96d342d..55227b3a3 100644 --- a/test/buffer.t.js +++ b/test/buffer.t.js @@ -1,5 +1,5 @@ /*--- -includes: [compatBuffer.js, runTsuite.js] +includes: [compatBuffer.js, runTsuite.js, compareArray.js] flags: [async] ---*/ @@ -10,6 +10,297 @@ function p(args, default_opts) { return params; } +let alloc_tsuite = { + name: "Buffer.alloc() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = Buffer.alloc(params.size, params.fill, params.encoding); + + if (r.toString() !== params.expected) { + throw Error(`unexpected output "${r.toString()}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: { encoding: 'utf-8' }, + + tests: [ + { size: 3, fill: 0x61, expected: 'aaa' }, + { size: 3, fill: 'A', expected: 'AAA' }, + { size: 3, fill: 'ABCD', expected: 'ABC' }, + { size: 3, fill: '414243', encoding: 'hex', expected: 'ABC' }, + { size: 4, fill: '414243', encoding: 'hex', expected: 'ABCA' }, + { size: 3, fill: 'QUJD', encoding: 'base64', expected: 'ABC' }, + { size: 3, fill: 'QUJD', encoding: 'base64url', expected: 'ABC' }, + { size: 3, fill: Buffer.from('ABCD'), encoding: 'utf-8', expected: 'ABC' }, + { size: 3, fill: Buffer.from('ABCD'), encoding: 'utf8', expected: 'ABC' }, + { size: 3, fill: 'ABCD', encoding: 'utf-128', + exception: 'TypeError: "utf-128" encoding is not supported' }, + { size: 3, fill: Buffer.from('def'), expected: 'def' }, + ], +}; + + +let byteLength_tsuite = { + name: "Buffer.byteLength() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = Buffer.byteLength(params.value, params.encoding); + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: { encoding: 'utf-8' }, + + tests: [ + { value: 'abc', expected: 3 }, + { value: 'αβγ', expected: 6 }, + { value: 'αβγ', encoding: 'utf-8', expected: 6 }, + { value: 'αβγ', encoding: 'utf8', expected: 6 }, + { value: 'αβγ', encoding: 'utf-128', exception: 'TypeError: "utf-128" encoding is not supported' }, + { value: '414243', encoding: 'hex', expected: 3 }, + { value: 'QUJD', encoding: 'base64', expected: 3 }, + { value: 'QUJD', encoding: 'base64url', expected: 3 }, + { value: Buffer.from('αβγ'), expected: 6 }, + { value: Buffer.alloc(3).buffer, expected: 3 }, + { value: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), expected: 3 }, + ], +}; + + +let concat_tsuite = { + name: "Buffer.concat() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = Buffer.concat(params.buffers, params.length); + + if (r.toString() !== params.expected) { + throw Error(`unexpected output "${r.toString()}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + tests: [ + { buffers: [ Buffer.from('abc'), + Buffer.from(new Uint8Array([0x64, 0x65, 0x66]).buffer, 1) ], + expected: 'abcef' }, + { buffers: [ Buffer.from('abc'), Buffer.from('def'), Buffer.from('') ], + expected: 'abcdef' }, + { buffers: [ Buffer.from(''), Buffer.from('abc'), Buffer.from('def') ], + length: 4, expected: 'abcd' }, + ], +}; + + +let compare_tsuite = { + name: "Buffer.compare() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = Buffer.compare(params.buf1, params.buf2); + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { buf1: Buffer.from('abc'), buf2: Buffer.from('abc'), expected: 0 }, + { buf1: Buffer.from('abc'), + buf2: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), + expected: 0 }, + { buf1: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), + buf2: Buffer.from('abc'), + expected: 0 }, + { buf1: Buffer.from('abc'), buf2: Buffer.from('def'), expected: -1 }, + { buf1: Buffer.from('def'), buf2: Buffer.from('abc'), expected: 1 }, + { buf1: Buffer.from('abc'), buf2: Buffer.from('abcd'), expected: -1 }, + { buf1: Buffer.from('abcd'), buf2: Buffer.from('abc'), expected: 1 }, + { buf1: Buffer.from('abc'), buf2: Buffer.from('ab'), expected: 1 }, + { buf1: Buffer.from('ab'), buf2: Buffer.from('abc'), expected: -1 }, + { buf1: Buffer.from('abc'), buf2: Buffer.from(''), expected: 1 }, + { buf1: Buffer.from(''), buf2: Buffer.from('abc'), expected: -1 }, + { buf1: Buffer.from(''), buf2: Buffer.from(''), expected: 0 }, + ], +}; + + +let comparePrototype_tsuite = { + name: "buf.compare() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = params.buf.compare(params.target, params.tStart, params.tEnd, + params.sStart, params.sEnd); + + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { buf: Buffer.from('abc'), target: Buffer.from('abc'), expected: 0 }, + { buf: Buffer.from('abc'), + target: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), + expected: 0 }, + { buf: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), + target: Buffer.from('abc'), expected: 0 }, + { buf: Buffer.from('abc'), target: Buffer.from('def'), expected: -1 }, + { buf: Buffer.from('def'), target: Buffer.from('abc'), expected: 1 }, + { buf: Buffer.from('abc'), target: Buffer.from('abcd'), expected: -1 }, + { buf: Buffer.from('abcd'), target: Buffer.from('abc'), expected: 1 }, + { buf: Buffer.from('abc'), target: Buffer.from('ab'), expected: 1 }, + { buf: Buffer.from('ab'), target: Buffer.from('abc'), expected: -1 }, + { buf: Buffer.from('abc'), target: Buffer.from(''), expected: 1 }, + { buf: Buffer.from(''), target: Buffer.from('abc'), expected: -1 }, + { buf: Buffer.from(''), target: Buffer.from(''), expected: 0 }, + + { buf: Buffer.from('abcdef'), target: Buffer.from('abc'), + sEnd: 3, expected: 0 }, + { buf: Buffer.from('abcdef'), target: Buffer.from('def'), + sStart: 3, expected: 0 }, + { buf: Buffer.from('abcdef'), target: Buffer.from('abc'), + sStart: 0, sEnd: 3, expected: 0 }, + { buf: Buffer.from('abcdef'), target: Buffer.from('def'), + sStart: 3, sEnd: 6, expected: 0 }, + { buf: Buffer.from('abcdef'), target: Buffer.from('def'), + sStart: 3, sEnd: 5, tStart: 0, tEnd: 2, expected: 0 }, + ], +}; + + +let copy_tsuite = { + name: "buf.copy() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = params.buf.copy(params.target, params.tStart, params.sStart, params.sEnd); + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + if (params.target.toString() !== params.expected_buf) { + throw Error(`unexpected buf "${params.target.toString()}" != "${params.expected_buf}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), + expected: 6, expected_buf: 'abcdef' }, + { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), + tStart: 0, expected: 6, expected_buf: 'abcdef' }, + { buf: Buffer.from('abc'), target: Buffer.from('123456789'), + tStart: 5, expected: 3, expected_buf: '12345abc9' }, + { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), + tStart: 0, sStart: 0, expected: 6, expected_buf: 'abcdef' }, + { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), + tStart: 0, sStart: 0, sEnd: 3, expected: 3, expected_buf: 'abc456' }, + { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), + tStart: 2, sStart: 2, sEnd: 3, expected: 1, expected_buf: '12c456' }, + { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), + tStart: 7, exception: 'RangeError: \"targetStart\" is out of bounds' }, + { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), + sStart: 7, exception: 'RangeError: \"sourceStart\" is out of bounds' }, + ], +}; + + +let equals_tsuite = { + name: "buf.equals() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = params.buf1.equals(params.buf2); + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + tests: [ + + { buf1: Buffer.from('abc'), buf2: Buffer.from('abc'), expected: true }, + { buf1: Buffer.from('abc'), + buf2: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), + expected: true }, + { buf1: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), + buf2: Buffer.from('abc'), expected: true }, + { buf1: Buffer.from('abc'), buf2: Buffer.from('def'), expected: false }, + { buf1: Buffer.from('def'), buf2: Buffer.from('abc'), expected: false }, + { buf1: Buffer.from('abc'), buf2: Buffer.from('abcd'), expected: false }, + { buf1: Buffer.from('abcd'), buf2: Buffer.from('abc'), expected: false }, + { buf1: Buffer.from('abc'), buf2: Buffer.from('ab'), expected: false }, + { buf1: Buffer.from('ab'), buf2: Buffer.from('abc'), expected: false }, + { buf1: Buffer.from('abc'), buf2: Buffer.from(''), expected: false }, + { buf1: Buffer.from(''), buf2: Buffer.from('abc'), expected: false }, + { buf1: Buffer.from(''), buf2: Buffer.from(''), expected: true }, + ], +}; + + +let fill_tsuite = { + name: "buf.fill() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = params.buf.fill(params.value, params.offset, params.end); + + if (r.toString() !== params.expected) { + throw Error(`unexpected output "${r.toString()}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + tests: [ + { buf: Buffer.from('abc'), value: 0x61, expected: 'aaa' }, + { buf: Buffer.from('abc'), value: 0x61, expected: 'aaa', offset: 0, end: 3 }, + { buf: Buffer.from('abc'), value: 0x61, expected: 'abc', offset: 0, end: 0 }, + { buf: Buffer.from('abc'), value: 'A', expected: 'AAA' }, + { buf: Buffer.from('abc'), value: 'ABCD', expected: 'ABC' }, + { buf: Buffer.from('abc'), value: '414243', offset: 'hex', expected: 'ABC' }, + { buf: Buffer.from('abc'), value: '414243', offset: 'utf-128', + exception: 'TypeError: "utf-128" encoding is not supported' }, + { buf: Buffer.from('abc'), value: 'ABCD', offset: 1, expected: 'aAB' }, + { buf: Buffer.from('abc'), value: Buffer.from('def'), expected: 'def' }, + { buf: Buffer.from('def'), + value: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), + expected: 'abc' }, + { buf: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), + value: Buffer.from('def'), + expected: 'def' }, + ], +}; + + let from_tsuite = { name: "Buffer.from() tests", skip: () => (!has_buffer()), @@ -20,6 +311,12 @@ let from_tsuite = { params.modify(buf); } + if (params.args[0] instanceof ArrayBuffer) { + if (buf.buffer !== params.args[0]) { + throw Error(`unexpected buffer "${buf.buffer}" != "${params.args[0]}"`); + } + } + let r = buf.toString(params.fmt); if (r.length !== params.expected.length) { @@ -48,7 +345,6 @@ let from_tsuite = { { args: [(new Uint8Array([0xaa, 0xbb, 0xcc])).buffer, 1, 1], fmt: "hex", expected: 'bb' }, { args: [(new Uint8Array([0xaa, 0xbb, 0xcc])).buffer, '1', '1'], fmt: "hex", expected: 'bb' }, { args: [(new Uint8Array([0xaa, 0xbb, 0xcc])).buffer, 1, 0], fmt: "hex", expected: '' }, - { args: [(new Uint8Array([0xaa, 0xbb, 0xcc])).buffer, 1, -1], fmt: "hex", expected: '' }, { args: [(new Uint8Array([0xaa, 0xbb, 0xcc])).buffer], fmt: "hex", modify: (buf) => { buf[1] = 0; }, @@ -128,6 +424,68 @@ let from_tsuite = { { args: ['QUJDRA#', "base64url"], expected: 'ABCD' }, ]}; + +let includes_tsuite = { + name: "buf.includes() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = params.buf.includes(params.value, params.offset, params.encoding); + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { buf: Buffer.from('abcdef'), value: 'abc', expected: true }, + { buf: Buffer.from('abcdef'), value: 'def', expected: true }, + { buf: Buffer.from('abcdef'), value: 'abc', offset: 1, expected: false }, + { buf: Buffer.from('abcdef'), value: {}, + exception: 'TypeError: "value" argument must be of type string or an instance of Buffer' }, + ], +}; + + +let indexOf_tsuite = { + name: "buf.indexOf() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = params.buf.indexOf(params.value, params.offset, params.encoding); + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { buf: Buffer.from('abcdef'), value: 'abc', expected: 0 }, + { buf: Buffer.from('abcdef'), value: 'def', expected: 3 }, + { buf: Buffer.from('abcdef'), value: 'abc', offset: 1, expected: -1 }, + { buf: Buffer.from('abcdef'), value: 'def', offset: 1, expected: 3 }, + { buf: Buffer.from('abcdef'), value: 'def', offset: -3, expected: 3 }, + { buf: Buffer.from('abcdef'), value: '626364', encoding: 'hex', expected: 1 }, + { buf: Buffer.from('abcdef'), value: '626364', encoding: 'utf-128', + exception: 'TypeError: "utf-128" encoding is not supported' }, + { buf: Buffer.from('abcdef'), value: 0x62, expected: 1 }, + { buf: Buffer.from('abcabc'), value: 0x61, offset: 1, expected: 3 }, + { buf: Buffer.from('abcdef'), value: Buffer.from('def'), expected: 3 }, + { buf: Buffer.from('abcdef'), value: Buffer.from(new Uint8Array([0x60, 0x62, 0x63]).buffer, 1), expected: 1 }, + { buf: Buffer.from('abcdef'), value: {}, + exception: 'TypeError: "value" argument must be of type string or an instance of Buffer' }, + ], +}; + + let isBuffer_tsuite = { name: "Buffer.isBuffer() tests", skip: () => (!has_buffer()), @@ -151,6 +509,33 @@ let isBuffer_tsuite = { ]}; +let isEncoding_tsuite = { + name: "Buffer.isEncoding() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = Buffer.isEncoding(params.value); + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { value: 'utf-8', expected: true }, + { value: 'utf8', expected: true }, + { value: 'utf-128', expected: false }, + { value: 'hex', expected: true }, + { value: 'base64', expected: true }, + { value: 'base64url', expected: true }, + ], +}; + + function compare_object(a, b) { if (a === b) { return true; @@ -175,6 +560,264 @@ function compare_object(a, b) { } +let lastIndexOf_tsuite = { + name: "buf.lastIndexOf() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = params.buf.lastIndexOf(params.value, params.offset, params.encoding); + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { buf: Buffer.from('abcdef'), value: 'abc', expected: 0 }, + { buf: Buffer.from('abcabc'), value: 'abc', expected: 3 }, + { buf: Buffer.from('abcdef'), value: 'def', expected: 3 }, + { buf: Buffer.from('abcdef'), value: 'abc', offset: 1, expected: 0 }, + { buf: Buffer.from('abcdef'), value: 'def', offset: 1, expected: -1 }, + { buf: Buffer.from(Buffer.alloc(7).fill('Zabcdef').buffer, 1), value: 'abcdef', expected: 0 }, + { buf: Buffer.from(Buffer.alloc(7).fill('Zabcdef').buffer, 1), value: 'abcdefg', expected: -1 }, + { buf: Buffer.from('abcdef'), value: '626364', encoding: 'hex', expected: 1 }, + { buf: Buffer.from('abcdef'), value: '626364', encoding: 'utf-128', + exception: 'TypeError: "utf-128" encoding is not supported' }, + { buf: Buffer.from('abcabc'), value: 0x61, expected: 3 }, + { buf: Buffer.from('abcabc'), value: 0x61, offset: 1, expected: 0 }, + { buf: Buffer.from('abcdef'), value: Buffer.from('def'), expected: 3 }, + { buf: Buffer.from('abcdef'), value: Buffer.from(new Uint8Array([0x60, 0x62, 0x63]).buffer, 1), expected: 1 }, + { buf: Buffer.from('abcdef'), value: {}, + exception: 'TypeError: "value" argument must be of type string or an instance of Buffer' }, + ], +}; + + +let readXIntXX_tsuite = { + name: "buf.readXIntXX() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let b = params.buf; + let r = [ + b.readInt8(params.offset), + b.readUInt8(params.offset), + b.readInt16LE(params.offset), + b.readInt16BE(params.offset), + b.readUInt16LE(params.offset), + b.readUInt16BE(params.offset), + b.readInt32LE(params.offset), + b.readInt32BE(params.offset), + b.readUInt32LE(params.offset), + b.readUInt32BE(params.offset), + ]; + + + if (!compareArray(r, params.expected)) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 0, + expected: [ -86,170,-17494,-21829,48042,43707,-573785174,-1430532899,3721182122,2864434397 ] }, + { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 1, + expected: [ -69,187,-13125,-17460,52411,48076,-287454021,-1144201746,4007513275,3150765550 ] }, + { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 2, + expected: [ -52,204,-8756,-13091,56780,52445,-1122868,-857870593,4293844428,3437096703 ] }, + { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 3, + exception: 'RangeError: Index out of range' }, + ], +}; + + +let readFloat_tsuite = { + name: "buf.readFloat() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let b = Buffer.alloc(9); + let r = b.writeFloatLE(123.125, 0); + if (r !== 4) { + throw Error(`unexpected output "${r}" != "4"`); + } + + if (b.readFloatLE(0) !== 123.125) { + throw Error(`unexpected output "${b.readFloatLE(0)}" != "123.125"`); + } + + r = b.writeFloatBE(123.125, 0); + if (r !== 4) { + throw Error(`unexpected output "${r}" != "4"`); + } + + if (b.readFloatBE(0) !== 123.125) { + throw Error(`unexpected output "${b.readFloatBE(0)}" != "123.125"`); + } + + r = b.writeDoubleLE(123.125, 1); + if (r !== 9) { + throw Error(`unexpected output "${r}" != "9"`); + } + + if (b.readDoubleLE(1) !== 123.125) { + throw Error(`unexpected output "${b.readDoubleLE(1)}" != "123.125"`); + } + + r = b.writeDoubleBE(123.125, 1); + if (r !== 9) { + throw Error(`unexpected output "${r}" != "9"`); + } + + if (b.readDoubleBE(1) !== 123.125) { + throw Error(`unexpected output "${b.readDoubleBE(1)}" != "123.125"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + {} + ], +}; + + +let readGeneric_tsuite = { + name: "buf.readGeneric() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let b = params.buf; + let r = [ + b.readUIntLE(params.offset, params.length), + b.readUIntBE(params.offset, params.length), + b.readIntLE(params.offset, params.length), + b.readIntBE(params.offset, params.length), + ]; + + + if (!compareArray(r, params.expected)) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 0, length: 1, + expected: [ 170, 170, -86, -86 ] }, + { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 1, length: 2, + expected: [ 52411,48076,-13125,-17460 ] }, + { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 2, length: 3, + expected: [ 15654348,13426158,-1122868,-3351058 ] }, + { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 3, length: 4, + exception: 'RangeError: Index out of range' }, + { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 0, length: 0, + exception: 'RangeError: byteLength must be <= 6' }, + { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 0, length: 5, + expected: [ 1025923398570,733295205870,-73588229206,-366216421906 ] }, + { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 0, length: 6, + expected: [ 281401388481450,187723572702975,-73588229206,-93751404007681 ] }, + ], +}; + + +let slice_tsuite = { + name: "buf.slice() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = params.buf.slice(params.start, params.end); + + if (r.toString() !== params.expected) { + throw Error(`unexpected output "${r.toString()}" != "${params.expected}"`); + } + + params.buf[2] = 0x5a; + + if (r.constructor.name !== 'Buffer' || r.__proto__ !== params.buf.__proto__) { + throw Error(`unexpected output "${r.constructor.name}" != "Buffer"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { buf: Buffer.from('abcdef'), start: 1, expected: 'bcdef' }, + { buf: Buffer.from('abcdef'), start: 1, end: 3, expected: 'bc' }, + ], +}; + + +let subarray_tsuite = { + name: "buf.subarray() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = params.buf.subarray(params.start, params.end); + + params.buf[0] = 0x5a; + + if (r.toString() !== params.expected) { + throw Error(`unexpected output "${r.toString()}" != "${params.expected}"`); + } + + if (r.constructor.name !== 'Buffer' || r.__proto__ !== params.buf.__proto__) { + throw Error(`unexpected output "${r.constructor.name}" != "Buffer"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { buf: Buffer.from('abcdef'), start: 0, end: 3, expected: 'Zbc' }, + { buf: Buffer.from('abcdef'), start: 1, expected: 'bcdef' }, + ], +}; + + +let swap_tsuite = { + name: "buf.swap() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let r = Buffer.from(params.value, 'hex')[params.swap](); + + if (r.toString('hex') !== params.expected) { + throw Error(`unexpected output "${r.toString('hex')}" != "${params.expected}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: { swap: 'swap16' }, + + tests: [ + { value: '01020304', expected: '02010403' }, + { value: '010203', exception: 'RangeError: Buffer size must be a multiple of 2' }, + { value: 'aabbccddeeff0011', swap: 'swap32', expected: 'ddccbbaa1100ffee' }, + { value: 'aabbcc', swap: 'swap32', exception: 'RangeError: Buffer size must be a multiple of 4' }, + { value: 'aabbccddeeff00112233445566778899', swap: 'swap64', expected: '1100ffeeddccbbaa9988776655443322' }, + ], +}; + + let toJSON_tsuite = { name: "Buffer.toJSON() tests", skip: () => (!has_buffer()), @@ -224,13 +867,181 @@ let toString_tsuite = { { value: new Uint8Array([0xff, 0xde, 0xba]), fmt: "hex", expected: 'ffdeba' }, { value: new Uint8Array([0xff, 0xde, 0xba]), fmt: "base64", expected: '/966' }, { value: new Uint8Array([0xff, 0xde, 0xba]), fmt: "base64url", expected: '_966' }, + { value: "ABCD", fmt: "base64", expected: 'QUJDRA==' }, + { value: "ABCD", fmt: "base64url", expected: 'QUJDRA' }, { value: '', fmt: "utf-128", exception: 'TypeError: "utf-128" encoding is not supported' }, ]}; + +let write_tsuite = { + name: "buf.write() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let b = Buffer.alloc(10).fill('Z'); + let r; + + if (typeof params.offset != 'undefined' && typeof params.length != 'undefined') { + r = b.write(params.value, params.offset, params.length, params.encoding); + + } else if (typeof params.offset != 'undefined') { + r = b.write(params.value, params.offset, params.encoding); + + } else { + r = b.write(params.value, params.encoding); + } + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + if (b.toString() !== params.expected_buf) { + throw Error(`unexpected output "${b.toString()}" != "${params.expected_buf}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { value: 'abc', expected: 3, expected_buf: 'abcZZZZZZZ' }, + { value: 'abc', offset: 1, expected: 3, expected_buf: 'ZabcZZZZZZ' }, + { value: 'abc', offset: 1, length: 2, expected: 2, expected_buf: 'ZabZZZZZZZ' }, + { value: 'αβγ', offset: 1, expected: 6, expected_buf: 'ZαβγZZZ' }, + { value: 'αβγ', offset: 1, length: 1, expected: 0, expected_buf: 'ZZZZZZZZZZ' }, + { value: 'αβγ', offset: 1, length: 2, expected: 2, expected_buf: 'ZαZZZZZZZ' }, + { value: '414243', encoding: 'hex', expected: 3, expected_buf: 'ABCZZZZZZZ' }, + { value: '414243', encoding: 'hex', offset: 8, expected: 2, expected_buf: 'ZZZZZZZZAB' }, + { value: "x".repeat(12), expected: 10, expected_buf: 'xxxxxxxxxx' }, + { value: "x".repeat(12), offset: 1, expected: 9, expected_buf: 'Zxxxxxxxxx' }, + { value: "x", offset: 1, length: 2, encoding: 'utf-128', + exception: 'TypeError: "utf-128" encoding is not supported' }, + { value: "x".repeat(10), offset: 10, expected: 0, expected_buf: 'ZZZZZZZZZZ' }, + { value: "x".repeat(10), offset: 11, exception: 'RangeError: Index out of range' }, + ], +}; + + +let writeXIntXX_tsuite = { + name: "buf.writeXIntXX() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let b = Buffer.alloc(10).fill('Z'); + let r = b[params.write](params.value, params.offset); + + if (params.exception) { + throw Error(`expected exception "${params.exception}"`); + } + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + if (b.toString('hex') !== params.expected_buf) { + throw Error(`unexpected output "${b.toString('hex')}" != "${params.expected_buf}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { write: 'writeInt8', value: 0xaa, exception: 'RangeError: Index out of range' }, + { write: 'writeInt8', value: 0x00, offset: 3, expected: 4, expected_buf: '5a5a5a005a5a5a5a5a5a' }, + { write: 'writeUInt8', value: 0xaa, offset: 0, expected: 1, expected_buf: 'aa5a5a5a5a5a5a5a5a5a' }, + { write: 'writeInt16LE', value: 0xaabb, exception: 'RangeError: Index out of range' }, + { write: 'writeInt16BE', value: 0x7788, offset: 1, expected: 3, expected_buf: '5a77885a5a5a5a5a5a5a' }, + { write: 'writeUInt16LE', value: 0xaabb, offset: 0, expected: 2, expected_buf: 'bbaa5a5a5a5a5a5a5a5a' }, + { write: 'writeUInt16BE', value: 0x7788, offset: 1, expected: 3, expected_buf: '5a77885a5a5a5a5a5a5a' }, + { write: 'writeInt32LE', value: 0xaabbccdd, exception: 'RangeError: Index out of range' }, + { write: 'writeInt32BE', value: 0x778899aa, offset: 1, expected: 5, expected_buf: '5a778899aa5a5a5a5a5a' }, + { write: 'writeUInt32LE', value: 0xaabbccdd, offset: 0, expected: 4, expected_buf: 'ddccbbaa5a5a5a5a5a5a' }, + { write: 'writeUInt32BE', value: 0x778899aa, offset: 1, expected: 5, expected_buf: '5a778899aa5a5a5a5a5a' }, + ], +}; + + +let writeGeneric_tsuite = { + name: "buf.writeGeneric() tests", + skip: () => (!has_buffer()), + T: async (params) => { + let b = Buffer.alloc(10).fill('Z'); + let r = b[params.write](params.value, params.offset, params.length); + + if (params.exception) { + throw Error(`expected exception "${params.exception}"`); + } + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + if (b.toString('hex') !== params.expected_buf) { + throw Error(`unexpected output "${b.toString('hex')}" != "${params.expected_buf}"`); + } + + return 'SUCCESS'; + }, + + prepare_args: p, + opts: {}, + + tests: [ + { write: 'writeUIntLE', value: 0xaa, length: 1, + exception: 'RangeError: Index out of range' }, + { write: 'writeUIntLE', value: 0x44, length: 1, offset: 3, + expected: 4, expected_buf: '5a5a5a445a5a5a5a5a5a' }, + { write: 'writeUIntBE', value: 0xaabb, length: 2, offset: 0, + expected: 2, expected_buf: 'aabb5a5a5a5a5a5a5a5a' }, + { write: 'writeUIntBE', value: 0x7788, length: 2, offset: 1, + expected: 3, expected_buf: '5a77885a5a5a5a5a5a5a' }, + { write: 'writeIntLE', value: 0x445566, length: 3, offset: 5, + expected: 8, expected_buf: '5a5a5a5a5a6655445a5a' }, + { write: 'writeIntBE', value: 0x778899, length: 3, offset: 1, + expected: 4, expected_buf: '5a7788995a5a5a5a5a5a' }, + { write: 'writeIntLE', value: 0x44556677, length: 4, offset: 5, + expected: 9, expected_buf: '5a5a5a5a5a776655445a' }, + { write: 'writeIntBE', value: 0xaabbccdd, length: 4, offset: 1, + exception: 'RangeError: Index out of range' }, + { write: 'writeUIntLE', value: 0xaabbccddee, length: 5, offset: 0, + expected: 5, expected_buf: 'eeddccbbaa5a5a5a5a5a' }, + { write: 'writeUIntBE', value: 0x778899aabbcc, length: 6, offset: 1, + expected: 7, expected_buf: '5a778899aabbcc5a5a5a' }, + { write: 'writeUIntBE', value: 0, length: 7, + exception: 'The value of "byteLength" is out of range' }, + + ], +}; + + run([ + alloc_tsuite, + byteLength_tsuite, + concat_tsuite, + compare_tsuite, + comparePrototype_tsuite, + copy_tsuite, + equals_tsuite, + fill_tsuite, from_tsuite, + includes_tsuite, + indexOf_tsuite, isBuffer_tsuite, + isEncoding_tsuite, + lastIndexOf_tsuite, + readXIntXX_tsuite, + readFloat_tsuite, + readGeneric_tsuite, + slice_tsuite, + subarray_tsuite, + swap_tsuite, toJSON_tsuite, toString_tsuite, + write_tsuite, + writeXIntXX_tsuite, + writeGeneric_tsuite, ]) .then($DONE, $DONE); diff --git a/test/harness/runTsuite.js b/test/harness/runTsuite.js index dc2034fd9..aa3f5c0fb 100644 --- a/test/harness/runTsuite.js +++ b/test/harness/runTsuite.js @@ -48,6 +48,9 @@ function merge(to, from) { } else if (from[v] instanceof Uint8Array) { r[v] = new Uint8Array(from[v]); + } else if (from[v] instanceof ArrayBuffer) { + r[v] = new ArrayBuffer(from[v].byteLength); + } else { r[v] = Object.assign(Array.isArray(from[v]) ? [] : {}, from[v]); }