629 lines
18 KiB
C
629 lines
18 KiB
C
#define BLOB_IMPLEMENTATION
|
|
#include "blob.h"
|
|
#include "quickjs.h"
|
|
#include "qjs_blob.h"
|
|
#include "qjs_macros.h"
|
|
|
|
// Get countof from macros if not defined
|
|
#ifndef countof
|
|
#define countof(x) (sizeof(x)/sizeof((x)[0]))
|
|
#endif
|
|
|
|
// Free function for blob
|
|
void blob_free(JSRuntime *rt, blob *b)
|
|
{
|
|
if (b) {
|
|
blob_destroy(b);
|
|
}
|
|
}
|
|
|
|
// Use QJSCLASS macro to generate class boilerplate
|
|
QJSCLASS(blob,)
|
|
|
|
// Constructor function for blob
|
|
static JSValue js_blob_constructor(JSContext *ctx, JSValueConst new_target,
|
|
int argc, JSValueConst *argv) {
|
|
blob *bd = NULL;
|
|
|
|
// new Blob()
|
|
if (argc == 0) {
|
|
// empty antestone blob
|
|
bd = blob_new(0);
|
|
}
|
|
// new Blob(capacity)
|
|
else if (argc == 1 && JS_IsNumber(argv[0])) {
|
|
int64_t capacity_bits;
|
|
if (JS_ToInt64(ctx, &capacity_bits, argv[0]) < 0) {
|
|
return JS_EXCEPTION;
|
|
}
|
|
if (capacity_bits < 0) capacity_bits = 0;
|
|
bd = blob_new((size_t)capacity_bits);
|
|
}
|
|
// new Blob(length, logical/random)
|
|
else if (argc == 2 && JS_IsNumber(argv[0])) {
|
|
int64_t length_bits;
|
|
if (JS_ToInt64(ctx, &length_bits, argv[0]) < 0) {
|
|
return JS_EXCEPTION;
|
|
}
|
|
if (length_bits < 0) length_bits = 0;
|
|
|
|
if (JS_IsBool(argv[1])) {
|
|
// Fill with all 0s or all 1s
|
|
int is_one = JS_ToBool(ctx, argv[1]);
|
|
bd = blob_new_with_fill((size_t)length_bits, is_one);
|
|
} else if (JS_IsFunction(ctx, argv[1])) {
|
|
// Random function provided - call it for each bit
|
|
bd = blob_new((size_t)length_bits);
|
|
if (bd) {
|
|
bd->bit_length = length_bits;
|
|
for (size_t i = 0; i < length_bits; i++) {
|
|
JSValue randval = JS_Call(ctx, argv[1], JS_UNDEFINED, 0, NULL);
|
|
if (JS_IsException(randval)) {
|
|
blob_destroy(bd);
|
|
return JS_EXCEPTION;
|
|
}
|
|
int64_t fitval;
|
|
JS_ToInt64(ctx, &fitval, randval);
|
|
JS_FreeValue(ctx, randval);
|
|
|
|
// Set bit based on random value
|
|
size_t byte_idx = i / 8;
|
|
size_t bit_idx = i % 8;
|
|
if (fitval & 1) {
|
|
bd->data[byte_idx] |= (1 << bit_idx);
|
|
} else {
|
|
bd->data[byte_idx] &= ~(1 << bit_idx);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
return JS_ThrowTypeError(ctx, "Second argument must be boolean or random function");
|
|
}
|
|
}
|
|
// new Blob(blob, from, to)
|
|
else if (argc >= 1 && JS_IsObject(argv[0])) {
|
|
// we try copying from another blob if it's of the same class
|
|
blob *src = js2blob(ctx, argv[0]);
|
|
if (!src) {
|
|
return JS_ThrowTypeError(ctx, "Blob constructor: argument 1 not a blob");
|
|
}
|
|
int64_t from = 0, to = (int64_t)src->bit_length;
|
|
if (argc >= 2 && JS_IsNumber(argv[1])) {
|
|
JS_ToInt64(ctx, &from, argv[1]);
|
|
if (from < 0) from = 0;
|
|
}
|
|
if (argc >= 3 && JS_IsNumber(argv[2])) {
|
|
JS_ToInt64(ctx, &to, argv[2]);
|
|
if (to < from) to = from;
|
|
if (to > (int64_t)src->bit_length) to = (int64_t)src->bit_length;
|
|
}
|
|
bd = blob_new_from_blob(src, (size_t)from, (size_t)to);
|
|
}
|
|
// else fail
|
|
else {
|
|
return JS_ThrowTypeError(ctx, "Blob constructor: invalid arguments");
|
|
}
|
|
|
|
if (!bd) {
|
|
return JS_ThrowOutOfMemory(ctx);
|
|
}
|
|
|
|
return blob2js(ctx, bd);
|
|
}
|
|
|
|
// blob.write_bit(logical)
|
|
static JSValue js_blob_write_bit(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 1) {
|
|
return JS_ThrowTypeError(ctx, "write_bit(logical) requires 1 argument");
|
|
}
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "write_bit: not called on a blob");
|
|
}
|
|
|
|
// Handle numeric 0/1 or boolean
|
|
int bit_val;
|
|
if (JS_IsNumber(argv[0])) {
|
|
int32_t num;
|
|
JS_ToInt32(ctx, &num, argv[0]);
|
|
if (num != 0 && num != 1) {
|
|
return JS_ThrowTypeError(ctx, "write_bit: value must be true, false, 0, or 1");
|
|
}
|
|
bit_val = num;
|
|
} else {
|
|
bit_val = JS_ToBool(ctx, argv[0]);
|
|
}
|
|
|
|
if (blob_write_bit(bd, bit_val) < 0) {
|
|
return JS_ThrowTypeError(ctx, "write_bit: cannot write (maybe stone or OOM)");
|
|
}
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
// blob.write_blob(second_blob)
|
|
static JSValue js_blob_write_blob(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 1) {
|
|
return JS_ThrowTypeError(ctx, "write_blob(second_blob) requires 1 argument");
|
|
}
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "write_blob: not called on a blob");
|
|
}
|
|
blob *second = js2blob(ctx, argv[0]);
|
|
if (!second) {
|
|
return JS_ThrowTypeError(ctx, "write_blob: argument must be a blob");
|
|
}
|
|
|
|
if (blob_write_blob(bd, second) < 0) {
|
|
return JS_ThrowTypeError(ctx, "write_blob: cannot write to stone blob or OOM");
|
|
}
|
|
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
// blob.write_dec64(number)
|
|
static JSValue js_blob_write_dec64(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 1) {
|
|
return JS_ThrowTypeError(ctx, "write_dec64(number) requires 1 argument");
|
|
}
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "write_dec64: not called on a blob");
|
|
}
|
|
|
|
// Get the number as a double and convert to DEC64
|
|
double d;
|
|
if (JS_ToFloat64(ctx, &d, argv[0]) < 0) {
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
if (blob_write_dec64(bd, d) < 0) {
|
|
return JS_ThrowTypeError(ctx, "write_dec64: cannot write to stone blob or OOM");
|
|
}
|
|
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
// blob.write_fit(fit, length)
|
|
static JSValue js_blob_write_fit(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 2) {
|
|
return JS_ThrowTypeError(ctx, "write_fit(fit, length) requires 2 arguments");
|
|
}
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "write_fit: not called on a blob");
|
|
}
|
|
|
|
int64_t fit;
|
|
int32_t length;
|
|
if (JS_ToInt64(ctx, &fit, argv[0]) < 0) return JS_EXCEPTION;
|
|
if (JS_ToInt32(ctx, &length, argv[1]) < 0) return JS_EXCEPTION;
|
|
|
|
if (blob_write_fit(bd, fit, length) < 0) {
|
|
return JS_ThrowTypeError(ctx, "write_fit: cannot write (stone blob, OOM, or value out of range)");
|
|
}
|
|
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
// blob.write_kim(fit)
|
|
static JSValue js_blob_write_kim(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 1) {
|
|
return JS_ThrowTypeError(ctx, "write_kim(fit) requires 1 argument");
|
|
}
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "write_kim: not called on a blob");
|
|
}
|
|
|
|
// Handle number or single character string
|
|
int64_t value;
|
|
if (JS_IsString(argv[0])) {
|
|
const char *str = JS_ToCString(ctx, argv[0]);
|
|
if (!str) return JS_EXCEPTION;
|
|
|
|
// Get first UTF-32 character
|
|
// For simplicity, assuming ASCII for now
|
|
if (strlen(str) == 0) {
|
|
JS_FreeCString(ctx, str);
|
|
return JS_ThrowTypeError(ctx, "write_kim: empty string");
|
|
}
|
|
value = (unsigned char)str[0];
|
|
JS_FreeCString(ctx, str);
|
|
} else {
|
|
if (JS_ToInt64(ctx, &value, argv[0]) < 0) return JS_EXCEPTION;
|
|
}
|
|
|
|
if (blob_write_kim(bd, value) < 0) {
|
|
return JS_ThrowTypeError(ctx, "write_kim: cannot write to stone blob or OOM");
|
|
}
|
|
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
// blob.write_pad(block_size)
|
|
static JSValue js_blob_write_pad(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 1) {
|
|
return JS_ThrowTypeError(ctx, "write_pad(block_size) requires 1 argument");
|
|
}
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "write_pad: not called on a blob");
|
|
}
|
|
|
|
int32_t block_size;
|
|
if (JS_ToInt32(ctx, &block_size, argv[0]) < 0) return JS_EXCEPTION;
|
|
|
|
if (blob_write_pad(bd, block_size) < 0) {
|
|
return JS_ThrowTypeError(ctx, "write_pad: cannot write (stone blob, OOM, or invalid block size)");
|
|
}
|
|
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
// blob.write_text(text)
|
|
static JSValue js_blob_write_text(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 1) {
|
|
return JS_ThrowTypeError(ctx, "write_text(text) requires 1 argument");
|
|
}
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "write_text: not called on a blob");
|
|
}
|
|
|
|
const char *str = JS_ToCString(ctx, argv[0]);
|
|
if (!str) return JS_EXCEPTION;
|
|
|
|
int result = blob_write_text(bd, str);
|
|
JS_FreeCString(ctx, str);
|
|
|
|
if (result < 0) {
|
|
return JS_ThrowTypeError(ctx, "write_text: cannot write to stone blob or OOM");
|
|
}
|
|
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
// blob.read_logical(from)
|
|
static JSValue js_blob_read_logical(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 1) {
|
|
return JS_ThrowTypeError(ctx, "read_logical(from) requires 1 argument");
|
|
}
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "read_logical: not called on a blob");
|
|
}
|
|
int64_t pos;
|
|
if (JS_ToInt64(ctx, &pos, argv[0]) < 0) {
|
|
return JS_EXCEPTION;
|
|
}
|
|
if (pos < 0) {
|
|
return JS_NULL; // out of range
|
|
}
|
|
int bit_val;
|
|
if (blob_read_bit(bd, (size_t)pos, &bit_val) < 0) {
|
|
return JS_NULL; // not stone or out of range
|
|
}
|
|
return JS_NewBool(ctx, bit_val);
|
|
}
|
|
|
|
// blob.read_blob(from, to)
|
|
static JSValue js_blob_read_blob(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "read_blob: not called on a blob");
|
|
}
|
|
|
|
if (!bd->is_stone) {
|
|
return JS_ThrowTypeError(ctx, "read_blob: blob must be stone");
|
|
}
|
|
|
|
int64_t from = 0;
|
|
int64_t to = bd->bit_length;
|
|
|
|
if (argc >= 1) {
|
|
if (JS_ToInt64(ctx, &from, argv[0]) < 0) return JS_EXCEPTION;
|
|
if (from < 0) from = 0;
|
|
}
|
|
if (argc >= 2) {
|
|
if (JS_ToInt64(ctx, &to, argv[1]) < 0) return JS_EXCEPTION;
|
|
if (to > (int64_t)bd->bit_length) to = bd->bit_length;
|
|
}
|
|
|
|
blob *new_bd = blob_read_blob(bd, from, to);
|
|
if (!new_bd) {
|
|
return JS_ThrowOutOfMemory(ctx);
|
|
}
|
|
|
|
return blob2js(ctx, new_bd);
|
|
}
|
|
|
|
// blob.read_dec64(from)
|
|
static JSValue js_blob_read_dec64(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 1) {
|
|
return JS_ThrowTypeError(ctx, "read_dec64(from) requires 1 argument");
|
|
}
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "read_dec64: not called on a blob");
|
|
}
|
|
|
|
if (!bd->is_stone) {
|
|
return JS_ThrowTypeError(ctx, "read_dec64: blob must be stone");
|
|
}
|
|
|
|
int64_t from;
|
|
if (JS_ToInt64(ctx, &from, argv[0]) < 0) return JS_EXCEPTION;
|
|
|
|
double d;
|
|
if (blob_read_dec64(bd, from, &d) < 0) {
|
|
return JS_ThrowRangeError(ctx, "read_dec64: out of range");
|
|
}
|
|
|
|
return JS_NewFloat64(ctx, d);
|
|
}
|
|
|
|
// blob.read_fit(from, length)
|
|
static JSValue js_blob_read_fit(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 2) {
|
|
return JS_ThrowTypeError(ctx, "read_fit(from, length) requires 2 arguments");
|
|
}
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "read_fit: not called on a blob");
|
|
}
|
|
|
|
if (!bd->is_stone) {
|
|
return JS_ThrowTypeError(ctx, "read_fit: blob must be stone");
|
|
}
|
|
|
|
int64_t from;
|
|
int32_t length;
|
|
if (JS_ToInt64(ctx, &from, argv[0]) < 0) return JS_EXCEPTION;
|
|
if (JS_ToInt32(ctx, &length, argv[1]) < 0) return JS_EXCEPTION;
|
|
|
|
int64_t value;
|
|
if (blob_read_fit(bd, from, length, &value) < 0) {
|
|
return JS_ThrowRangeError(ctx, "read_fit: out of range or invalid length");
|
|
}
|
|
|
|
return JS_NewInt64(ctx, value);
|
|
}
|
|
|
|
// blob.read_kim(from)
|
|
static JSValue js_blob_read_kim(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 1) {
|
|
return JS_ThrowTypeError(ctx, "read_kim(from) requires 1 argument");
|
|
}
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "read_kim: not called on a blob");
|
|
}
|
|
|
|
if (!bd->is_stone) {
|
|
return JS_ThrowTypeError(ctx, "read_kim: blob must be stone");
|
|
}
|
|
|
|
int64_t from;
|
|
if (JS_ToInt64(ctx, &from, argv[0]) < 0) return JS_EXCEPTION;
|
|
|
|
int64_t value;
|
|
size_t bits_read;
|
|
if (blob_read_kim(bd, from, &value, &bits_read) < 0) {
|
|
return JS_ThrowRangeError(ctx, "read_kim: out of range or invalid kim encoding");
|
|
}
|
|
|
|
// Return an object with the value and the number of bits read
|
|
JSValue obj = JS_NewObject(ctx);
|
|
JS_SetPropertyStr(ctx, obj, "value", JS_NewInt64(ctx, value));
|
|
JS_SetPropertyStr(ctx, obj, "bits_read", JS_NewInt64(ctx, bits_read));
|
|
return obj;
|
|
}
|
|
|
|
// blob.read_text(from)
|
|
static JSValue js_blob_read_text(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 1) {
|
|
return JS_ThrowTypeError(ctx, "read_text(from) requires 1 argument");
|
|
}
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "read_text: not called on a blob");
|
|
}
|
|
|
|
if (!bd->is_stone) {
|
|
return JS_ThrowTypeError(ctx, "read_text: blob must be stone");
|
|
}
|
|
|
|
int64_t from;
|
|
if (JS_ToInt64(ctx, &from, argv[0]) < 0) return JS_EXCEPTION;
|
|
|
|
char *text;
|
|
size_t bits_read;
|
|
if (blob_read_text(bd, from, &text, &bits_read) < 0) {
|
|
return JS_ThrowRangeError(ctx, "read_text: out of range or invalid encoding");
|
|
}
|
|
|
|
JSValue result = JS_NewString(ctx, text);
|
|
free(text);
|
|
|
|
// Return object with text and total bits read
|
|
JSValue obj = JS_NewObject(ctx);
|
|
JS_SetPropertyStr(ctx, obj, "text", result);
|
|
JS_SetPropertyStr(ctx, obj, "bits_read", JS_NewInt64(ctx, bits_read));
|
|
return obj;
|
|
}
|
|
|
|
// blob.pad?(from, block_size)
|
|
static JSValue js_blob_pad_q(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 2) {
|
|
return JS_ThrowTypeError(ctx, "pad?(from, block_size) requires 2 arguments");
|
|
}
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "pad?: not called on a blob");
|
|
}
|
|
|
|
if (!bd->is_stone) {
|
|
return JS_ThrowTypeError(ctx, "pad?: blob must be stone");
|
|
}
|
|
|
|
int64_t from;
|
|
int32_t block_size;
|
|
if (JS_ToInt64(ctx, &from, argv[0]) < 0) return JS_EXCEPTION;
|
|
if (JS_ToInt32(ctx, &block_size, argv[1]) < 0) return JS_EXCEPTION;
|
|
|
|
return JS_NewBool(ctx, blob_pad_check(bd, from, block_size));
|
|
}
|
|
|
|
// blob.stone()
|
|
static JSValue js_blob_stone(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "stone: not called on a blob");
|
|
}
|
|
if (!bd->is_stone) {
|
|
blob_make_stone(bd);
|
|
}
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
// blob.length getter
|
|
// Return number of bits in the blob
|
|
static JSValue js_blob_get_length(JSContext *ctx, JSValueConst this_val, int magic) {
|
|
blob *bd = js2blob(ctx, this_val);
|
|
if (!bd) {
|
|
return JS_ThrowTypeError(ctx, "length: not called on a blob");
|
|
}
|
|
return JS_NewInt64(ctx, bd->bit_length);
|
|
}
|
|
|
|
// Static kim_length function
|
|
static JSValue js_blob_kim_length(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv) {
|
|
if (argc < 1) {
|
|
return JS_ThrowTypeError(ctx, "kim_length(value) requires 1 argument");
|
|
}
|
|
|
|
if (JS_IsBool(argv[0])) {
|
|
return JS_NewInt32(ctx, 1);
|
|
}
|
|
|
|
if (JS_IsNumber(argv[0])) {
|
|
int64_t value;
|
|
if (JS_ToInt64(ctx, &value, argv[0]) < 0) return JS_EXCEPTION;
|
|
return JS_NewInt32(ctx, kim_length_for_fit(value));
|
|
}
|
|
|
|
if (JS_IsString(argv[0])) {
|
|
const char *str = JS_ToCString(ctx, argv[0]);
|
|
if (!str) return JS_EXCEPTION;
|
|
|
|
size_t len = strlen(str);
|
|
// Length encoding + each character encoding
|
|
int total_bits = kim_length_for_fit(len);
|
|
for (size_t i = 0; i < len; i++) {
|
|
total_bits += kim_length_for_fit((unsigned char)str[i]);
|
|
}
|
|
|
|
JS_FreeCString(ctx, str);
|
|
return JS_NewInt32(ctx, total_bits);
|
|
}
|
|
|
|
return JS_NULL;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Exports list
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static const JSCFunctionListEntry js_blob_funcs[] = {
|
|
// Write methods
|
|
JS_CFUNC_DEF("write_bit", 1, js_blob_write_bit),
|
|
JS_CFUNC_DEF("write_blob", 1, js_blob_write_blob),
|
|
JS_CFUNC_DEF("write_dec64", 1, js_blob_write_dec64),
|
|
JS_CFUNC_DEF("write_fit", 2, js_blob_write_fit),
|
|
JS_CFUNC_DEF("write_kim", 1, js_blob_write_kim),
|
|
JS_CFUNC_DEF("write_pad", 1, js_blob_write_pad),
|
|
JS_CFUNC_DEF("write_text", 1, js_blob_write_text),
|
|
|
|
// Read methods
|
|
JS_CFUNC_DEF("read_logical", 1, js_blob_read_logical),
|
|
JS_CFUNC_DEF("read_blob", 2, js_blob_read_blob),
|
|
JS_CFUNC_DEF("read_dec64", 1, js_blob_read_dec64),
|
|
JS_CFUNC_DEF("read_fit", 2, js_blob_read_fit),
|
|
JS_CFUNC_DEF("read_kim", 1, js_blob_read_kim),
|
|
JS_CFUNC_DEF("read_text", 1, js_blob_read_text),
|
|
JS_CFUNC_DEF("pad?", 2, js_blob_pad_q),
|
|
|
|
// Other methods
|
|
JS_CFUNC_DEF("stone", 0, js_blob_stone),
|
|
|
|
// Length property getter
|
|
JS_CGETSET_DEF("length", js_blob_get_length, NULL),
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// js_blob_use(ctx) for easy embedding: returns an object with the blob functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
JSValue js_blob_use(JSContext *js) {
|
|
// Register the blob class
|
|
QJSCLASSPREP_FUNCS(blob);
|
|
|
|
// Create the constructor function
|
|
JSValue ctor = JS_NewCFunction2(js, js_blob_constructor, "blob", 3, JS_CFUNC_constructor, 0);
|
|
|
|
// Set the prototype on the constructor
|
|
JSValue proto = JS_GetClassProto(js, js_blob_id);
|
|
JS_SetConstructor(js, ctor, proto);
|
|
JS_FreeValue(js, proto);
|
|
|
|
// Add static kim_length method to constructor
|
|
JS_SetPropertyStr(js, ctor, "kim_length",
|
|
JS_NewCFunction(js, js_blob_kim_length, "kim_length", 1));
|
|
|
|
return ctor;
|
|
}
|
|
|
|
JSValue js_new_blob_stoned_copy(JSContext *js, void *data, size_t bytes)
|
|
{
|
|
blob *b = blob_new(bytes*8);
|
|
memcpy(b->data, data, bytes);
|
|
b->bit_length = bytes * 8; // Set the actual length in bits
|
|
blob_make_stone(b);
|
|
|
|
return blob2js(js, b);
|
|
}
|
|
|
|
void *js_get_blob_data(JSContext *js, size_t *size, JSValue v)
|
|
{
|
|
blob *b = js2blob(js, v);
|
|
if (!b || !b->is_stone)
|
|
return NULL;
|
|
|
|
*size = (b->bit_length + 7) / 8; // Return actual byte size based on bit length
|
|
return b->data;
|
|
}
|
|
|
|
int js_is_blob(JSContext *js, JSValue v)
|
|
{
|
|
blob *b = js2blob(js,v);
|
|
if (b) return 1;
|
|
return 0;
|
|
}
|