fix blob; throw on non stone reads; update blob test
This commit is contained in:
@@ -333,6 +333,8 @@ var $_ = create_actor()
|
|||||||
$_.random = crypto.random
|
$_.random = crypto.random
|
||||||
$_.random[cell.DOC] = "returns a number between 0 and 1. There is a 50% chance that the result is less than 0.5."
|
$_.random[cell.DOC] = "returns a number between 0 and 1. There is a 50% chance that the result is less than 0.5."
|
||||||
|
|
||||||
|
$_.random_fit = crypto.random_fit
|
||||||
|
|
||||||
$_.clock = function(fn) { return os.now() }
|
$_.clock = function(fn) { return os.now() }
|
||||||
$_.clock[cell.DOC] = "takes a function input value that will eventually be called with the current time in number form."
|
$_.clock[cell.DOC] = "takes a function input value that will eventually be called with the current time in number form."
|
||||||
|
|
||||||
|
|||||||
@@ -197,10 +197,12 @@ void copy_bits_fast(const void *src, void *dest,
|
|||||||
// Copy bits [from..to) from src buffer into the blob at its current length
|
// Copy bits [from..to) from src buffer into the blob at its current length
|
||||||
// (i.e. 'to' is exclusive). Advance blob->length by (to - from). Return 0.
|
// (i.e. 'to' is exclusive). Advance blob->length by (to - from). Return 0.
|
||||||
static int blob_copy_bits(blob *dest, const void *src, size_t from, size_t to) {
|
static int blob_copy_bits(blob *dest, const void *src, size_t from, size_t to) {
|
||||||
|
size_t bits = to - from;
|
||||||
|
if (blob_ensure_capacity(dest, bits) < 0) return -1;
|
||||||
int n = (int)from;
|
int n = (int)from;
|
||||||
int m = (int)(to - 1); // inclusive
|
int m = (int)(to - 1);
|
||||||
copy_bits_fast(src, dest->data, n, m, (int)dest->length);
|
copy_bits_fast(src, dest->data, n, m, (int)dest->length);
|
||||||
dest->length += (to - from);
|
dest->length += bits;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +239,6 @@ blob *blob_new_with_fill(size_t length_bits, int logical_value) {
|
|||||||
if (logical_value) {
|
if (logical_value) {
|
||||||
size_t bytes = b->capacity / 8;
|
size_t bytes = b->capacity / 8;
|
||||||
memset(b->data, 0xFF, bytes);
|
memset(b->data, 0xFF, bytes);
|
||||||
// Clear unused bits in last byte
|
|
||||||
size_t used_bits = length_bits & 7;
|
size_t used_bits = length_bits & 7;
|
||||||
if (used_bits && bytes > 0) {
|
if (used_bits && bytes > 0) {
|
||||||
uint8_t mask = (1 << used_bits) - 1;
|
uint8_t mask = (1 << used_bits) - 1;
|
||||||
@@ -247,6 +248,7 @@ blob *blob_new_with_fill(size_t length_bits, int logical_value) {
|
|||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void blob_destroy(blob *b) {
|
void blob_destroy(blob *b) {
|
||||||
if (!b) return;
|
if (!b) return;
|
||||||
if (b->data) {
|
if (b->data) {
|
||||||
@@ -261,7 +263,7 @@ void blob_destroy(blob *b) {
|
|||||||
void blob_make_stone(blob *b) {
|
void blob_make_stone(blob *b) {
|
||||||
if (!b) return;
|
if (!b) return;
|
||||||
b->is_stone = 1;
|
b->is_stone = 1;
|
||||||
// Optionally shrink buffer to exactly length bits
|
// shrink buffer to exactly length bits
|
||||||
if (b->capacity > b->length) {
|
if (b->capacity > b->length) {
|
||||||
size_t size_bytes = (b->length + 7) >> 3;
|
size_t size_bytes = (b->length + 7) >> 3;
|
||||||
if (size_bytes) {
|
if (size_bytes) {
|
||||||
|
|||||||
@@ -50,28 +50,32 @@ static JSValue js_blob_constructor(JSContext *ctx, JSValueConst new_target,
|
|||||||
int is_one = JS_ToBool(ctx, argv[1]);
|
int is_one = JS_ToBool(ctx, argv[1]);
|
||||||
bd = blob_new_with_fill((size_t)length_bits, is_one);
|
bd = blob_new_with_fill((size_t)length_bits, is_one);
|
||||||
} else if (JS_IsFunction(ctx, argv[1])) {
|
} else if (JS_IsFunction(ctx, argv[1])) {
|
||||||
// Random function provided - call it for each bit
|
/* Random function provided – call it for each bit */
|
||||||
|
size_t bytes = (length_bits + 7) / 8;
|
||||||
bd = blob_new((size_t)length_bits);
|
bd = blob_new((size_t)length_bits);
|
||||||
if (bd) {
|
if (bd) {
|
||||||
bd->length = length_bits;
|
bd->length = length_bits;
|
||||||
|
/* Ensure the backing storage starts out zeroed */
|
||||||
|
memset(bd->data, 0, bytes);
|
||||||
for (size_t i = 0; i < length_bits; i++) {
|
for (size_t i = 0; i < length_bits; i++) {
|
||||||
JSValue randval = JS_Call(ctx, argv[1], JS_UNDEFINED, 0, NULL);
|
JSValue randval = JS_Call(ctx, argv[1], JS_UNDEFINED, 0, NULL);
|
||||||
if (JS_IsException(randval)) {
|
if (JS_IsException(randval)) {
|
||||||
blob_destroy(bd);
|
blob_destroy(bd);
|
||||||
return JS_EXCEPTION;
|
return JS_EXCEPTION;
|
||||||
}
|
}
|
||||||
int64_t fitval;
|
|
||||||
JS_ToInt64(ctx, &fitval, randval);
|
int32_t fitval;
|
||||||
|
JS_ToInt32(ctx, &fitval, randval);
|
||||||
JS_FreeValue(ctx, randval);
|
JS_FreeValue(ctx, randval);
|
||||||
|
|
||||||
// Set bit based on random value
|
/* Compute which byte and which bit within that byte to set/clear */
|
||||||
size_t byte_idx = i / 8;
|
size_t byte_idx = i / 8;
|
||||||
size_t bit_idx = i % 8;
|
size_t bit_idx = i % 8;
|
||||||
if (fitval & 1) {
|
|
||||||
bd->data[byte_idx] |= (1 << bit_idx);
|
if (fitval & 1)
|
||||||
} else {
|
bd->data[byte_idx] |= (uint8_t)(1 << bit_idx);
|
||||||
bd->data[byte_idx] &= ~(1 << bit_idx);
|
else
|
||||||
}
|
bd->data[byte_idx] &= (uint8_t)~(1 << bit_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -232,14 +236,14 @@ static JSValue js_blob_read_logical(JSContext *ctx, JSValueConst this_val,
|
|||||||
}
|
}
|
||||||
int64_t pos;
|
int64_t pos;
|
||||||
if (JS_ToInt64(ctx, &pos, argv[0]) < 0) {
|
if (JS_ToInt64(ctx, &pos, argv[0]) < 0) {
|
||||||
return JS_EXCEPTION;
|
return JS_ThrowInternalError(ctx, "must provide a positive bit");
|
||||||
}
|
}
|
||||||
if (pos < 0) {
|
if (pos < 0) {
|
||||||
return JS_NULL; // out of range
|
return JS_ThrowRangeError(ctx, "read_logical: position must be non-negative");
|
||||||
}
|
}
|
||||||
int bit_val;
|
int bit_val;
|
||||||
if (blob_read_bit(bd, (size_t)pos, &bit_val) < 0) {
|
if (blob_read_bit(bd, (size_t)pos, &bit_val) < 0) {
|
||||||
return JS_NULL; // not stone or out of range
|
return JS_ThrowTypeError(ctx, "read_logical: blob must be stone");
|
||||||
}
|
}
|
||||||
return JS_NewBool(ctx, bit_val);
|
return JS_NewBool(ctx, bit_val);
|
||||||
}
|
}
|
||||||
@@ -284,22 +288,21 @@ static JSValue js_blob_read_number(JSContext *ctx, JSValueConst this_val,
|
|||||||
}
|
}
|
||||||
blob *bd = js2blob(ctx, this_val);
|
blob *bd = js2blob(ctx, this_val);
|
||||||
if (!bd) {
|
if (!bd) {
|
||||||
return JS_ThrowTypeError(ctx, "read_dec64: not called on a blob");
|
return JS_ThrowTypeError(ctx, "read_number: not called on a blob");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bd->is_stone) {
|
if (!bd->is_stone) {
|
||||||
return JS_ThrowTypeError(ctx, "read_dec64: blob must be stone");
|
return JS_ThrowTypeError(ctx, "read_number: blob must be stone");
|
||||||
}
|
}
|
||||||
|
|
||||||
double from;
|
double from;
|
||||||
if (JS_ToFloat64(ctx, &from, argv[0]) < 0) return JS_EXCEPTION;
|
if (JS_ToFloat64(ctx, &from, argv[0]) < 0) return JS_EXCEPTION;
|
||||||
|
|
||||||
if (from < 0) return JS_ThrowRangeError(ctx, "read_dec64: out of range");
|
if (from < 0) return JS_ThrowRangeError(ctx, "read_number: position must be non-negative");
|
||||||
|
|
||||||
double d;
|
double d;
|
||||||
printf("reading blob from %g\n", from);
|
|
||||||
if (blob_read_dec64(bd, from, &d) < 0) {
|
if (blob_read_dec64(bd, from, &d) < 0) {
|
||||||
return JS_ThrowRangeError(ctx, "read_dec64: out of range");
|
return JS_ThrowRangeError(ctx, "read_number: out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
return JS_NewFloat64(ctx, d);
|
return JS_NewFloat64(ctx, d);
|
||||||
|
|||||||
@@ -199,6 +199,16 @@ JSValue js_crypto_random(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|||||||
return JS_NewFloat64(js, val);
|
return JS_NewFloat64(js, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSValue js_crypto_random_fit(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
int32_t r;
|
||||||
|
if (randombytes(&r, sizeof(r)) != 0) {
|
||||||
|
return JS_ThrowInternalError(js, "crypto.random: unable to get random bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
return JS_NewInt32(js, r);
|
||||||
|
}
|
||||||
|
|
||||||
JSValue js_crypto_hash(JSContext *js, JSValue self, int argc, JSValue *argv)
|
JSValue js_crypto_hash(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
{
|
{
|
||||||
if (argc < 1)
|
if (argc < 1)
|
||||||
@@ -237,6 +247,7 @@ static const JSCFunctionListEntry js_crypto_funcs[] = {
|
|||||||
JS_CFUNC_DEF("keypair", 0, js_crypto_keypair),
|
JS_CFUNC_DEF("keypair", 0, js_crypto_keypair),
|
||||||
JS_CFUNC_DEF("shared", 1, js_crypto_shared),
|
JS_CFUNC_DEF("shared", 1, js_crypto_shared),
|
||||||
JS_CFUNC_DEF("random", 0, js_crypto_random),
|
JS_CFUNC_DEF("random", 0, js_crypto_random),
|
||||||
|
JS_CFUNC_DEF("random_fit", 0, js_crypto_random_fit),
|
||||||
JS_CFUNC_DEF("hash", 2, js_crypto_hash),
|
JS_CFUNC_DEF("hash", 2, js_crypto_hash),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -58,13 +58,27 @@ test("Write and read single bit", function() {
|
|||||||
assertEqual(b.read_logical(3), false, "Fourth bit should be false (0)");
|
assertEqual(b.read_logical(3), false, "Fourth bit should be false (0)");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test 4: Out of range read returns null
|
// Test 4: Out of range read throws error
|
||||||
test("Out of range read returns null", function() {
|
test("Out of range read throws error", function() {
|
||||||
var b = new Blob();
|
var b = new Blob();
|
||||||
b.write_bit(true);
|
b.write_bit(true);
|
||||||
b.stone();
|
b.stone();
|
||||||
assertEqual(b.read_logical(100), null, "Out of range read should return null");
|
|
||||||
assertEqual(b.read_logical(-1), null, "Negative index read should return null");
|
var threw = false;
|
||||||
|
try {
|
||||||
|
b.read_logical(100); // Out of range
|
||||||
|
} catch (e) {
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
assert(threw, "Out of range read should throw");
|
||||||
|
|
||||||
|
threw = false;
|
||||||
|
try {
|
||||||
|
b.read_logical(-1); // Negative index
|
||||||
|
} catch (e) {
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
assert(threw, "Negative index read should throw");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test 5: Write and read numbers
|
// Test 5: Write and read numbers
|
||||||
@@ -314,6 +328,14 @@ test("Non-stone blob read methods should throw", function() {
|
|||||||
|
|
||||||
// Try to read without stoning - should throw
|
// Try to read without stoning - should throw
|
||||||
var threw = false;
|
var threw = false;
|
||||||
|
try {
|
||||||
|
b.read_logical(0);
|
||||||
|
} catch (e) {
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
assert(threw, "read_logical on non-stone blob should throw");
|
||||||
|
|
||||||
|
threw = false;
|
||||||
try {
|
try {
|
||||||
b.read_number(0);
|
b.read_number(0);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user