2042 lines
51 KiB
C
2042 lines
51 KiB
C
|
|
#include <assert.h>
|
||
|
|
#include <bare.h>
|
||
|
|
#include <base64.h>
|
||
|
|
#include <hex.h>
|
||
|
|
#include <js.h>
|
||
|
|
#include <stdatomic.h>
|
||
|
|
#include <stdint.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <utf.h>
|
||
|
|
|
||
|
|
// Results up to this length are encoded on the stack, so that the common case
|
||
|
|
// of a short hex or base64 string involves neither an allocation nor the
|
||
|
|
// bookkeeping of an external string.
|
||
|
|
#define BARE_BUFFER_STACK_STRING_MAX 1024
|
||
|
|
|
||
|
|
// A buffer may be viewed at any byte offset, so the code units of a UTF-16
|
||
|
|
// string are not necessarily aligned within its backing store. Spans that are
|
||
|
|
// not are staged through an aligned copy.
|
||
|
|
#define BARE_BUFFER_STACK_UTF16_MAX (BARE_BUFFER_STACK_STRING_MAX / sizeof(utf16_t))
|
||
|
|
|
||
|
|
// A typed callback cannot raise, so an argument it rejects is reported as a
|
||
|
|
// result no successful call can produce and JavaScript raises on it. There is
|
||
|
|
// one sentinel per error the untyped callback raises directly, so that the
|
||
|
|
// error does not change once V8 takes the fast path.
|
||
|
|
#define BARE_BUFFER_OUT_OF_BOUNDS INT32_MIN
|
||
|
|
#define BARE_BUFFER_INVALID_BUFFER (INT32_MIN + 1)
|
||
|
|
#define BARE_BUFFER_INVALID_STRING (INT32_MIN + 2)
|
||
|
|
|
||
|
|
static inline bool
|
||
|
|
bare_buffer__is_aligned(const void *data) {
|
||
|
|
return ((uintptr_t) data & (sizeof(utf16_t) - 1)) == 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static _Atomic size_t bare_buffer__max_arraybuffer_length;
|
||
|
|
static _Atomic size_t bare_buffer__max_string_length;
|
||
|
|
|
||
|
|
static inline void
|
||
|
|
bare_buffer__read_limits(js_env_t *env) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_platform_t *platform;
|
||
|
|
err = js_get_env_platform(env, &platform);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
js_platform_limits_t limits = {
|
||
|
|
.version = 0,
|
||
|
|
};
|
||
|
|
|
||
|
|
err = js_get_platform_limits(platform, &limits);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
atomic_store_explicit(&bare_buffer__max_arraybuffer_length, limits.arraybuffer_length, memory_order_relaxed);
|
||
|
|
atomic_store_explicit(&bare_buffer__max_string_length, limits.string_length, memory_order_relaxed);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline size_t
|
||
|
|
bare_buffer__arraybuffer_limit(void) {
|
||
|
|
return atomic_load_explicit(&bare_buffer__max_arraybuffer_length, memory_order_relaxed);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline size_t
|
||
|
|
bare_buffer__string_limit(void) {
|
||
|
|
return atomic_load_explicit(&bare_buffer__max_string_length, memory_order_relaxed);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Nothing between here and the raising helpers below raises an error. A typed
|
||
|
|
// callback is entered without a handle scope and V8 does not support reporting
|
||
|
|
// errors from one, so raising there aborts the process. An argument that does
|
||
|
|
// not check out is reported to the caller instead, and a condition that has to
|
||
|
|
// reach JavaScript is returned as a negative count for the caller to raise.
|
||
|
|
|
||
|
|
static inline bool
|
||
|
|
bare_buffer__is_buffer(js_env_t *env, js_value_t *value) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
bool is_arraybuffer;
|
||
|
|
err = js_is_arraybuffer(env, value, &is_arraybuffer);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (is_arraybuffer) return true;
|
||
|
|
|
||
|
|
bool is_shared;
|
||
|
|
err = js_is_sharedarraybuffer(env, value, &is_shared);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return is_shared;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int
|
||
|
|
bare_buffer__get_info(js_env_t *env, js_value_t *buffer, void **data, size_t *len) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
bool is_arraybuffer;
|
||
|
|
err = js_is_arraybuffer(env, buffer, &is_arraybuffer);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (is_arraybuffer) {
|
||
|
|
err = js_get_arraybuffer_info(env, buffer, data, len);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool is_shared;
|
||
|
|
err = js_is_sharedarraybuffer(env, buffer, &is_shared);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (is_shared) {
|
||
|
|
err = js_get_sharedarraybuffer_info(env, buffer, data, len);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline bool
|
||
|
|
bare_buffer__is_string(js_env_t *env, js_value_t *value) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
bool is_string;
|
||
|
|
err = js_is_string(env, value, &is_string);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return is_string;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Returns 0, or the sentinel for the argument that did not check out.
|
||
|
|
static inline int
|
||
|
|
bare_buffer__slice(js_env_t *env, js_value_t *buffer, int64_t offset, int64_t len, void **data) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
void *base;
|
||
|
|
size_t byte_len;
|
||
|
|
err = bare_buffer__get_info(env, buffer, &base, &byte_len);
|
||
|
|
if (err < 0) return BARE_BUFFER_INVALID_BUFFER;
|
||
|
|
|
||
|
|
if (offset < 0 || len < 0 || (uint64_t) offset + (uint64_t) len > byte_len) {
|
||
|
|
return BARE_BUFFER_OUT_OF_BOUNDS;
|
||
|
|
}
|
||
|
|
|
||
|
|
// A detached or zero length store has no address, and the string and codec
|
||
|
|
// APIs read a null destination as a request for the length a write would need
|
||
|
|
// rather than as a write of no bytes.
|
||
|
|
static uint8_t nowhere;
|
||
|
|
|
||
|
|
// Only an empty store lacks an address, so the bounds check above has already
|
||
|
|
// narrowed the span to nothing and the single byte is never reached.
|
||
|
|
assert(base != NULL || len == 0);
|
||
|
|
|
||
|
|
*data = base == NULL ? &nowhere : (uint8_t *) base + offset;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int
|
||
|
|
bare_buffer__alloc(size_t len, void **result) {
|
||
|
|
void *data = malloc(len);
|
||
|
|
|
||
|
|
if (data == NULL) return -1;
|
||
|
|
|
||
|
|
*result = data;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer__write_utf8(js_env_t *env, js_value_t *string, utf8_t *buf, int64_t len) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t str_len;
|
||
|
|
err = js_get_value_string_utf8(env, string, NULL, 0, &str_len);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
size_t written;
|
||
|
|
|
||
|
|
if (str_len <= (size_t) len) {
|
||
|
|
err = js_get_value_string_utf8(env, string, buf, str_len, &written);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return written;
|
||
|
|
}
|
||
|
|
|
||
|
|
// A truncated write stops on a code point boundary, so it leaves at most the
|
||
|
|
// three bytes of an incomplete sequence. Keeping those is enough to undo the
|
||
|
|
// null terminator, which can only land among them.
|
||
|
|
size_t tail_at = (size_t) len - (len < 3 ? (size_t) len : 3);
|
||
|
|
|
||
|
|
utf8_t tail[3];
|
||
|
|
memcpy(tail, &buf[tail_at], (size_t) len - tail_at);
|
||
|
|
|
||
|
|
err = js_get_value_string_utf8(env, string, buf, len, &written);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (written < (size_t) len && written >= tail_at) {
|
||
|
|
memcpy(&buf[written], &tail[written - tail_at], (size_t) len - written);
|
||
|
|
}
|
||
|
|
|
||
|
|
return written;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer__write_latin1(js_env_t *env, js_value_t *string, latin1_t *buf, int64_t len) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t str_len;
|
||
|
|
err = js_get_value_string_latin1(env, string, NULL, 0, &str_len);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (str_len > (size_t) len) str_len = len;
|
||
|
|
|
||
|
|
size_t written;
|
||
|
|
err = js_get_value_string_latin1(env, string, buf, str_len, &written);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return written;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer__write_utf16le(js_env_t *env, js_value_t *string, uint8_t *bytes, int64_t len) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t str_len;
|
||
|
|
err = js_get_value_string_utf16le(env, string, NULL, 0, &str_len);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
size_t capacity = (size_t) len / sizeof(utf16_t);
|
||
|
|
|
||
|
|
if (str_len > capacity) str_len = capacity;
|
||
|
|
|
||
|
|
size_t written;
|
||
|
|
|
||
|
|
if (bare_buffer__is_aligned(bytes)) {
|
||
|
|
err = js_get_value_string_utf16le(env, string, (utf16_t *) bytes, str_len, &written);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return written * sizeof(utf16_t);
|
||
|
|
}
|
||
|
|
|
||
|
|
utf16_t stack[BARE_BUFFER_STACK_UTF16_MAX];
|
||
|
|
utf16_t *str = stack;
|
||
|
|
|
||
|
|
if (str_len > BARE_BUFFER_STACK_UTF16_MAX) {
|
||
|
|
err = bare_buffer__alloc(str_len * sizeof(utf16_t), (void **) &str);
|
||
|
|
if (err < 0) return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = js_get_value_string_utf16le(env, string, str, str_len, &written);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
memcpy(bytes, str, written * sizeof(utf16_t));
|
||
|
|
|
||
|
|
if (str != stack) free(str);
|
||
|
|
|
||
|
|
return written * sizeof(utf16_t);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer__write_base64(js_env_t *env, js_value_t *string, utf8_t *buf, int64_t len) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_string_encoding_t encoding;
|
||
|
|
const void *str;
|
||
|
|
size_t str_len;
|
||
|
|
|
||
|
|
js_string_view_t *str_view;
|
||
|
|
err = js_get_string_view(env, string, &encoding, &str, &str_len, &str_view);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
size_t written = len;
|
||
|
|
|
||
|
|
if (encoding == js_utf16le) {
|
||
|
|
err = base64_decode_utf16le(str, str_len, buf, &written);
|
||
|
|
} else {
|
||
|
|
err = base64_decode_utf8(str, str_len, buf, &written);
|
||
|
|
}
|
||
|
|
|
||
|
|
int64_t decoded = err == 0 ? (int64_t) written : -1;
|
||
|
|
|
||
|
|
err = js_release_string_view(env, str_view);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return decoded;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer__write_hex(js_env_t *env, js_value_t *string, utf8_t *buf, int64_t len) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_string_encoding_t encoding;
|
||
|
|
const void *str;
|
||
|
|
size_t str_len;
|
||
|
|
|
||
|
|
js_string_view_t *str_view;
|
||
|
|
err = js_get_string_view(env, string, &encoding, &str, &str_len, &str_view);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
size_t written = len;
|
||
|
|
|
||
|
|
if (encoding == js_utf16le) {
|
||
|
|
err = hex_decode_utf16le(str, str_len, buf, &written);
|
||
|
|
} else {
|
||
|
|
err = hex_decode_utf8(str, str_len, buf, &written);
|
||
|
|
}
|
||
|
|
|
||
|
|
int64_t decoded = err == 0 ? (int64_t) written : -1;
|
||
|
|
|
||
|
|
err = js_release_string_view(env, str_view);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return decoded;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool
|
||
|
|
bare_buffer__check_buffer(js_env_t *env, js_value_t *value) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
if (bare_buffer__is_buffer(env, value)) return true;
|
||
|
|
|
||
|
|
err = js_throw_type_error(env, NULL, "Buffer must be an array buffer");
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool
|
||
|
|
bare_buffer__check_number(js_env_t *env, js_value_t *value, const char *message) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
bool is_number;
|
||
|
|
err = js_is_number(env, value, &is_number);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!is_number) {
|
||
|
|
err = js_throw_type_error(env, NULL, message);
|
||
|
|
assert(err == 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
return is_number;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool
|
||
|
|
bare_buffer__check_string(js_env_t *env, js_value_t *value, const char *message) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
if (bare_buffer__is_string(env, value)) return true;
|
||
|
|
|
||
|
|
err = js_throw_type_error(env, NULL, message);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer__view_out_of_bounds(js_env_t *env) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
err = js_throw_range_error(env, NULL, "View is out of bounds of its backing store");
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer__out_of_memory(js_env_t *env) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
err = js_throw_error(env, NULL, "Out of memory");
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer__invalid_string_length(js_env_t *env) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
err = js_throw_range_error(env, NULL, "Invalid string length");
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int64_t
|
||
|
|
bare_buffer__to_int64(js_env_t *env, js_value_t *value) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
int64_t result;
|
||
|
|
err = js_get_value_int64(env, value, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_alloc(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 1;
|
||
|
|
js_value_t *argv[1];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_number(env, argv[0], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[0]);
|
||
|
|
|
||
|
|
if (len < 0) {
|
||
|
|
err = js_throw_range_error(env, NULL, "Length must not be negative");
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((uint64_t) len > bare_buffer__arraybuffer_limit()) {
|
||
|
|
err = js_throw_range_error(env, NULL, "Length must not exceed the maximum array buffer length");
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_arraybuffer(env, len, NULL, &result);
|
||
|
|
if (err < 0) return NULL;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_alloc_unsafe(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 1;
|
||
|
|
js_value_t *argv[1];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_number(env, argv[0], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[0]);
|
||
|
|
|
||
|
|
if (len < 0) {
|
||
|
|
err = js_throw_range_error(env, NULL, "Length must not be negative");
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((uint64_t) len > bare_buffer__arraybuffer_limit()) {
|
||
|
|
err = js_throw_range_error(env, NULL, "Length must not exceed the maximum array buffer length");
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_unsafe_arraybuffer(env, len, NULL, &result);
|
||
|
|
if (err < 0) return NULL;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer_typed_byte_length_utf8(js_value_t *receiver, js_value_t *str, js_typed_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__is_string(env, str)) return BARE_BUFFER_INVALID_STRING;
|
||
|
|
|
||
|
|
size_t str_len;
|
||
|
|
err = js_get_value_string_utf8(env, str, NULL, 0, &str_len);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return str_len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_byte_length_utf8(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 1;
|
||
|
|
js_value_t *argv[1];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_string(env, argv[0], "String must be a string")) return NULL;
|
||
|
|
|
||
|
|
size_t str_len;
|
||
|
|
err = js_get_value_string_utf8(env, argv[0], NULL, 0, &str_len);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int64(env, str_len, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_to_string_utf8(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 3;
|
||
|
|
js_value_t *argv[3];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
utf8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
if (err < 0) return bare_buffer__view_out_of_bounds(env);
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_string_utf8(env, buf, len, &result);
|
||
|
|
if (err < 0) return NULL;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t
|
||
|
|
bare_buffer_typed_validate_utf8(
|
||
|
|
js_value_t *receiver,
|
||
|
|
js_value_t *handle,
|
||
|
|
int64_t offset,
|
||
|
|
int64_t len,
|
||
|
|
js_typed_callback_info_t *info
|
||
|
|
) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
utf8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, handle, offset, len, (void **) &buf);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
return utf8_validate(buf, len);
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_validate_utf8(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 3;
|
||
|
|
js_value_t *argv[3];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
utf8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
|
||
|
|
int32_t valid = err == 0 ? utf8_validate(buf, len) : err;
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int32(env, valid, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer_typed_write_utf8(
|
||
|
|
js_value_t *receiver,
|
||
|
|
js_value_t *handle,
|
||
|
|
int64_t offset,
|
||
|
|
int64_t len,
|
||
|
|
js_value_t *string,
|
||
|
|
js_typed_callback_info_t *info
|
||
|
|
) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__is_buffer(env, handle)) return BARE_BUFFER_INVALID_BUFFER;
|
||
|
|
if (!bare_buffer__is_string(env, string)) return BARE_BUFFER_INVALID_STRING;
|
||
|
|
|
||
|
|
utf8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, handle, offset, len, (void **) &buf);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
return bare_buffer__write_utf8(env, string, buf, len);
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_write_utf8(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 4;
|
||
|
|
js_value_t *argv[4];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_string(env, argv[3], "String must be a string")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
utf8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
|
||
|
|
int64_t written = err == 0 ? bare_buffer__write_utf8(env, argv[3], buf, len) : err;
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int64(env, written, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_to_string_utf16le(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 3;
|
||
|
|
js_value_t *argv[3];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
uint8_t *bytes;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &bytes);
|
||
|
|
if (err < 0) return bare_buffer__view_out_of_bounds(env);
|
||
|
|
|
||
|
|
size_t str_len = (size_t) len / sizeof(utf16_t);
|
||
|
|
|
||
|
|
if (str_len > bare_buffer__string_limit()) {
|
||
|
|
return bare_buffer__invalid_string_length(env);
|
||
|
|
}
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
|
||
|
|
if (bare_buffer__is_aligned(bytes)) {
|
||
|
|
err = js_create_string_utf16le(env, (utf16_t *) bytes, str_len, &result);
|
||
|
|
if (err < 0) return NULL;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
utf16_t stack[BARE_BUFFER_STACK_UTF16_MAX];
|
||
|
|
utf16_t *str = stack;
|
||
|
|
|
||
|
|
if (str_len > BARE_BUFFER_STACK_UTF16_MAX) {
|
||
|
|
err = bare_buffer__alloc(str_len * sizeof(utf16_t), (void **) &str);
|
||
|
|
if (err < 0) return bare_buffer__out_of_memory(env);
|
||
|
|
}
|
||
|
|
|
||
|
|
memcpy(str, bytes, str_len * sizeof(utf16_t));
|
||
|
|
|
||
|
|
err = js_create_string_utf16le(env, str, str_len, &result);
|
||
|
|
|
||
|
|
if (str != stack) free(str);
|
||
|
|
|
||
|
|
if (err < 0) return NULL;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer_typed_write_utf16le(
|
||
|
|
js_value_t *receiver,
|
||
|
|
js_value_t *handle,
|
||
|
|
int64_t offset,
|
||
|
|
int64_t len,
|
||
|
|
js_value_t *string,
|
||
|
|
js_typed_callback_info_t *info
|
||
|
|
) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__is_buffer(env, handle)) return BARE_BUFFER_INVALID_BUFFER;
|
||
|
|
if (!bare_buffer__is_string(env, string)) return BARE_BUFFER_INVALID_STRING;
|
||
|
|
|
||
|
|
uint8_t *bytes;
|
||
|
|
err = bare_buffer__slice(env, handle, offset, len, (void **) &bytes);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
return bare_buffer__write_utf16le(env, string, bytes, len);
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_write_utf16le(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 4;
|
||
|
|
js_value_t *argv[4];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_string(env, argv[3], "String must be a string")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
uint8_t *bytes;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &bytes);
|
||
|
|
|
||
|
|
int64_t written = err == 0 ? bare_buffer__write_utf16le(env, argv[3], bytes, len) : err;
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int64(env, written, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_to_string_latin1(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 3;
|
||
|
|
js_value_t *argv[3];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
latin1_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
if (err < 0) return bare_buffer__view_out_of_bounds(env);
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_string_latin1(env, buf, len, &result);
|
||
|
|
if (err < 0) return NULL;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer_typed_write_latin1(
|
||
|
|
js_value_t *receiver,
|
||
|
|
js_value_t *handle,
|
||
|
|
int64_t offset,
|
||
|
|
int64_t len,
|
||
|
|
js_value_t *string,
|
||
|
|
js_typed_callback_info_t *info
|
||
|
|
) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__is_buffer(env, handle)) return BARE_BUFFER_INVALID_BUFFER;
|
||
|
|
if (!bare_buffer__is_string(env, string)) return BARE_BUFFER_INVALID_STRING;
|
||
|
|
|
||
|
|
latin1_t *buf;
|
||
|
|
err = bare_buffer__slice(env, handle, offset, len, (void **) &buf);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
return bare_buffer__write_latin1(env, string, buf, len);
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_write_latin1(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 4;
|
||
|
|
js_value_t *argv[4];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_string(env, argv[3], "String must be a string")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
latin1_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
|
||
|
|
int64_t written = err == 0 ? bare_buffer__write_latin1(env, argv[3], buf, len) : err;
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int64(env, written, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_to_string_base64(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 3;
|
||
|
|
js_value_t *argv[3];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
utf8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
if (err < 0) return bare_buffer__view_out_of_bounds(env);
|
||
|
|
|
||
|
|
size_t str_len;
|
||
|
|
err = base64_encode_utf8(buf, len, NULL, &str_len);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (str_len > bare_buffer__string_limit()) {
|
||
|
|
return bare_buffer__invalid_string_length(env);
|
||
|
|
}
|
||
|
|
|
||
|
|
utf8_t stack[BARE_BUFFER_STACK_STRING_MAX];
|
||
|
|
utf8_t *str = stack;
|
||
|
|
|
||
|
|
if (str_len > sizeof(stack)) {
|
||
|
|
err = bare_buffer__alloc(str_len, (void **) &str);
|
||
|
|
if (err < 0) return bare_buffer__out_of_memory(env);
|
||
|
|
}
|
||
|
|
|
||
|
|
err = base64_encode_utf8(buf, len, str, &str_len);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_string_latin1(env, str, str_len, &result);
|
||
|
|
|
||
|
|
if (str != stack) free(str);
|
||
|
|
|
||
|
|
if (err < 0) return NULL;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_to_string_base64url(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 3;
|
||
|
|
js_value_t *argv[3];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
utf8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
if (err < 0) return bare_buffer__view_out_of_bounds(env);
|
||
|
|
|
||
|
|
size_t str_len;
|
||
|
|
err = base64url_encode_utf8(buf, len, NULL, &str_len);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (str_len > bare_buffer__string_limit()) {
|
||
|
|
return bare_buffer__invalid_string_length(env);
|
||
|
|
}
|
||
|
|
|
||
|
|
utf8_t stack[BARE_BUFFER_STACK_STRING_MAX];
|
||
|
|
utf8_t *str = stack;
|
||
|
|
|
||
|
|
if (str_len > sizeof(stack)) {
|
||
|
|
err = bare_buffer__alloc(str_len, (void **) &str);
|
||
|
|
if (err < 0) return bare_buffer__out_of_memory(env);
|
||
|
|
}
|
||
|
|
|
||
|
|
err = base64url_encode_utf8(buf, len, str, &str_len);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_string_latin1(env, str, str_len, &result);
|
||
|
|
|
||
|
|
if (str != stack) free(str);
|
||
|
|
|
||
|
|
if (err < 0) return NULL;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer_typed_write_base64(
|
||
|
|
js_value_t *receiver,
|
||
|
|
js_value_t *handle,
|
||
|
|
int64_t offset,
|
||
|
|
int64_t len,
|
||
|
|
js_value_t *string,
|
||
|
|
js_typed_callback_info_t *info
|
||
|
|
) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__is_buffer(env, handle)) return BARE_BUFFER_INVALID_BUFFER;
|
||
|
|
if (!bare_buffer__is_string(env, string)) return BARE_BUFFER_INVALID_STRING;
|
||
|
|
|
||
|
|
utf8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, handle, offset, len, (void **) &buf);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
return bare_buffer__write_base64(env, string, buf, len);
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_write_base64(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 4;
|
||
|
|
js_value_t *argv[4];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_string(env, argv[3], "String must be a string")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
utf8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
|
||
|
|
int64_t decoded = err == 0 ? bare_buffer__write_base64(env, argv[3], buf, len) : err;
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int64(env, decoded, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_to_string_hex(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 3;
|
||
|
|
js_value_t *argv[3];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
utf8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
if (err < 0) return bare_buffer__view_out_of_bounds(env);
|
||
|
|
|
||
|
|
size_t str_len;
|
||
|
|
err = hex_encode_utf8(buf, len, NULL, &str_len);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (str_len > bare_buffer__string_limit()) {
|
||
|
|
return bare_buffer__invalid_string_length(env);
|
||
|
|
}
|
||
|
|
|
||
|
|
utf8_t stack[BARE_BUFFER_STACK_STRING_MAX];
|
||
|
|
utf8_t *str = stack;
|
||
|
|
|
||
|
|
if (str_len > sizeof(stack)) {
|
||
|
|
err = bare_buffer__alloc(str_len, (void **) &str);
|
||
|
|
if (err < 0) return bare_buffer__out_of_memory(env);
|
||
|
|
}
|
||
|
|
|
||
|
|
err = hex_encode_utf8(buf, len, str, &str_len);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_string_latin1(env, str, str_len, &result);
|
||
|
|
|
||
|
|
if (str != stack) free(str);
|
||
|
|
|
||
|
|
if (err < 0) return NULL;
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer_typed_write_hex(
|
||
|
|
js_value_t *receiver,
|
||
|
|
js_value_t *handle,
|
||
|
|
int64_t offset,
|
||
|
|
int64_t len,
|
||
|
|
js_value_t *string,
|
||
|
|
js_typed_callback_info_t *info
|
||
|
|
) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__is_buffer(env, handle)) return BARE_BUFFER_INVALID_BUFFER;
|
||
|
|
if (!bare_buffer__is_string(env, string)) return BARE_BUFFER_INVALID_STRING;
|
||
|
|
|
||
|
|
utf8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, handle, offset, len, (void **) &buf);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
return bare_buffer__write_hex(env, string, buf, len);
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_write_hex(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 4;
|
||
|
|
js_value_t *argv[4];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_string(env, argv[3], "String must be a string")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
utf8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
|
||
|
|
int64_t decoded = err == 0 ? bare_buffer__write_hex(env, argv[3], buf, len) : err;
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int64(env, decoded, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t
|
||
|
|
bare_buffer_typed_validate_ascii(
|
||
|
|
js_value_t *receiver,
|
||
|
|
js_value_t *handle,
|
||
|
|
int64_t offset,
|
||
|
|
int64_t len,
|
||
|
|
js_typed_callback_info_t *info
|
||
|
|
) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
ascii_t *buf;
|
||
|
|
err = bare_buffer__slice(env, handle, offset, len, (void **) &buf);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
return ascii_validate(buf, len);
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_validate_ascii(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 3;
|
||
|
|
js_value_t *argv[3];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
ascii_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
|
||
|
|
int32_t valid = err == 0 ? ascii_validate(buf, len) : err;
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int32(env, valid, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void
|
||
|
|
bare_buffer__swap16(uint8_t *data, size_t len) {
|
||
|
|
for (size_t i = 0; i + 1 < len; i += 2) {
|
||
|
|
uint8_t a = data[i];
|
||
|
|
data[i] = data[i + 1];
|
||
|
|
data[i + 1] = a;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void
|
||
|
|
bare_buffer__swap32(uint8_t *data, size_t len) {
|
||
|
|
for (size_t i = 0; i + 3 < len; i += 4) {
|
||
|
|
uint8_t a = data[i];
|
||
|
|
uint8_t b = data[i + 1];
|
||
|
|
data[i] = data[i + 3];
|
||
|
|
data[i + 1] = data[i + 2];
|
||
|
|
data[i + 2] = b;
|
||
|
|
data[i + 3] = a;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void
|
||
|
|
bare_buffer__swap64(uint8_t *data, size_t len) {
|
||
|
|
for (size_t i = 0; i + 7 < len; i += 8) {
|
||
|
|
for (size_t j = 0; j < 4; j++) {
|
||
|
|
uint8_t a = data[i + j];
|
||
|
|
data[i + j] = data[i + 7 - j];
|
||
|
|
data[i + 7 - j] = a;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static const uint8_t *
|
||
|
|
bare_buffer__memrchr(const uint8_t *data, uint8_t c, size_t len) {
|
||
|
|
const size_t chunk_len = 512;
|
||
|
|
|
||
|
|
while (len > 0) {
|
||
|
|
size_t take = len < chunk_len ? len : chunk_len;
|
||
|
|
|
||
|
|
const uint8_t *chunk = &data[len - take];
|
||
|
|
|
||
|
|
// When matches are dense the last one is within a few bytes of the end,
|
||
|
|
// which is far cheaper to find by looking than by narrowing.
|
||
|
|
size_t probe = take < 8 ? take : 8;
|
||
|
|
|
||
|
|
for (size_t i = 0; i < probe; i++) {
|
||
|
|
if (chunk[take - 1 - i] == c) return &chunk[take - 1 - i];
|
||
|
|
}
|
||
|
|
|
||
|
|
if (memchr(chunk, c, take - probe) != NULL) {
|
||
|
|
size_t lo = 0;
|
||
|
|
size_t hi = take - probe;
|
||
|
|
|
||
|
|
while (hi - lo > 1) {
|
||
|
|
size_t mid = lo + (hi - lo) / 2;
|
||
|
|
|
||
|
|
if (memchr(&chunk[mid], c, hi - mid) != NULL) lo = mid;
|
||
|
|
else hi = mid;
|
||
|
|
}
|
||
|
|
|
||
|
|
return &chunk[lo];
|
||
|
|
}
|
||
|
|
|
||
|
|
len -= take;
|
||
|
|
}
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer__index_of(const uint8_t *data, size_t len, const uint8_t *needle, size_t needle_len, int64_t start) {
|
||
|
|
if (needle_len == 0 || needle_len > len) return -1;
|
||
|
|
|
||
|
|
// Compared before narrowing, so that a start past the end cannot wrap into a
|
||
|
|
// valid position on a target where size_t is narrower than int64_t.
|
||
|
|
if (start < 0 || (uint64_t) start > len - needle_len) return -1;
|
||
|
|
|
||
|
|
size_t from = (size_t) start;
|
||
|
|
|
||
|
|
if (needle_len == 1) {
|
||
|
|
const uint8_t *at = memchr(&data[from], needle[0], len - from);
|
||
|
|
|
||
|
|
return at == NULL ? -1 : (int64_t) (at - data);
|
||
|
|
}
|
||
|
|
|
||
|
|
size_t end = len - needle_len + 1;
|
||
|
|
size_t i = from;
|
||
|
|
size_t rejected = 0;
|
||
|
|
|
||
|
|
while (i < end) {
|
||
|
|
const uint8_t *at = memchr(&data[i], needle[0], end - i);
|
||
|
|
|
||
|
|
if (at == NULL) return -1;
|
||
|
|
|
||
|
|
i = (size_t) (at - data);
|
||
|
|
|
||
|
|
if (memcmp(&data[i + 1], &needle[1], needle_len - 1) == 0) return (int64_t) i;
|
||
|
|
|
||
|
|
i++;
|
||
|
|
|
||
|
|
if (++rejected == 32) break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (i >= end) return -1;
|
||
|
|
|
||
|
|
size_t skip[256];
|
||
|
|
|
||
|
|
for (size_t j = 0; j < 256; j++) {
|
||
|
|
skip[j] = needle_len;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (size_t j = 0; j < needle_len - 1; j++) {
|
||
|
|
skip[needle[j]] = needle_len - 1 - j;
|
||
|
|
}
|
||
|
|
|
||
|
|
size_t last = needle_len - 1;
|
||
|
|
|
||
|
|
while (i < end) {
|
||
|
|
if (data[i + last] == needle[last] && memcmp(&data[i], needle, last) == 0) {
|
||
|
|
return (int64_t) i;
|
||
|
|
}
|
||
|
|
|
||
|
|
i += skip[data[i + last]];
|
||
|
|
}
|
||
|
|
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer__last_index_of(const uint8_t *data, size_t len, const uint8_t *needle, size_t needle_len, int64_t start) {
|
||
|
|
if (needle_len == 0 || needle_len > len) return -1;
|
||
|
|
|
||
|
|
// A start before the first byte names no position, the same reading that has
|
||
|
|
// the forward search report no match for a start past the last one.
|
||
|
|
if (start < 0) return -1;
|
||
|
|
|
||
|
|
size_t last = len - needle_len;
|
||
|
|
size_t from = (uint64_t) start > last ? last : (size_t) start;
|
||
|
|
|
||
|
|
if (needle_len == 1) {
|
||
|
|
const uint8_t *at = bare_buffer__memrchr(data, needle[0], from + 1);
|
||
|
|
|
||
|
|
return at == NULL ? -1 : (int64_t) (at - data);
|
||
|
|
}
|
||
|
|
|
||
|
|
size_t i = from;
|
||
|
|
size_t rejected = 0;
|
||
|
|
|
||
|
|
while (rejected < 32) {
|
||
|
|
const uint8_t *at = bare_buffer__memrchr(data, needle[0], i + 1);
|
||
|
|
|
||
|
|
if (at == NULL) return -1;
|
||
|
|
|
||
|
|
i = (size_t) (at - data);
|
||
|
|
|
||
|
|
if (memcmp(&data[i + 1], &needle[1], needle_len - 1) == 0) return (int64_t) i;
|
||
|
|
|
||
|
|
if (i == 0) return -1;
|
||
|
|
|
||
|
|
i--;
|
||
|
|
rejected++;
|
||
|
|
}
|
||
|
|
|
||
|
|
size_t skip[256];
|
||
|
|
|
||
|
|
for (size_t j = 0; j < 256; j++) {
|
||
|
|
skip[j] = needle_len;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (size_t j = needle_len - 1; j >= 1; j--) {
|
||
|
|
skip[needle[j]] = j;
|
||
|
|
}
|
||
|
|
|
||
|
|
while (true) {
|
||
|
|
if (data[i] == needle[0] && memcmp(&data[i + 1], &needle[1], needle_len - 1) == 0) {
|
||
|
|
return (int64_t) i;
|
||
|
|
}
|
||
|
|
|
||
|
|
size_t shift = skip[data[i]];
|
||
|
|
|
||
|
|
if (i < shift) return -1;
|
||
|
|
|
||
|
|
i -= shift;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int
|
||
|
|
bare_buffer__memcmp(void *a, size_t a_len, void *b, size_t b_len) {
|
||
|
|
int r = memcmp(a, b, a_len < b_len ? a_len : b_len);
|
||
|
|
|
||
|
|
if (r == 0) {
|
||
|
|
if (a_len < b_len) return -1;
|
||
|
|
if (a_len > b_len) return 1;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return r < 0 ? -1 : 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t
|
||
|
|
bare_buffer_typed_compare(
|
||
|
|
js_value_t *receiver,
|
||
|
|
js_value_t *a_handle,
|
||
|
|
int64_t a_offset,
|
||
|
|
int64_t a_len,
|
||
|
|
js_value_t *b_handle,
|
||
|
|
int64_t b_offset,
|
||
|
|
int64_t b_len,
|
||
|
|
js_typed_callback_info_t *info
|
||
|
|
) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__is_buffer(env, a_handle)) return BARE_BUFFER_INVALID_BUFFER;
|
||
|
|
if (!bare_buffer__is_buffer(env, b_handle)) return BARE_BUFFER_INVALID_BUFFER;
|
||
|
|
|
||
|
|
uint8_t *a;
|
||
|
|
err = bare_buffer__slice(env, a_handle, a_offset, a_len, (void **) &a);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
uint8_t *b;
|
||
|
|
err = bare_buffer__slice(env, b_handle, b_offset, b_len, (void **) &b);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
return bare_buffer__memcmp(a, a_len, b, b_len);
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_compare(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 6;
|
||
|
|
js_value_t *argv[6];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[3])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[4], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[5], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t a_offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t a_len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
int64_t b_offset = bare_buffer__to_int64(env, argv[4]);
|
||
|
|
int64_t b_len = bare_buffer__to_int64(env, argv[5]);
|
||
|
|
|
||
|
|
uint8_t *a;
|
||
|
|
err = bare_buffer__slice(env, argv[0], a_offset, a_len, (void **) &a);
|
||
|
|
|
||
|
|
int32_t comparison = err;
|
||
|
|
|
||
|
|
if (err == 0) {
|
||
|
|
uint8_t *b;
|
||
|
|
err = bare_buffer__slice(env, argv[3], b_offset, b_len, (void **) &b);
|
||
|
|
|
||
|
|
comparison = err == 0 ? bare_buffer__memcmp(a, a_len, b, b_len) : err;
|
||
|
|
}
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int32(env, comparison, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t
|
||
|
|
bare_buffer_typed_swap16(
|
||
|
|
js_value_t *receiver,
|
||
|
|
js_value_t *handle,
|
||
|
|
int64_t offset,
|
||
|
|
int64_t len,
|
||
|
|
js_typed_callback_info_t *info
|
||
|
|
) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
uint8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, handle, offset, len, (void **) &buf);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
bare_buffer__swap16(buf, len);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_swap16(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 3;
|
||
|
|
js_value_t *argv[3];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
uint8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
|
||
|
|
if (err == 0) bare_buffer__swap16(buf, len);
|
||
|
|
|
||
|
|
int32_t swapped = err;
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int32(env, swapped, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t
|
||
|
|
bare_buffer_typed_swap32(
|
||
|
|
js_value_t *receiver,
|
||
|
|
js_value_t *handle,
|
||
|
|
int64_t offset,
|
||
|
|
int64_t len,
|
||
|
|
js_typed_callback_info_t *info
|
||
|
|
) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
uint8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, handle, offset, len, (void **) &buf);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
bare_buffer__swap32(buf, len);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_swap32(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 3;
|
||
|
|
js_value_t *argv[3];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
uint8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
|
||
|
|
if (err == 0) bare_buffer__swap32(buf, len);
|
||
|
|
|
||
|
|
int32_t swapped = err;
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int32(env, swapped, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int32_t
|
||
|
|
bare_buffer_typed_swap64(
|
||
|
|
js_value_t *receiver,
|
||
|
|
js_value_t *handle,
|
||
|
|
int64_t offset,
|
||
|
|
int64_t len,
|
||
|
|
js_typed_callback_info_t *info
|
||
|
|
) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
uint8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, handle, offset, len, (void **) &buf);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
bare_buffer__swap64(buf, len);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_swap64(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 3;
|
||
|
|
js_value_t *argv[3];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
uint8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
|
||
|
|
if (err == 0) bare_buffer__swap64(buf, len);
|
||
|
|
|
||
|
|
int32_t swapped = err;
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int32(env, swapped, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer_typed_index_of(
|
||
|
|
js_value_t *receiver,
|
||
|
|
js_value_t *handle,
|
||
|
|
int64_t offset,
|
||
|
|
int64_t len,
|
||
|
|
js_value_t *needle_handle,
|
||
|
|
int64_t needle_offset,
|
||
|
|
int64_t needle_len,
|
||
|
|
int64_t from,
|
||
|
|
js_typed_callback_info_t *info
|
||
|
|
) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__is_buffer(env, handle)) return BARE_BUFFER_INVALID_BUFFER;
|
||
|
|
if (!bare_buffer__is_buffer(env, needle_handle)) return BARE_BUFFER_INVALID_BUFFER;
|
||
|
|
|
||
|
|
uint8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, handle, offset, len, (void **) &buf);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
uint8_t *needle;
|
||
|
|
err = bare_buffer__slice(env, needle_handle, needle_offset, needle_len, (void **) &needle);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
return bare_buffer__index_of(buf, len, needle, needle_len, from);
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_index_of(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 7;
|
||
|
|
js_value_t *argv[7];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[3])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[4], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[5], "Length must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[6], "Start must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t needle_offset = bare_buffer__to_int64(env, argv[4]);
|
||
|
|
int64_t needle_len = bare_buffer__to_int64(env, argv[5]);
|
||
|
|
int64_t from = bare_buffer__to_int64(env, argv[6]);
|
||
|
|
|
||
|
|
uint8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
|
||
|
|
int64_t at = err;
|
||
|
|
|
||
|
|
if (err == 0) {
|
||
|
|
uint8_t *needle;
|
||
|
|
err = bare_buffer__slice(env, argv[3], needle_offset, needle_len, (void **) &needle);
|
||
|
|
|
||
|
|
at = err == 0 ? bare_buffer__index_of(buf, len, needle, needle_len, from) : err;
|
||
|
|
}
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int64(env, at, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int64_t
|
||
|
|
bare_buffer_typed_last_index_of(
|
||
|
|
js_value_t *receiver,
|
||
|
|
js_value_t *handle,
|
||
|
|
int64_t offset,
|
||
|
|
int64_t len,
|
||
|
|
js_value_t *needle_handle,
|
||
|
|
int64_t needle_offset,
|
||
|
|
int64_t needle_len,
|
||
|
|
int64_t from,
|
||
|
|
js_typed_callback_info_t *info
|
||
|
|
) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_env_t *env;
|
||
|
|
err = js_get_typed_callback_info(info, &env, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__is_buffer(env, handle)) return BARE_BUFFER_INVALID_BUFFER;
|
||
|
|
if (!bare_buffer__is_buffer(env, needle_handle)) return BARE_BUFFER_INVALID_BUFFER;
|
||
|
|
|
||
|
|
uint8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, handle, offset, len, (void **) &buf);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
uint8_t *needle;
|
||
|
|
err = bare_buffer__slice(env, needle_handle, needle_offset, needle_len, (void **) &needle);
|
||
|
|
if (err != 0) return err;
|
||
|
|
|
||
|
|
return bare_buffer__last_index_of(buf, len, needle, needle_len, from);
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_last_index_of(js_env_t *env, js_callback_info_t *info) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
size_t argc = 7;
|
||
|
|
js_value_t *argv[7];
|
||
|
|
|
||
|
|
err = js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[0])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[1], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[2], "Length must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t offset = bare_buffer__to_int64(env, argv[1]);
|
||
|
|
int64_t len = bare_buffer__to_int64(env, argv[2]);
|
||
|
|
|
||
|
|
if (!bare_buffer__check_buffer(env, argv[3])) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[4], "Offset must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[5], "Length must be a number")) return NULL;
|
||
|
|
if (!bare_buffer__check_number(env, argv[6], "Start must be a number")) return NULL;
|
||
|
|
|
||
|
|
int64_t needle_offset = bare_buffer__to_int64(env, argv[4]);
|
||
|
|
int64_t needle_len = bare_buffer__to_int64(env, argv[5]);
|
||
|
|
int64_t from = bare_buffer__to_int64(env, argv[6]);
|
||
|
|
|
||
|
|
uint8_t *buf;
|
||
|
|
err = bare_buffer__slice(env, argv[0], offset, len, (void **) &buf);
|
||
|
|
|
||
|
|
int64_t at = err;
|
||
|
|
|
||
|
|
if (err == 0) {
|
||
|
|
uint8_t *needle;
|
||
|
|
err = bare_buffer__slice(env, argv[3], needle_offset, needle_len, (void **) &needle);
|
||
|
|
|
||
|
|
at = err == 0 ? bare_buffer__last_index_of(buf, len, needle, needle_len, from) : err;
|
||
|
|
}
|
||
|
|
|
||
|
|
js_value_t *result;
|
||
|
|
err = js_create_int64(env, at, &result);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static js_value_t *
|
||
|
|
bare_buffer_exports(js_env_t *env, js_value_t *exports) {
|
||
|
|
int err;
|
||
|
|
|
||
|
|
js_value_t *constants;
|
||
|
|
err = js_create_object(env, &constants);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
err = js_set_named_property(env, exports, "constants", constants);
|
||
|
|
assert(err == 0);
|
||
|
|
|
||
|
|
bare_buffer__read_limits(env);
|
||
|
|
|
||
|
|
#define V(name, value) \
|
||
|
|
{ \
|
||
|
|
js_value_t *val; \
|
||
|
|
err = js_create_int64(env, value, &val); \
|
||
|
|
assert(err == 0); \
|
||
|
|
err = js_set_named_property(env, constants, name, val); \
|
||
|
|
assert(err == 0); \
|
||
|
|
}
|
||
|
|
|
||
|
|
V("MAX_LENGTH", bare_buffer__arraybuffer_limit());
|
||
|
|
V("MAX_STRING_LENGTH", bare_buffer__string_limit());
|
||
|
|
#undef V
|
||
|
|
|
||
|
|
#define V(name, untyped, signature, typed) \
|
||
|
|
{ \
|
||
|
|
js_value_t *val; \
|
||
|
|
if (signature) { \
|
||
|
|
err = js_create_typed_function(env, name, -1, untyped, signature, typed, NULL, &val); \
|
||
|
|
assert(err == 0); \
|
||
|
|
} else { \
|
||
|
|
err = js_create_function(env, name, -1, untyped, NULL, &val); \
|
||
|
|
assert(err == 0); \
|
||
|
|
} \
|
||
|
|
err = js_set_named_property(env, exports, name, val); \
|
||
|
|
assert(err == 0); \
|
||
|
|
}
|
||
|
|
|
||
|
|
V("alloc", bare_buffer_alloc, NULL, NULL);
|
||
|
|
|
||
|
|
V("allocUnsafe", bare_buffer_alloc_unsafe, NULL, NULL);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"byteLengthUTF8",
|
||
|
|
bare_buffer_byte_length_utf8,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int64,
|
||
|
|
.args_len = 2,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_string,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_byte_length_utf8
|
||
|
|
);
|
||
|
|
|
||
|
|
V("toStringUTF8", bare_buffer_to_string_utf8, NULL, NULL);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"validateUTF8",
|
||
|
|
bare_buffer_validate_utf8,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int32,
|
||
|
|
.args_len = 4,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_validate_utf8
|
||
|
|
);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"writeUTF8",
|
||
|
|
bare_buffer_write_utf8,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int64,
|
||
|
|
.args_len = 5,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
js_string,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_write_utf8
|
||
|
|
);
|
||
|
|
|
||
|
|
V("toStringUTF16LE", bare_buffer_to_string_utf16le, NULL, NULL);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"writeUTF16LE",
|
||
|
|
bare_buffer_write_utf16le,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int64,
|
||
|
|
.args_len = 5,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
js_string,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_write_utf16le
|
||
|
|
);
|
||
|
|
|
||
|
|
V("toStringLatin1", bare_buffer_to_string_latin1, NULL, NULL);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"writeLatin1",
|
||
|
|
bare_buffer_write_latin1,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int64,
|
||
|
|
.args_len = 5,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
js_string,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_write_latin1
|
||
|
|
);
|
||
|
|
|
||
|
|
V("toStringBase64", bare_buffer_to_string_base64, NULL, NULL);
|
||
|
|
V("toStringBase64URL", bare_buffer_to_string_base64url, NULL, NULL);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"writeBase64",
|
||
|
|
bare_buffer_write_base64,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int64,
|
||
|
|
.args_len = 5,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
js_string,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_write_base64
|
||
|
|
);
|
||
|
|
|
||
|
|
V("toStringHex", bare_buffer_to_string_hex, NULL, NULL);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"writeHex",
|
||
|
|
bare_buffer_write_hex,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int64,
|
||
|
|
.args_len = 5,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
js_string,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_write_hex
|
||
|
|
);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"validateAscii",
|
||
|
|
bare_buffer_validate_ascii,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int32,
|
||
|
|
.args_len = 4,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_validate_ascii
|
||
|
|
);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"swap16",
|
||
|
|
bare_buffer_swap16,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int32,
|
||
|
|
.args_len = 4,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_swap16
|
||
|
|
);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"swap32",
|
||
|
|
bare_buffer_swap32,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int32,
|
||
|
|
.args_len = 4,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_swap32
|
||
|
|
);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"swap64",
|
||
|
|
bare_buffer_swap64,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int32,
|
||
|
|
.args_len = 4,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_swap64
|
||
|
|
);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"indexOf",
|
||
|
|
bare_buffer_index_of,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int64,
|
||
|
|
.args_len = 8,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_index_of
|
||
|
|
);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"lastIndexOf",
|
||
|
|
bare_buffer_last_index_of,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int64,
|
||
|
|
.args_len = 8,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_last_index_of
|
||
|
|
);
|
||
|
|
|
||
|
|
V(
|
||
|
|
"compare",
|
||
|
|
bare_buffer_compare,
|
||
|
|
&((js_callback_signature_t){
|
||
|
|
.version = 0,
|
||
|
|
.result = js_int32,
|
||
|
|
.args_len = 7,
|
||
|
|
.args = (int[]){
|
||
|
|
js_object,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
js_object,
|
||
|
|
js_int64,
|
||
|
|
js_int64,
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
bare_buffer_typed_compare
|
||
|
|
);
|
||
|
|
#undef V
|
||
|
|
|
||
|
|
return exports;
|
||
|
|
}
|
||
|
|
|
||
|
|
BARE_MODULE(bare_buffer, bare_buffer_exports)
|