Merge branch 'bench_endoders'
This commit is contained in:
405
benches/encoders.cm
Normal file
405
benches/encoders.cm
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
// encoders.cm — nota/wota/json encode+decode benchmark
|
||||||
|
// Isolates per-type bottlenecks across all three serializers.
|
||||||
|
|
||||||
|
var nota = use('nota')
|
||||||
|
var wota = use('wota')
|
||||||
|
var json = use('json')
|
||||||
|
|
||||||
|
// --- Test data shapes ---
|
||||||
|
|
||||||
|
// Small integers: fast path for all encoders
|
||||||
|
var integers_small = array(100, function(i) { return i + 1 })
|
||||||
|
|
||||||
|
// Floats: stresses nota's snprintf path
|
||||||
|
var floats_array = array(100, function(i) {
|
||||||
|
return 3.14159 * (i + 1) + 0.00001 * i
|
||||||
|
})
|
||||||
|
|
||||||
|
// Short strings in records: per-string overhead, property enumeration
|
||||||
|
var strings_short = array(50, function(i) {
|
||||||
|
var r = {}
|
||||||
|
r[`k${i}`] = `value_${i}`
|
||||||
|
return r
|
||||||
|
})
|
||||||
|
|
||||||
|
// Single long string: throughput test (wota byte loop, nota kim)
|
||||||
|
var long_str = ""
|
||||||
|
var li = 0
|
||||||
|
for (li = 0; li < 100; li++) {
|
||||||
|
long_str = `${long_str}abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMN`
|
||||||
|
}
|
||||||
|
var strings_long = long_str
|
||||||
|
|
||||||
|
// Unicode text: nota's kim encoding, wota's byte packing
|
||||||
|
var strings_unicode = "こんにちは世界 🌍🌎🌏 Ñoño café résumé naïve Ω∑∏ 你好世界"
|
||||||
|
|
||||||
|
// Nested records: cycle detection, property enumeration
|
||||||
|
function make_nested(depth, breadth) {
|
||||||
|
var obj = {}
|
||||||
|
var i = 0
|
||||||
|
var k = null
|
||||||
|
if (depth <= 0) {
|
||||||
|
for (i = 0; i < breadth; i++) {
|
||||||
|
k = `v${i}`
|
||||||
|
obj[k] = i * 2.5
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
for (i = 0; i < breadth; i++) {
|
||||||
|
k = `n${i}`
|
||||||
|
obj[k] = make_nested(depth - 1, breadth)
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
var nested_records = make_nested(3, 4)
|
||||||
|
|
||||||
|
// Flat record: property enumeration cost
|
||||||
|
var flat_record = {}
|
||||||
|
var fi = 0
|
||||||
|
for (fi = 0; fi < 50; fi++) {
|
||||||
|
flat_record[`prop_${fi}`] = fi * 1.1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mixed payload: realistic workload
|
||||||
|
var mixed_payload = array(50, function(i) {
|
||||||
|
var r = {}
|
||||||
|
r.id = i
|
||||||
|
r.name = `item_${i}`
|
||||||
|
r.active = i % 2 == 0
|
||||||
|
r.score = i * 3.14
|
||||||
|
r.tags = [`t${i % 5}`, `t${(i + 1) % 5}`]
|
||||||
|
return r
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- Pre-encode for decode benchmarks ---
|
||||||
|
|
||||||
|
var nota_enc_integers = nota.encode(integers_small)
|
||||||
|
var nota_enc_floats = nota.encode(floats_array)
|
||||||
|
var nota_enc_strings_short = nota.encode(strings_short)
|
||||||
|
var nota_enc_strings_long = nota.encode(strings_long)
|
||||||
|
var nota_enc_strings_unicode = nota.encode(strings_unicode)
|
||||||
|
var nota_enc_nested = nota.encode(nested_records)
|
||||||
|
var nota_enc_flat = nota.encode(flat_record)
|
||||||
|
var nota_enc_mixed = nota.encode(mixed_payload)
|
||||||
|
|
||||||
|
var wota_enc_integers = wota.encode(integers_small)
|
||||||
|
var wota_enc_floats = wota.encode(floats_array)
|
||||||
|
var wota_enc_strings_short = wota.encode(strings_short)
|
||||||
|
var wota_enc_strings_long = wota.encode(strings_long)
|
||||||
|
var wota_enc_strings_unicode = wota.encode(strings_unicode)
|
||||||
|
var wota_enc_nested = wota.encode(nested_records)
|
||||||
|
var wota_enc_flat = wota.encode(flat_record)
|
||||||
|
var wota_enc_mixed = wota.encode(mixed_payload)
|
||||||
|
|
||||||
|
var json_enc_integers = json.encode(integers_small)
|
||||||
|
var json_enc_floats = json.encode(floats_array)
|
||||||
|
var json_enc_strings_short = json.encode(strings_short)
|
||||||
|
var json_enc_strings_long = json.encode(strings_long)
|
||||||
|
var json_enc_strings_unicode = json.encode(strings_unicode)
|
||||||
|
var json_enc_nested = json.encode(nested_records)
|
||||||
|
var json_enc_flat = json.encode(flat_record)
|
||||||
|
var json_enc_mixed = json.encode(mixed_payload)
|
||||||
|
|
||||||
|
// --- Benchmark functions ---
|
||||||
|
|
||||||
|
return {
|
||||||
|
// NOTA encode
|
||||||
|
nota_encode_integers: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.encode(integers_small) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_encode_floats: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.encode(floats_array) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_encode_strings_short: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.encode(strings_short) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_encode_strings_long: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.encode(strings_long) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_encode_strings_unicode: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.encode(strings_unicode) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_encode_nested: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.encode(nested_records) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_encode_flat: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.encode(flat_record) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_encode_mixed: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.encode(mixed_payload) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
|
||||||
|
// NOTA decode
|
||||||
|
nota_decode_integers: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.decode(nota_enc_integers) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_decode_floats: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.decode(nota_enc_floats) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_decode_strings_short: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.decode(nota_enc_strings_short) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_decode_strings_long: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.decode(nota_enc_strings_long) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_decode_strings_unicode: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.decode(nota_enc_strings_unicode) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_decode_nested: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.decode(nota_enc_nested) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_decode_flat: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.decode(nota_enc_flat) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
nota_decode_mixed: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = nota.decode(nota_enc_mixed) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
|
||||||
|
// WOTA encode
|
||||||
|
wota_encode_integers: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.encode(integers_small) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_encode_floats: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.encode(floats_array) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_encode_strings_short: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.encode(strings_short) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_encode_strings_long: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.encode(strings_long) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_encode_strings_unicode: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.encode(strings_unicode) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_encode_nested: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.encode(nested_records) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_encode_flat: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.encode(flat_record) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_encode_mixed: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.encode(mixed_payload) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
|
||||||
|
// WOTA decode
|
||||||
|
wota_decode_integers: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.decode(wota_enc_integers) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_decode_floats: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.decode(wota_enc_floats) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_decode_strings_short: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.decode(wota_enc_strings_short) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_decode_strings_long: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.decode(wota_enc_strings_long) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_decode_strings_unicode: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.decode(wota_enc_strings_unicode) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_decode_nested: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.decode(wota_enc_nested) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_decode_flat: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.decode(wota_enc_flat) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
wota_decode_mixed: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = wota.decode(wota_enc_mixed) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
|
||||||
|
// JSON encode
|
||||||
|
json_encode_integers: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.encode(integers_small) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_encode_floats: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.encode(floats_array) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_encode_strings_short: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.encode(strings_short) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_encode_strings_long: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.encode(strings_long) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_encode_strings_unicode: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.encode(strings_unicode) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_encode_nested: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.encode(nested_records) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_encode_flat: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.encode(flat_record) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_encode_mixed: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.encode(mixed_payload) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
|
||||||
|
// JSON decode
|
||||||
|
json_decode_integers: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.decode(json_enc_integers) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_decode_floats: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.decode(json_enc_floats) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_decode_strings_short: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.decode(json_enc_strings_short) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_decode_strings_long: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.decode(json_enc_strings_long) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_decode_strings_unicode: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.decode(json_enc_strings_unicode) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_decode_nested: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.decode(json_enc_nested) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_decode_flat: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.decode(json_enc_flat) }
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
json_decode_mixed: function(n) {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < n; i++) { r = json.decode(json_enc_mixed) }
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -308,6 +308,30 @@ void nota_write_blob(NotaBuffer *nb, unsigned long long nbits, const char *data)
|
|||||||
|
|
||||||
void nota_write_text(NotaBuffer *nb, const char *s)
|
void nota_write_text(NotaBuffer *nb, const char *s)
|
||||||
{
|
{
|
||||||
|
/* ASCII fast path: if all bytes < 0x80, KIM == UTF-8 and rune count == byte count */
|
||||||
|
size_t slen = strlen(s);
|
||||||
|
const unsigned char *scan = (const unsigned char *)s;
|
||||||
|
int is_ascii = 1;
|
||||||
|
for (size_t k = 0; k < slen; k++) {
|
||||||
|
if (scan[k] >= 0x80) {
|
||||||
|
is_ascii = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_ascii) {
|
||||||
|
long long runes = (long long)slen;
|
||||||
|
char *p = nota_buffer_alloc(nb, 1 + 10 + slen);
|
||||||
|
p[0] = NOTA_TEXT;
|
||||||
|
char *end = nota_continue_num(runes, p, 4);
|
||||||
|
memcpy(end, s, slen);
|
||||||
|
size_t used = (size_t)(end - p) + slen;
|
||||||
|
size_t allocated = 1 + 10 + slen;
|
||||||
|
nb->size -= (allocated - used);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Non-ASCII path: full UTF-8 decode + KIM encode */
|
||||||
long long runes = utf8_count(s);
|
long long runes = utf8_count(s);
|
||||||
|
|
||||||
size_t max_kim = (size_t)(runes * 5);
|
size_t max_kim = (size_t)(runes * 5);
|
||||||
@@ -367,6 +391,13 @@ static void nota_write_int_buf(NotaBuffer *nb, long long n)
|
|||||||
nb->size -= (10 - used);
|
nb->size -= (10 - used);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const double nota_pow10_table[29] = {
|
||||||
|
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
|
||||||
|
1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
|
||||||
|
1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23,
|
||||||
|
1e24, 1e25, 1e26, 1e27, 1e28
|
||||||
|
};
|
||||||
|
|
||||||
static void extract_mantissa_coefficient(double num, long *coefficient, long *exponent)
|
static void extract_mantissa_coefficient(double num, long *coefficient, long *exponent)
|
||||||
{
|
{
|
||||||
if (num == 0.0) {
|
if (num == 0.0) {
|
||||||
@@ -375,32 +406,46 @@ static void extract_mantissa_coefficient(double num, long *coefficient, long *ex
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Round to 12 decimal places to avoid floating artifacts. */
|
double absval = fabs(num);
|
||||||
double rounded = floor(fabs(num) * 1e12 + 0.5) / 1e12;
|
int sign = (num < 0) ? -1 : 1;
|
||||||
if (num < 0) {
|
|
||||||
rounded = -rounded;
|
/* Get decimal exponent via log10 */
|
||||||
|
int dec_exp = (int)floor(log10(absval));
|
||||||
|
|
||||||
|
/* Scale to extract 14-digit coefficient.
|
||||||
|
We want coeff * 10^exp = absval, with coeff having up to 14 digits.
|
||||||
|
So coeff = absval * 10^(13 - dec_exp), exp = dec_exp - 13 */
|
||||||
|
int shift = 13 - dec_exp;
|
||||||
|
double scaled;
|
||||||
|
if (shift >= 0 && shift <= 28) {
|
||||||
|
scaled = absval * nota_pow10_table[shift];
|
||||||
|
} else if (shift < 0 && -shift <= 28) {
|
||||||
|
scaled = absval / nota_pow10_table[-shift];
|
||||||
|
} else {
|
||||||
|
scaled = absval * pow(10.0, (double)shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
char buf[64];
|
long long coeff = (long long)(scaled + 0.5);
|
||||||
snprintf(buf, sizeof(buf), "%.14g", rounded);
|
|
||||||
|
|
||||||
char *exp_pos = strpbrk(buf, "eE");
|
/* Correct off-by-one from log10 rounding */
|
||||||
long exp_from_sci = 0;
|
if (coeff >= 100000000000000LL) {
|
||||||
if (exp_pos) {
|
coeff = (coeff + 5) / 10;
|
||||||
exp_from_sci = atol(exp_pos + 1);
|
shift--;
|
||||||
*exp_pos = '\0';
|
} else if (coeff < 10000000000000LL && coeff > 0) {
|
||||||
|
coeff = (long long)(absval * pow(10.0, (double)(shift + 1)) + 0.5);
|
||||||
|
shift++;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *dec_point = strchr(buf, '.');
|
int exp_out = -shift;
|
||||||
int digits_after_decimal = 0;
|
|
||||||
if (dec_point) {
|
/* Strip trailing zeros */
|
||||||
digits_after_decimal = (int)strlen(dec_point + 1);
|
while (coeff != 0 && coeff % 10 == 0) {
|
||||||
memmove(dec_point, dec_point + 1, strlen(dec_point));
|
coeff /= 10;
|
||||||
|
exp_out++;
|
||||||
}
|
}
|
||||||
|
|
||||||
long long coeff_ll = atoll(buf);
|
*coefficient = (long)(coeff * sign);
|
||||||
*coefficient = (long)coeff_ll;
|
*exponent = (long)exp_out;
|
||||||
*exponent = exp_from_sci - digits_after_decimal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nota_write_float_buf(NotaBuffer *nb, double d)
|
static void nota_write_float_buf(NotaBuffer *nb, double d)
|
||||||
|
|||||||
@@ -11308,38 +11308,41 @@ static int nota_get_arr_len (JSContext *ctx, JSValue arr) {
|
|||||||
return (int)len;
|
return (int)len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct NotaVisitedNode {
|
||||||
|
JSGCRef ref;
|
||||||
|
struct NotaVisitedNode *next;
|
||||||
|
} NotaVisitedNode;
|
||||||
|
|
||||||
typedef struct NotaEncodeContext {
|
typedef struct NotaEncodeContext {
|
||||||
JSContext *ctx;
|
JSContext *ctx;
|
||||||
JSGCRef *visitedStack_ref; /* pointer to GC-rooted ref */
|
NotaVisitedNode *visited_list;
|
||||||
NotaBuffer nb;
|
NotaBuffer nb;
|
||||||
int cycle;
|
int cycle;
|
||||||
JSGCRef *replacer_ref; /* pointer to GC-rooted ref */
|
JSGCRef *replacer_ref; /* pointer to GC-rooted ref */
|
||||||
} NotaEncodeContext;
|
} NotaEncodeContext;
|
||||||
|
|
||||||
static void nota_stack_push (NotaEncodeContext *enc, JSValueConst val) {
|
static void nota_stack_push (NotaEncodeContext *enc, JSValueConst val) {
|
||||||
JSContext *ctx = enc->ctx;
|
NotaVisitedNode *node = (NotaVisitedNode *)sys_malloc (sizeof (NotaVisitedNode));
|
||||||
int len = nota_get_arr_len (ctx, enc->visitedStack_ref->val);
|
JS_PushGCRef (enc->ctx, &node->ref);
|
||||||
JS_SetPropertyNumber (ctx, enc->visitedStack_ref->val, len, JS_DupValue (ctx, val));
|
node->ref.val = JS_DupValue (enc->ctx, val);
|
||||||
|
node->next = enc->visited_list;
|
||||||
|
enc->visited_list = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nota_stack_pop (NotaEncodeContext *enc) {
|
static void nota_stack_pop (NotaEncodeContext *enc) {
|
||||||
JSContext *ctx = enc->ctx;
|
NotaVisitedNode *node = enc->visited_list;
|
||||||
int len = nota_get_arr_len (ctx, enc->visitedStack_ref->val);
|
enc->visited_list = node->next;
|
||||||
JS_SetPropertyStr (ctx, enc->visitedStack_ref->val, "length", JS_NewUint32 (ctx, len - 1));
|
JS_FreeValue (enc->ctx, node->ref.val);
|
||||||
|
JS_PopGCRef (enc->ctx, &node->ref);
|
||||||
|
sys_free (node);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nota_stack_has (NotaEncodeContext *enc, JSValueConst val) {
|
static int nota_stack_has (NotaEncodeContext *enc, JSValueConst val) {
|
||||||
JSContext *ctx = enc->ctx;
|
NotaVisitedNode *node = enc->visited_list;
|
||||||
int len = nota_get_arr_len (ctx, enc->visitedStack_ref->val);
|
while (node) {
|
||||||
for (int i = 0; i < len; i++) {
|
if (JS_StrictEq (enc->ctx, node->ref.val, val))
|
||||||
JSValue elem = JS_GetPropertyNumber (ctx, enc->visitedStack_ref->val, i);
|
return 1;
|
||||||
if (mist_is_gc_object (elem) && mist_is_gc_object (val)) {
|
node = node->next;
|
||||||
if (JS_StrictEq (ctx, elem, val)) {
|
|
||||||
JS_FreeValue (ctx, elem);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JS_FreeValue (ctx, elem);
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -11481,6 +11484,13 @@ static void nota_encode_value (NotaEncodeContext *enc, JSValueConst val, JSValue
|
|||||||
nota_write_sym (&enc->nb, NOTA_NULL);
|
nota_write_sym (&enc->nb, NOTA_NULL);
|
||||||
break;
|
break;
|
||||||
case JS_TAG_PTR: {
|
case JS_TAG_PTR: {
|
||||||
|
if (JS_IsText (replaced_ref.val)) {
|
||||||
|
const char *str = JS_ToCString (ctx, replaced_ref.val);
|
||||||
|
nota_write_text (&enc->nb, str);
|
||||||
|
JS_FreeCString (ctx, str);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (js_is_blob (ctx, replaced_ref.val)) {
|
if (js_is_blob (ctx, replaced_ref.val)) {
|
||||||
size_t buf_len;
|
size_t buf_len;
|
||||||
void *buf_data = js_get_blob_data (ctx, &buf_len, replaced_ref.val);
|
void *buf_data = js_get_blob_data (ctx, &buf_len, replaced_ref.val);
|
||||||
@@ -11592,16 +11602,14 @@ static void nota_encode_value (NotaEncodeContext *enc, JSValueConst val, JSValue
|
|||||||
}
|
}
|
||||||
|
|
||||||
void *value2nota (JSContext *ctx, JSValue v) {
|
void *value2nota (JSContext *ctx, JSValue v) {
|
||||||
JSGCRef val_ref, stack_ref, key_ref;
|
JSGCRef val_ref, key_ref;
|
||||||
JS_PushGCRef (ctx, &val_ref);
|
JS_PushGCRef (ctx, &val_ref);
|
||||||
JS_PushGCRef (ctx, &stack_ref);
|
|
||||||
JS_PushGCRef (ctx, &key_ref);
|
JS_PushGCRef (ctx, &key_ref);
|
||||||
val_ref.val = v;
|
val_ref.val = v;
|
||||||
|
|
||||||
NotaEncodeContext enc_s, *enc = &enc_s;
|
NotaEncodeContext enc_s, *enc = &enc_s;
|
||||||
enc->ctx = ctx;
|
enc->ctx = ctx;
|
||||||
stack_ref.val = JS_NewArray (ctx);
|
enc->visited_list = NULL;
|
||||||
enc->visitedStack_ref = &stack_ref;
|
|
||||||
enc->cycle = 0;
|
enc->cycle = 0;
|
||||||
enc->replacer_ref = NULL;
|
enc->replacer_ref = NULL;
|
||||||
|
|
||||||
@@ -11611,14 +11619,12 @@ void *value2nota (JSContext *ctx, JSValue v) {
|
|||||||
|
|
||||||
if (enc->cycle) {
|
if (enc->cycle) {
|
||||||
JS_PopGCRef (ctx, &key_ref);
|
JS_PopGCRef (ctx, &key_ref);
|
||||||
JS_PopGCRef (ctx, &stack_ref);
|
|
||||||
JS_PopGCRef (ctx, &val_ref);
|
JS_PopGCRef (ctx, &val_ref);
|
||||||
nota_buffer_free (&enc->nb);
|
nota_buffer_free (&enc->nb);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PopGCRef (ctx, &key_ref);
|
JS_PopGCRef (ctx, &key_ref);
|
||||||
JS_PopGCRef (ctx, &stack_ref);
|
|
||||||
JS_PopGCRef (ctx, &val_ref);
|
JS_PopGCRef (ctx, &val_ref);
|
||||||
void *data_ptr = enc->nb.data;
|
void *data_ptr = enc->nb.data;
|
||||||
enc->nb.data = NULL;
|
enc->nb.data = NULL;
|
||||||
@@ -11646,18 +11652,16 @@ JSValue nota2value (JSContext *js, void *nota) {
|
|||||||
static JSValue js_nota_encode (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
static JSValue js_nota_encode (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||||
if (argc < 1) return JS_ThrowTypeError (ctx, "nota.encode requires at least 1 argument");
|
if (argc < 1) return JS_ThrowTypeError (ctx, "nota.encode requires at least 1 argument");
|
||||||
|
|
||||||
JSGCRef val_ref, stack_ref, replacer_ref, key_ref;
|
JSGCRef val_ref, replacer_ref, key_ref;
|
||||||
JS_PushGCRef (ctx, &val_ref);
|
JS_PushGCRef (ctx, &val_ref);
|
||||||
JS_PushGCRef (ctx, &stack_ref);
|
|
||||||
JS_PushGCRef (ctx, &replacer_ref);
|
JS_PushGCRef (ctx, &replacer_ref);
|
||||||
JS_PushGCRef (ctx, &key_ref);
|
JS_PushGCRef (ctx, &key_ref);
|
||||||
val_ref.val = argv[0];
|
val_ref.val = argv[0];
|
||||||
stack_ref.val = JS_NewArray (ctx);
|
|
||||||
replacer_ref.val = (argc > 1 && JS_IsFunction (argv[1])) ? argv[1] : JS_NULL;
|
replacer_ref.val = (argc > 1 && JS_IsFunction (argv[1])) ? argv[1] : JS_NULL;
|
||||||
|
|
||||||
NotaEncodeContext enc_s, *enc = &enc_s;
|
NotaEncodeContext enc_s, *enc = &enc_s;
|
||||||
enc->ctx = ctx;
|
enc->ctx = ctx;
|
||||||
enc->visitedStack_ref = &stack_ref;
|
enc->visited_list = NULL;
|
||||||
enc->cycle = 0;
|
enc->cycle = 0;
|
||||||
enc->replacer_ref = &replacer_ref;
|
enc->replacer_ref = &replacer_ref;
|
||||||
|
|
||||||
@@ -11678,7 +11682,6 @@ static JSValue js_nota_encode (JSContext *ctx, JSValueConst this_val, int argc,
|
|||||||
|
|
||||||
JS_PopGCRef (ctx, &key_ref);
|
JS_PopGCRef (ctx, &key_ref);
|
||||||
JS_PopGCRef (ctx, &replacer_ref);
|
JS_PopGCRef (ctx, &replacer_ref);
|
||||||
JS_PopGCRef (ctx, &stack_ref);
|
|
||||||
JS_PopGCRef (ctx, &val_ref);
|
JS_PopGCRef (ctx, &val_ref);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -429,11 +429,15 @@ char *wota_read_text_len(size_t *byte_len, char **text_utf8, char *wota)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Copy bytes from the packed 64-bit words */
|
/* Copy bytes from the packed 64-bit words */
|
||||||
for (long long i = 0; i < nwords; i++) {
|
size_t full_words = nbytes / 8;
|
||||||
uint64_t wval = data_words[i];
|
size_t remainder = nbytes % 8;
|
||||||
for (int j = 0; j < 8 && (i * 8 + j) < (long long)nbytes; j++) {
|
for (size_t i = 0; i < full_words; i++) {
|
||||||
out[i * 8 + j] = (char)((wval >> (56 - j * 8)) & 0xff);
|
uint64_t wval = wota_bswap64(data_words[i]);
|
||||||
}
|
memcpy(out + i * 8, &wval, 8);
|
||||||
|
}
|
||||||
|
if (remainder > 0) {
|
||||||
|
uint64_t wval = wota_bswap64(data_words[full_words]);
|
||||||
|
memcpy(out + full_words * 8, &wval, remainder);
|
||||||
}
|
}
|
||||||
|
|
||||||
out[nbytes] = '\0';
|
out[nbytes] = '\0';
|
||||||
@@ -609,14 +613,18 @@ void wota_write_text_len(WotaBuffer *wb, const char *utf8, size_t nbytes)
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint64_t *blocks = wota_buffer_alloc(wb, nwords);
|
uint64_t *blocks = wota_buffer_alloc(wb, nwords);
|
||||||
memset(blocks, 0, nwords * sizeof(uint64_t));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < nwords; i++) {
|
size_t full_words = nbytes / 8;
|
||||||
|
size_t remainder = nbytes % 8;
|
||||||
|
for (size_t i = 0; i < full_words; i++) {
|
||||||
|
uint64_t wval;
|
||||||
|
memcpy(&wval, utf8 + i * 8, 8);
|
||||||
|
blocks[i] = wota_bswap64(wval);
|
||||||
|
}
|
||||||
|
if (remainder > 0) {
|
||||||
uint64_t wval = 0;
|
uint64_t wval = 0;
|
||||||
for (int j = 0; j < 8 && (i * 8 + j) < nbytes; j++) {
|
memcpy(&wval, utf8 + full_words * 8, remainder);
|
||||||
wval |= ((uint64_t)(unsigned char)utf8[i * 8 + j]) << (56 - j * 8);
|
blocks[full_words] = wota_bswap64(wval);
|
||||||
}
|
|
||||||
blocks[i] = wval;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user