This commit is contained in:
2025-09-15 22:52:55 -05:00
parent b5496d93bc
commit 5b2a88d520
8 changed files with 1348 additions and 228 deletions

View File

@@ -5,7 +5,6 @@
#include <stdint.h>
#include "kim.h"
/* Nota type nibble values */
#define NOTA_BLOB 0x00
#define NOTA_TEXT 0x10
#define NOTA_ARR 0x20
@@ -21,7 +20,6 @@
#define NOTA_PRIVATE 0x08
#define NOTA_SYSTEM 0x09
/* Some internal constants/macros (used in varint logic, etc.) */
#define NOTA_CONT 0x80
#define NOTA_DATA 0x7f
#define NOTA_INT_DATA 0x07
@@ -33,7 +31,6 @@
#define CONTINUE(CHAR) (CHAR>>7)
#define UTF8_DATA 0x3f
/* A helper to get the high-level Nota type nibble from a byte */
static inline int nota_type(const char *nota) { return (*nota) & 0x70; }
char *nota_read_blob(long long *len, char **blob, char *nota);
@@ -50,10 +47,8 @@ typedef struct NotaBuffer {
size_t capacity; /* allocated size of data */
} NotaBuffer;
/* Initialize a NotaBuffer with a given initial capacity. */
void nota_buffer_init(NotaBuffer *nb, size_t initial_capacity);
/* Free the buffer's internal memory. (Does NOT free nb itself.) */
void nota_buffer_free(NotaBuffer *nb);
void nota_write_blob (NotaBuffer *nb, unsigned long long nbits, const char *data);
@@ -73,25 +68,18 @@ void nota_write_sym (NotaBuffer *nb, int sym);
#include "kim.h"
/* -------------------------------------------------------
HELPER: skip a varint
------------------------------------------------------- */
static inline char *nota_skip(char *nota)
{
while (CONTINUE(*nota)) {
nota++;
}
while (CONTINUE(*nota))
nota++;
return nota + 1;
}
/* -------------------------------------------------------
HELPER: read a varint
------------------------------------------------------- */
char *nota_read_num(long long *n, char *nota)
{
if (!n) {
return nota_skip(nota);
}
if (!n)
return nota_skip(nota);
unsigned char b = (unsigned char)*nota;
long long result = b & NOTA_HEAD_DATA;
nota++;
@@ -106,7 +94,7 @@ char *nota_read_num(long long *n, char *nota)
}
/* Count how many bits of varint we need to encode n,
with sb “special bits” in the first byte. */
with sb “special bits” in the first byte */
static inline int nota_bits(long long n, int sb)
{
if (n == 0) return sb;
@@ -116,7 +104,6 @@ static inline int nota_bits(long long n, int sb)
return needed;
}
/* Write a varint into *nota, with sb bits in the first char (which is already set). */
static inline char *nota_continue_num(long long n, char *nota, int sb)
{
int bits = nota_bits(n, sb);
@@ -272,7 +259,6 @@ void nota_buffer_free(NotaBuffer *nb)
nb->capacity = 0;
}
/* Allocate 'len' bytes in the buffer and return a pointer to them. */
static char *nota_buffer_alloc(NotaBuffer *nb, size_t len)
{
nota_buffer_grow(nb, len);
@@ -363,183 +349,11 @@ void nota_write_record(NotaBuffer *nb, unsigned long long count)
nb->size -= (10 - used);
}
void nota_write_number_str(NotaBuffer *nb, const char *str)
{
/* -------------------------------------------
1) Parse sign
------------------------------------------- */
int negative = 0;
if (*str == '+') {
str++;
}
else if (*str == '-') {
negative = 1;
str++;
}
/* -------------------------------------------
2) Parse integer part
------------------------------------------- */
long long coefficient = 0;
int got_digits = 0;
while (*str >= '0' && *str <= '9') {
got_digits = 1;
int d = (*str - '0');
str++;
// Basic overflow check (very naive):
if (coefficient <= (LLONG_MAX - d) / 10) {
coefficient = coefficient * 10 + d;
} else {
// If you want to handle overflow by switching to float, do that here.
// For simplicity, let's just keep wrapping. In production, be careful!
coefficient = coefficient * 10 + d;
}
}
/* -------------------------------------------
3) Check for decimal part
------------------------------------------- */
int has_decimal_point = 0;
int fraction_digits = 0;
if (*str == '.') {
has_decimal_point = 1;
str++;
while (*str >= '0' && *str <= '9') {
got_digits = 1;
int d = (*str - '0');
str++;
fraction_digits++;
if (coefficient <= (LLONG_MAX - d) / 10) {
coefficient = coefficient * 10 + d;
} else {
// Same naive overflow comment
coefficient = coefficient * 10 + d;
}
}
}
/* -------------------------------------------
4) Check for exponent part
------------------------------------------- */
int exponent_negative = 0;
long long exponent_val = 0;
if (*str == 'e' || *str == 'E') {
str++;
if (*str == '+') {
str++;
}
else if (*str == '-') {
exponent_negative = 1;
str++;
}
while (*str >= '0' && *str <= '9') {
int d = (*str - '0');
str++;
if (exponent_val <= (LLONG_MAX - d) / 10) {
exponent_val = exponent_val * 10 + d;
} else {
// Again, naive overflow handling
exponent_val = exponent_val * 10 + d;
}
}
}
/* -------------------------------------------
5) If there were no valid digits at all,
store 0 and return. (simple fallback)
------------------------------------------- */
if (!got_digits) {
nota_write_int_buf(nb, 0);
return;
}
/* -------------------------------------------
6) Combine fraction digits into exponent
final_exponent = exponent_val - fraction_digits
(apply exponent sign if any)
------------------------------------------- */
if (exponent_negative) {
exponent_val = -exponent_val;
}
long long final_exponent = exponent_val - fraction_digits;
/* -------------------------------------------
7) Decide if we are storing an integer
or a float in Nota format.
-------------------------------------------
Rule used here:
- If there's no decimal point AND final_exponent == 0,
=> integer
- If we do have a decimal point, but fraction_digits == 0
and exponent_val == 0, then the user typed something
like "123." or "100.0". That is effectively an integer,
so store it as an integer if you want a purely numeric approach.
- Otherwise store as float.
------------------------------------------- */
// If "no decimal" => definitely integer:
// or decimal present but fraction_digits=0 & exponent_val=0 => integer
int treat_as_integer = 0;
if (!has_decimal_point && final_exponent == 0) {
treat_as_integer = 1;
}
else if (has_decimal_point && fraction_digits == 0 && exponent_val == 0) {
// Means "123." or "123.0"
treat_as_integer = 1;
}
if (treat_as_integer) {
// If negative => flip the sign in the stored value
if (negative) {
coefficient = -coefficient;
}
// Write the integer in Nota format (varint with sign bit)
nota_write_int_buf(nb, coefficient);
return;
}
/* -------------------------------------------
8) Write as float in Nota format
We do basically the same approach as
nota_write_float_buf does:
- NOTA_FLOAT nibble
- sign bit if negative
- exponent sign bit if final_exponent < 0
- varint of |final_exponent|
- varint of |coefficient|
------------------------------------------- */
{
char *p = nota_buffer_alloc(nb, 21); // Up to ~21 bytes worst-case
p[0] = NOTA_FLOAT;
if (negative) {
p[0] |= (1 << 3); // Mantissa sign bit
}
if (final_exponent < 0) {
p[0] |= (1 << 4); // Exponent sign bit
final_exponent = -final_exponent;
}
// Write exponent as varint (with 3 bits used in the first byte)
char *c = nota_continue_num(final_exponent, p, 3);
// Write the absolute coefficient (7 bits used in the first byte)
char *end = nota_continue_num(coefficient, c, 7);
// Adjust the buffer size to the actual used length
size_t used = (size_t)(end - p);
nb->size -= (21 - used);
}
}
void nota_write_number(NotaBuffer *nb, double n)
{
nota_write_int_or_float_buf(nb, n);
}
/* Write an integer in varint form (with sign bit) */
static void nota_write_int_buf(NotaBuffer *nb, long long n)
{
/* up to ~10 bytes for varint */

View File

@@ -146,19 +146,13 @@ static void nota_encode_value(NotaEncodeContext *enc, JSValueConst val, JSValueC
int tag = JS_VALUE_GET_TAG(replaced);
switch (tag) {
case JS_TAG_INT: {
case JS_TAG_INT:
case JS_TAG_FLOAT64: {
double d;
JS_ToFloat64(ctx, &d, replaced);
nota_write_number(&enc->nb, d);
break;
}
case JS_TAG_BIG_INT:
case JS_TAG_FLOAT64: {
const char *str = JS_ToCString(ctx, replaced);
nota_write_number_str(&enc->nb, str);
JS_FreeCString(ctx, str);
break;
}
case JS_TAG_STRING: {
const char *str = JS_ToCString(ctx, replaced);
nota_write_text(&enc->nb, str);