Minor nota speed improvement; use nota growable array internally so no more fixed size
All checks were successful
Build and Deploy / build-linux (push) Successful in 1m15s
Build and Deploy / build-windows (CLANG64) (push) Successful in 14m57s
Build and Deploy / package-dist (push) Has been skipped
Build and Deploy / deploy-itch (push) Has been skipped
Build and Deploy / deploy-gitea (push) Has been skipped
All checks were successful
Build and Deploy / build-linux (push) Successful in 1m15s
Build and Deploy / build-windows (CLANG64) (push) Successful in 14m57s
Build and Deploy / package-dist (push) Has been skipped
Build and Deploy / deploy-itch (push) Has been skipped
Build and Deploy / deploy-gitea (push) Has been skipped
This commit is contained in:
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@@ -54,20 +54,12 @@ jobs:
|
||||
- name: Check Out Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cache MSYS2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: C:\msys64
|
||||
key: ${{ runner.os }}-msys2-${{ matrix.msystem }}-${{ hashFiles('**/lockfiles', '.github/workflows/*.yml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-msys2-${{ matrix.msystem }}-
|
||||
|
||||
- name: Setup MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.msystem }}
|
||||
update: true
|
||||
cache: false
|
||||
cache: true
|
||||
install: |
|
||||
git
|
||||
zip
|
||||
|
||||
15
source/kim.h
15
source/kim.h
@@ -2,14 +2,11 @@
|
||||
#define KIM_H
|
||||
|
||||
// write number of runes from a kim stream int a utf8 stream
|
||||
void utf8_to_kim(const char **utf, char **kim);
|
||||
void utf8_to_kim(const char **utf, char **kim, long long *runeout);
|
||||
|
||||
// write number of runes from a kim stream int a utf8 stream
|
||||
void kim_to_utf8(char **kim, char **utf, int runes);
|
||||
|
||||
// Return the number of bytes a given utf-8 rune will have
|
||||
int utf8_bytes(char c);
|
||||
|
||||
// Return the number of runes in a utf8 string
|
||||
int utf8_count(const char *utf8);
|
||||
|
||||
@@ -24,7 +21,7 @@ void encode_utf8(char **s, int code);
|
||||
static void encode_kim(char **s, int code);
|
||||
int decode_kim(char **s);
|
||||
|
||||
int utf8_bytes(char c)
|
||||
static inline int utf8_bytes(char c)
|
||||
{
|
||||
int bytes = __builtin_clz(~(c));
|
||||
if (!bytes) return 1;
|
||||
@@ -105,11 +102,15 @@ int decode_kim(char **s)
|
||||
return rune;
|
||||
}
|
||||
|
||||
void utf8_to_kim(const char **utf, char **kim)
|
||||
void utf8_to_kim(const char **utf, char **kim, long long *runeout)
|
||||
{
|
||||
const char * str = *utf;
|
||||
while (*str)
|
||||
long long runes = 0;
|
||||
while (*str) {
|
||||
runes++;
|
||||
encode_kim(kim, decode_utf8(&str));
|
||||
}
|
||||
if (runeout) *runeout = runes;
|
||||
}
|
||||
|
||||
void kim_to_utf8(char **kim, char **utf, int runes)
|
||||
|
||||
801
source/nota.h
801
source/nota.h
@@ -1,13 +1,17 @@
|
||||
#ifndef NOTA_H
|
||||
#define NOTA_H
|
||||
|
||||
#define NOTA_BLOB 0x00 // C 0 0 0
|
||||
#define NOTA_TEXT 0x10 // C 0 0 1
|
||||
#define NOTA_ARR 0x20 // C 0 1 0
|
||||
#define NOTA_REC 0x30 // C 0 1 1
|
||||
#define NOTA_FLOAT 0x40 // C 1 0
|
||||
#define NOTA_INT 0x60 // C 1 1 0
|
||||
#define NOTA_SYM 0x70 // C 1 1 1
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Nota type nibble values */
|
||||
#define NOTA_BLOB 0x00
|
||||
#define NOTA_TEXT 0x10
|
||||
#define NOTA_ARR 0x20
|
||||
#define NOTA_REC 0x30
|
||||
#define NOTA_FLOAT 0x40
|
||||
#define NOTA_INT 0x60
|
||||
#define NOTA_SYM 0x70
|
||||
|
||||
#define NOTA_NULL 0x00
|
||||
#define NOTA_FALSE 0x02
|
||||
@@ -16,40 +20,7 @@
|
||||
#define NOTA_PRIVATE 0x08
|
||||
#define NOTA_SYSTEM 0x09
|
||||
|
||||
// Returns the type NOTA_ of the byte at *nota
|
||||
int nota_type(char *nota);
|
||||
|
||||
// Functions take a pointer to a buffer *nota, read or write the value, and then return a pointer to the next byte of the stream
|
||||
|
||||
// Pass NULL into the read in variable to skip over it
|
||||
|
||||
char *nota_read_blob(long long *len, char **blob, char *nota);
|
||||
// ALLOCATES! Uses strdup to return it via the text pointer
|
||||
char *nota_read_text(char **text, char *nota);
|
||||
char *nota_read_array(long long *len, char *nota);
|
||||
char *nota_read_record(long long *len, char *nota);
|
||||
char *nota_read_float(double *d, char *nota);
|
||||
char *nota_read_int(long long *l, char *nota);
|
||||
char *nota_read_sym(int *sym, char *nota);
|
||||
|
||||
char *nota_write_blob(unsigned long long n, char *data, char *nota);
|
||||
char *nota_write_text(const char *s, char *nota);
|
||||
char *nota_write_array(unsigned long long n, char *nota);
|
||||
char *nota_write_record(unsigned long long n, char *nota);
|
||||
char *nota_write_number(double n, char *nota);
|
||||
char *nota_write_sym(int sym, char *nota);
|
||||
|
||||
void print_nota_hex(char *nota);
|
||||
|
||||
#ifdef NOTA_IMPLEMENTATION
|
||||
|
||||
#include "stdio.h"
|
||||
#include "math.h"
|
||||
#include "string.h"
|
||||
#include "stdlib.h"
|
||||
#include "limits.h"
|
||||
#include "kim.h"
|
||||
|
||||
/* Some internal constants/macros (used in varint logic, etc.) */
|
||||
#define NOTA_CONT 0x80
|
||||
#define NOTA_DATA 0x7f
|
||||
#define NOTA_INT_DATA 0x07
|
||||
@@ -58,38 +29,74 @@ void print_nota_hex(char *nota);
|
||||
#define NOTA_EXP_SIGN(CHAR) (CHAR & (1<<4))
|
||||
#define NOTA_TYPE 0x70
|
||||
#define NOTA_HEAD_DATA 0x0f
|
||||
#define CONTINUE(CHAR) (CHAR>>7)
|
||||
|
||||
#define CONTINUE(CHAR) ((CHAR)>>7)
|
||||
#define UTF8_DATA 0x3f
|
||||
|
||||
/* define this to use native string instead of kim. Bytes are encoded instead of runes */
|
||||
#define NOTA_UTF8
|
||||
/* A helper to get the high-level Nota type nibble from a byte */
|
||||
static inline int nota_type(const char *nota) { return (*nota) & 0x70; }
|
||||
|
||||
int nota_type(char *nota) { return *nota & NOTA_TYPE; }
|
||||
char *nota_read_blob(long long *len, char **blob, char *nota);
|
||||
char *nota_read_text(char **text, char *nota);
|
||||
char *nota_read_array(long long *len, char *nota);
|
||||
char *nota_read_record(long long *len, char *nota);
|
||||
char *nota_read_float(double *d, char *nota);
|
||||
char *nota_read_int(long long *n, char *nota);
|
||||
char *nota_read_sym(int *sym, char *nota);
|
||||
|
||||
char *nota_skip(char *nota)
|
||||
typedef struct NotaBuffer {
|
||||
char *data;
|
||||
size_t size; /* number of bytes used */
|
||||
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);
|
||||
void nota_write_text (NotaBuffer *nb, const char *s);
|
||||
void nota_write_array (NotaBuffer *nb, unsigned long long count);
|
||||
void nota_write_record(NotaBuffer *nb, unsigned long long count);
|
||||
void nota_write_number(NotaBuffer *nb, double n);
|
||||
void nota_write_sym (NotaBuffer *nb, int sym);
|
||||
|
||||
#ifdef NOTA_IMPLEMENTATION
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "kim.h"
|
||||
|
||||
/* -------------------------------------------------------
|
||||
HELPER: skip a varint
|
||||
------------------------------------------------------- */
|
||||
static inline char *nota_skip(char *nota)
|
||||
{
|
||||
while (CONTINUE(*nota))
|
||||
while (CONTINUE(*nota)) {
|
||||
nota++;
|
||||
|
||||
return nota+1;
|
||||
}
|
||||
return nota + 1;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------
|
||||
HELPER: read a varint
|
||||
------------------------------------------------------- */
|
||||
char *nota_read_num(long long *n, char *nota)
|
||||
{
|
||||
if (!n) {
|
||||
return nota_skip(nota);
|
||||
}
|
||||
|
||||
// Start by reading the first byte
|
||||
unsigned char b = *nota;
|
||||
int result = b & NOTA_HEAD_DATA; // lower bits
|
||||
unsigned char b = (unsigned char)*nota;
|
||||
long long result = b & NOTA_HEAD_DATA;
|
||||
nota++;
|
||||
|
||||
// While the top bit is set, read more 7-bit chunks
|
||||
while (CONTINUE(b)) {
|
||||
b = *nota;
|
||||
nota++;
|
||||
while (b & NOTA_CONT) {
|
||||
b = (unsigned char)*nota++;
|
||||
result = (result << 7) | (b & NOTA_DATA);
|
||||
}
|
||||
|
||||
@@ -97,34 +104,36 @@ char *nota_read_num(long long *n, char *nota)
|
||||
return nota;
|
||||
}
|
||||
|
||||
// Given a number n, and bits used in the first char sb, how many bits are needed
|
||||
int nota_bits(long long n, int sb)
|
||||
/* Count how many bits of varint we need to encode n,
|
||||
with sb “special bits” in the first byte. */
|
||||
static inline int nota_bits(long long n, int sb)
|
||||
{
|
||||
if (n == 0) return sb;
|
||||
int bits = sizeof(n)*CHAR_BIT - __builtin_clzll(n);
|
||||
bits-=sb;
|
||||
int bits = (sizeof(n)*CHAR_BIT) - __builtin_clzll(n);
|
||||
bits -= sb;
|
||||
int needed = ((bits + 6) / 7)*7 + sb;
|
||||
return needed;
|
||||
}
|
||||
|
||||
// write a number from n into *nota, with sb bits in the first char
|
||||
char *nota_continue_num(long long n, char *nota, int sb)
|
||||
/* 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);
|
||||
bits -= sb;
|
||||
|
||||
if (bits > 0)
|
||||
nota[0] |= NOTA_CONT;
|
||||
else
|
||||
nota[0] &= ~NOTA_CONT;
|
||||
|
||||
int shex = (~0) << sb;
|
||||
nota[0] &= shex; /* clear shex bits */
|
||||
nota[0] |= (~shex) & (n>>bits);
|
||||
nota[0] &= shex; /* clear sb bits */
|
||||
nota[0] |= (~shex) & ((unsigned long long)n >> bits);
|
||||
|
||||
int i = 1;
|
||||
while (bits > 0) {
|
||||
bits -= 7;
|
||||
int head = bits == 0 ? 0 : NOTA_CONT;
|
||||
int head = (bits == 0) ? 0 : NOTA_CONT;
|
||||
nota[i] = head | (NOTA_DATA & (n >> bits));
|
||||
i++;
|
||||
}
|
||||
@@ -132,358 +141,30 @@ char *nota_continue_num(long long n, char *nota, int sb)
|
||||
return ¬a[i];
|
||||
}
|
||||
|
||||
|
||||
void print_nota_hex(char *nota)
|
||||
char *nota_read_blob(long long *len, char **blob, char *nota)
|
||||
{
|
||||
do {
|
||||
printf("%02X ", (unsigned char)(*nota));
|
||||
} while(CONTINUE(*(nota++)));
|
||||
printf("\n");
|
||||
|
||||
return;
|
||||
long long chars = 0;
|
||||
if (!((*nota>>4 & 0x07) ^ NOTA_TEXT>>4))
|
||||
nota_read_num(&chars, nota);
|
||||
|
||||
if ((*nota>>5) == 2 || (*nota>>5) == 6)
|
||||
chars = 1;
|
||||
|
||||
for (int i = 0; i < chars+1; i++) {
|
||||
do {
|
||||
printf("%02X ", (unsigned char)(*nota));
|
||||
} while(CONTINUE(*(nota++)));
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
if (!len) return nota;
|
||||
nota = nota_read_num(len, nota);
|
||||
int bytes = (int)floor((*len + 7) / 8.0);
|
||||
*len = bytes;
|
||||
*blob = (char *)malloc(bytes);
|
||||
memcpy(*blob, nota, bytes);
|
||||
return nota + bytes;
|
||||
}
|
||||
|
||||
char *nota_write_int(long long n, char *nota)
|
||||
char *nota_read_text(char **text, char *nota)
|
||||
{
|
||||
char sign = 0;
|
||||
long long chars;
|
||||
nota = nota_read_num(&chars, nota);
|
||||
|
||||
if (n < 0) {
|
||||
sign = 0x08;
|
||||
n *= -1;
|
||||
}
|
||||
|
||||
*nota = NOTA_INT | sign;
|
||||
|
||||
return nota_continue_num(n, nota, 3);
|
||||
}
|
||||
|
||||
#define NOTA_DBL_PREC 6
|
||||
#define xstr(s) str(s)
|
||||
#define str(s) #s
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
void extract_mantissa_coefficient(double num, long *coefficient, long *exponent)
|
||||
{
|
||||
if (num == 0.0) {
|
||||
*coefficient = 0;
|
||||
*exponent = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Optional: handle sign separately if you want 'coefficient' always positive.
|
||||
// For simplicity, let's just let atol(...) parse the sign if it's there.
|
||||
|
||||
// 1) Slightly round the number to avoid too many FP trailing digits:
|
||||
// Example: Round to 12 decimal places.
|
||||
double rounded = floor(fabs(num) * 1e12 + 0.5) / 1e12;
|
||||
if (num < 0) {
|
||||
rounded = -rounded;
|
||||
}
|
||||
|
||||
// 2) Convert to string with fewer digits of precision so we do NOT get
|
||||
// the long binary-fraction expansions (like 98.599999999999994).
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "%.14g", rounded);
|
||||
|
||||
// 3) Look for scientific notation
|
||||
char *exp_pos = strpbrk(buf, "eE");
|
||||
long exp_from_sci = 0;
|
||||
if (exp_pos) {
|
||||
exp_from_sci = atol(exp_pos + 1);
|
||||
*exp_pos = '\0'; // Truncate the exponent part from the string
|
||||
}
|
||||
|
||||
// 4) Find decimal point
|
||||
char *dec_point = strchr(buf, '.');
|
||||
int digits_after_decimal = 0;
|
||||
|
||||
if (dec_point) {
|
||||
digits_after_decimal = (int)strlen(dec_point + 1);
|
||||
// Remove the '.' by shifting the remainder left
|
||||
memmove(dec_point, dec_point + 1, strlen(dec_point));
|
||||
}
|
||||
|
||||
// 5) Now the string is just an integer (possibly signed), so parse it
|
||||
long long coeff_ll = atoll(buf); // support up to 64-bit range
|
||||
*coefficient = (long)coeff_ll;
|
||||
|
||||
// 6) The final decimal exponent is whatever came from 'e/E'
|
||||
// minus however many digits we removed by removing the decimal point.
|
||||
*exponent = exp_from_sci - digits_after_decimal;
|
||||
}
|
||||
|
||||
char *nota_write_decimal_str(const char *decimal, char *nota)
|
||||
{
|
||||
// Handle negative sign
|
||||
int neg = (decimal[0] == '-');
|
||||
if (neg) decimal++; // Skip the '-' if present
|
||||
|
||||
// Parse integer part
|
||||
long coef = 0;
|
||||
long exp = 0;
|
||||
int decimal_point_seen = 0;
|
||||
const char *ptr = decimal;
|
||||
int has_exponent = 0;
|
||||
|
||||
// First pass: calculate coefficient up to 'e' or 'E'
|
||||
while (*ptr && *ptr != 'e' && *ptr != 'E') {
|
||||
if (*ptr == '.') {
|
||||
decimal_point_seen = 1;
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*ptr >= '0' && *ptr <= '9') {
|
||||
coef = coef * 10 + (*ptr - '0');
|
||||
if (decimal_point_seen) {
|
||||
exp--; // Each digit after decimal point decreases exponent
|
||||
}
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
|
||||
// Parse exponent part if present
|
||||
if (*ptr == 'e' || *ptr == 'E') {
|
||||
has_exponent = 1;
|
||||
ptr++; // Skip 'e' or 'E'
|
||||
|
||||
int exp_sign = 1;
|
||||
if (*ptr == '-') {
|
||||
exp_sign = -1;
|
||||
ptr++;
|
||||
} else if (*ptr == '+') {
|
||||
ptr++;
|
||||
}
|
||||
|
||||
long explicit_exp = 0;
|
||||
while (*ptr >= '0' && *ptr <= '9') {
|
||||
explicit_exp = explicit_exp * 10 + (*ptr - '0');
|
||||
ptr++;
|
||||
}
|
||||
exp += exp_sign * explicit_exp;
|
||||
}
|
||||
|
||||
// If no decimal point and no exponent, treat as integer
|
||||
if (!decimal_point_seen && !has_exponent) {
|
||||
return nota_write_int(coef * (neg ? -1 : 1), nota);
|
||||
}
|
||||
|
||||
// Remove trailing zeros from coefficient
|
||||
while (coef > 0 && coef % 10 == 0 && exp < 0) {
|
||||
coef /= 10;
|
||||
exp++;
|
||||
}
|
||||
|
||||
// Handle zero case
|
||||
if (coef == 0) {
|
||||
return nota_write_int(0, nota);
|
||||
}
|
||||
|
||||
// Set up the notation format similar to nota_write_float
|
||||
int expsign = exp < 0 ? ~0 : 0;
|
||||
exp = llabs(exp);
|
||||
|
||||
nota[0] = NOTA_FLOAT;
|
||||
nota[0] |= (expsign & 1) << 4; // Exponent sign bit
|
||||
nota[0] |= (neg & 1) << 3; // Number sign bit
|
||||
|
||||
char *c = nota_continue_num(exp, nota, 3);
|
||||
return nota_continue_num(coef, c, 7);
|
||||
}
|
||||
|
||||
char *nota_write_float(double n, char *nota)
|
||||
{
|
||||
int neg = (n < 0);
|
||||
long coef;
|
||||
long exp;
|
||||
extract_mantissa_coefficient(n, &coef, &exp);
|
||||
|
||||
// Store integer if exponent is zero
|
||||
if (exp == 0)
|
||||
return nota_write_int(coef * (neg ? -1 : 1), nota);
|
||||
|
||||
int expsign = exp < 0 ? ~0 : 0;
|
||||
exp = labs(exp);
|
||||
|
||||
nota[0] = NOTA_FLOAT;
|
||||
nota[0] |= (expsign & 1) << 4;
|
||||
nota[0] |= (neg & 1) << 3;
|
||||
|
||||
char *c = nota_continue_num(exp, nota, 3);
|
||||
return nota_continue_num(labs(coef), c, 7);
|
||||
}
|
||||
|
||||
char *nota_read_float_str(char **d, char *nota)
|
||||
{
|
||||
// Extract sign bits from the first byte
|
||||
int neg = NOTA_SIG_SIGN(*nota); // bit 3 => mantissa sign
|
||||
int esign = NOTA_EXP_SIGN(*nota); // bit 4 => exponent sign
|
||||
|
||||
// Read the exponent’s lower 3 bits from the first byte
|
||||
long long e = (*nota) & NOTA_INT_DATA; // NOTA_INT_DATA = 0x07
|
||||
|
||||
// Count exponent bytes and accumulate value
|
||||
int e_bytes = 1;
|
||||
while (CONTINUE(*nota)) {
|
||||
nota++;
|
||||
e = (e << 7) | ((*nota) & NOTA_DATA); // NOTA_DATA = 0x7F
|
||||
e_bytes++;
|
||||
}
|
||||
|
||||
// Move past the last exponent byte
|
||||
nota++;
|
||||
|
||||
// Read the mantissa
|
||||
long long sig = (*nota) & NOTA_DATA;
|
||||
int sig_bytes = 1;
|
||||
while (CONTINUE(*nota)) {
|
||||
nota++;
|
||||
sig = (sig << 7) | ((*nota) & NOTA_DATA);
|
||||
sig_bytes++;
|
||||
}
|
||||
|
||||
// Move past the last mantissa byte
|
||||
nota++;
|
||||
|
||||
// Apply sign bits
|
||||
if (neg) sig = -sig;
|
||||
if (esign) e = -e;
|
||||
|
||||
// Calculate digits in mantissa (sig) and exponent (e)
|
||||
int sig_digits = (sig == 0) ? 1 : (int)log10(llabs(sig)) + 1;
|
||||
int e_digits = (e == 0) ? 1 : (int)log10(llabs(e)) + 1;
|
||||
|
||||
// Calculate total string size:
|
||||
// - Mantissa: sign (1), digits, decimal (1), 2 decimal places
|
||||
// - Exponent: 'e', sign (1), digits
|
||||
// - Null terminator (1)
|
||||
int size = 1 + sig_digits + 1 + 2 + 1 + 1 + e_digits + 1;
|
||||
if (neg) size++; // Extra space for negative mantissa
|
||||
if (esign) size++; // Extra space for negative exponent
|
||||
|
||||
// Allocate the string
|
||||
char *result = (char *)malloc(size);
|
||||
if (!result) {
|
||||
*d = NULL;
|
||||
return nota; // Return current position even on failure
|
||||
}
|
||||
|
||||
// Format the string as "xey" (e.g., "1.23e4")
|
||||
double value = (double)sig * pow(10.0, (double)e);
|
||||
snprintf(result, size, "%.*fe%lld", 2, value/pow(10.0, (double)e), e);
|
||||
|
||||
// Set the output pointer and return
|
||||
*d = result;
|
||||
char utf[chars*4 + 1]; /* enough for wide chars + null */
|
||||
char *pp = utf;
|
||||
kim_to_utf8(¬a, &pp, chars);
|
||||
*pp = 0;
|
||||
*text = strdup(utf);
|
||||
return nota;
|
||||
}
|
||||
|
||||
char *nota_read_float(double *d, char *nota)
|
||||
{
|
||||
// If the caller passed NULL for d, just skip over the float encoding
|
||||
if (!d) {
|
||||
return nota_skip(nota);
|
||||
}
|
||||
|
||||
// Extract sign bits from the first byte
|
||||
int neg = NOTA_SIG_SIGN(*nota); // bit 3 => mantissa sign
|
||||
int esign = NOTA_EXP_SIGN(*nota); // bit 4 => exponent sign
|
||||
|
||||
// Read the exponent’s lower 3 bits from the first byte
|
||||
long long e = (*nota) & NOTA_INT_DATA; // NOTA_INT_DATA = 0x07
|
||||
|
||||
// While the continuation bit is set, advance and accumulate exponent
|
||||
while (CONTINUE(*nota)) {
|
||||
nota++;
|
||||
e = (e << 7) | ((*nota) & NOTA_DATA); // NOTA_DATA = 0x7F
|
||||
}
|
||||
|
||||
// Move past the last exponent byte
|
||||
nota++;
|
||||
|
||||
// Now read the mantissa in the same variable-length style
|
||||
long long sig = (*nota) & NOTA_DATA;
|
||||
while (CONTINUE(*nota)) {
|
||||
nota++;
|
||||
sig = (sig << 7) | ((*nota) & NOTA_DATA);
|
||||
}
|
||||
|
||||
// Move past the last mantissa byte
|
||||
nota++;
|
||||
|
||||
// Apply sign bits
|
||||
if (neg) sig = -sig;
|
||||
if (esign) e = -e;
|
||||
|
||||
// Finally compute the double value: mantissa * 10^exponent
|
||||
*d = (double)sig * pow(10.0, (double)e);
|
||||
|
||||
// Return the pointer to wherever we ended
|
||||
return nota;
|
||||
}
|
||||
|
||||
char *nota_write_number(double n, char *nota)
|
||||
{
|
||||
if (n < (double)INT64_MIN || n > (double)INT64_MAX) return nota_write_float(n, nota);
|
||||
|
||||
double int_part;
|
||||
double frac = modf(n, &int_part);
|
||||
|
||||
if (fabs(frac) < 1e-14)
|
||||
return nota_write_int((long long)int_part, nota);
|
||||
else
|
||||
return nota_write_float(n, nota);
|
||||
}
|
||||
|
||||
char *nota_read_int(long long *n, char *nota)
|
||||
{
|
||||
if (!n)
|
||||
return nota_skip(nota);
|
||||
|
||||
*n = 0;
|
||||
char *c = nota;
|
||||
*n |= (*c) & NOTA_INT_DATA; /* first three bits */
|
||||
while (CONTINUE(*(c++)))
|
||||
*n = (*n<<7) | (*c & NOTA_DATA);
|
||||
|
||||
if (NOTA_INT_SIGN(*nota)) *n *= -1;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* n is the number of bits */
|
||||
char *nota_write_blob(unsigned long long n, char *data, char *nota)
|
||||
{
|
||||
nota[0] = NOTA_BLOB;
|
||||
nota = nota_continue_num(n, nota, 4);
|
||||
int bytes = floor((n+7)/8);
|
||||
for (int i = 0; i < bytes; i++)
|
||||
nota[i] = data[i];
|
||||
|
||||
return nota+bytes;
|
||||
}
|
||||
|
||||
char *nota_write_array(unsigned long long n, char *nota)
|
||||
{
|
||||
nota[0] = NOTA_ARR;
|
||||
return nota_continue_num(n, nota, 4);
|
||||
}
|
||||
|
||||
char *nota_read_array(long long *len, char *nota)
|
||||
{
|
||||
if (!len) return nota;
|
||||
@@ -496,59 +177,285 @@ char *nota_read_record(long long *len, char *nota)
|
||||
return nota_read_num(len, nota);
|
||||
}
|
||||
|
||||
char *nota_read_blob(long long *len, char **blob, char *nota)
|
||||
char *nota_read_float(double *d, char *nota)
|
||||
{
|
||||
if (!len) return nota;
|
||||
nota = nota_read_num(len,nota);
|
||||
int bytes = floor((*len+7)/8);
|
||||
*len = bytes;
|
||||
if (!d) {
|
||||
return nota_skip(nota);
|
||||
}
|
||||
|
||||
*blob = malloc(bytes);
|
||||
memcpy(*blob,nota,bytes);
|
||||
int neg = NOTA_SIG_SIGN(*nota);
|
||||
int esign = NOTA_EXP_SIGN(*nota);
|
||||
|
||||
return nota+bytes;
|
||||
long long e = (*nota) & NOTA_INT_DATA;
|
||||
while (CONTINUE(*nota)) {
|
||||
nota++;
|
||||
e = (e << 7) | ((*nota) & NOTA_DATA);
|
||||
}
|
||||
nota++;
|
||||
|
||||
long long sig = (*nota) & NOTA_DATA;
|
||||
while (CONTINUE(*nota)) {
|
||||
nota++;
|
||||
sig = (sig << 7) | ((*nota) & NOTA_DATA);
|
||||
}
|
||||
nota++;
|
||||
|
||||
if (neg) sig = -sig;
|
||||
if (esign) e = -e;
|
||||
|
||||
*d = (double)sig * pow(10.0, (double)e);
|
||||
return nota;
|
||||
}
|
||||
|
||||
char *nota_write_record(unsigned long long n, char *nota)
|
||||
char *nota_read_int(long long *n, char *nota)
|
||||
{
|
||||
nota[0] = NOTA_REC;
|
||||
return nota_continue_num(n, nota, 4);
|
||||
}
|
||||
if (!n) return nota_skip(nota);
|
||||
|
||||
char *nota_write_sym(int sym, char *nota)
|
||||
{
|
||||
*nota = NOTA_SYM | sym;
|
||||
return nota+1;
|
||||
*n = 0;
|
||||
char *c = nota;
|
||||
*n |= (*c) & NOTA_INT_DATA;
|
||||
while (CONTINUE(*(c++))) {
|
||||
*n = (*n << 7) | (*c & NOTA_DATA);
|
||||
}
|
||||
|
||||
/* if sign bit is set in the first byte, negative. */
|
||||
if (NOTA_INT_SIGN(*nota)) *n = -*n;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
char *nota_read_sym(int *sym, char *nota)
|
||||
{
|
||||
if (sym) *sym = (*nota) & 0x0f;
|
||||
return nota+1;
|
||||
if (sym) *sym = ((*nota) & 0x0f);
|
||||
return nota + 1;
|
||||
}
|
||||
|
||||
char *nota_read_text(char **text, char *nota)
|
||||
static void nota_buffer_grow(NotaBuffer *nb, size_t min_add)
|
||||
{
|
||||
long long chars;
|
||||
nota = nota_read_num(&chars, nota);
|
||||
size_t needed = nb->size + min_add;
|
||||
if (needed <= nb->capacity) return;
|
||||
|
||||
char utf[chars*4]; // enough for the worst case scenario
|
||||
char *pp = utf;
|
||||
kim_to_utf8(¬a, &pp, chars);
|
||||
*pp = 0;
|
||||
*text = strdup(utf);
|
||||
|
||||
return nota;
|
||||
size_t new_cap = (nb->capacity == 0 ? 64 : nb->capacity * 2);
|
||||
while (new_cap < needed) {
|
||||
new_cap *= 2;
|
||||
}
|
||||
char *new_data = (char *)realloc(nb->data, new_cap);
|
||||
if (!new_data) {
|
||||
fprintf(stderr, "realloc failed in nota_buffer_grow\n");
|
||||
abort();
|
||||
}
|
||||
nb->data = new_data;
|
||||
nb->capacity = new_cap;
|
||||
}
|
||||
|
||||
char *nota_write_text(const char *s, char *nota)
|
||||
void nota_buffer_init(NotaBuffer *nb, size_t initial_capacity)
|
||||
{
|
||||
nota[0] = NOTA_TEXT;
|
||||
long long n = utf8_count(s);
|
||||
nota = nota_continue_num(n,nota,4);
|
||||
utf8_to_kim(&s, ¬a);
|
||||
return nota;
|
||||
nb->data = NULL;
|
||||
nb->size = 0;
|
||||
nb->capacity = 0;
|
||||
if (initial_capacity > 0) {
|
||||
nb->data = (char *)malloc(initial_capacity);
|
||||
if (!nb->data) {
|
||||
fprintf(stderr, "malloc failed in nota_buffer_init\n");
|
||||
abort();
|
||||
}
|
||||
nb->capacity = initial_capacity;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
void nota_buffer_free(NotaBuffer *nb)
|
||||
{
|
||||
if (nb->data) free(nb->data);
|
||||
nb->data = NULL;
|
||||
nb->size = 0;
|
||||
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);
|
||||
char *p = nb->data + nb->size;
|
||||
nb->size += len;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void nota_write_int_buf(NotaBuffer *nb, long long n);
|
||||
static void nota_write_float_buf(NotaBuffer *nb, double d);
|
||||
|
||||
static void nota_write_int_or_float_buf(NotaBuffer *nb, double n)
|
||||
{
|
||||
if (n < (double)INT64_MIN || n > (double)INT64_MAX) {
|
||||
nota_write_float_buf(nb, n);
|
||||
return;
|
||||
}
|
||||
|
||||
double ip;
|
||||
double frac = modf(n, &ip);
|
||||
if (fabs(frac) < 1e-14)
|
||||
nota_write_int_buf(nb, (long long)ip);
|
||||
else
|
||||
nota_write_float_buf(nb, n);
|
||||
}
|
||||
|
||||
void nota_write_sym(NotaBuffer *nb, int sym)
|
||||
{
|
||||
char *p = nota_buffer_alloc(nb, 1);
|
||||
*p = NOTA_SYM | (sym & 0x0f);
|
||||
}
|
||||
|
||||
void nota_write_blob(NotaBuffer *nb, unsigned long long nbits, const char *data)
|
||||
{
|
||||
unsigned long long bytes_len = (nbits + 7ULL) >> 3;
|
||||
|
||||
char *p = nota_buffer_alloc(nb, 1 + 10 + bytes_len);
|
||||
p[0] = NOTA_BLOB;
|
||||
char *end = nota_continue_num(nbits, p, 4);
|
||||
|
||||
size_t varint_used = (size_t)(end - p - 1);
|
||||
|
||||
memcpy(end, data, (size_t)bytes_len);
|
||||
size_t total_used = 1 + varint_used + bytes_len;
|
||||
size_t allocated = 1 + 10 + bytes_len;
|
||||
|
||||
nb->size -= (allocated - total_used);
|
||||
}
|
||||
|
||||
void nota_write_text(NotaBuffer *nb, const char *s)
|
||||
{
|
||||
long long runes = utf8_count(s);
|
||||
|
||||
size_t max_kim = (size_t)(runes * 5);
|
||||
char *p = nota_buffer_alloc(nb, 1 + 10 + max_kim);
|
||||
|
||||
p[0] = NOTA_TEXT;
|
||||
char *end = nota_continue_num(runes, p, 4);
|
||||
|
||||
char *kim_out = end;
|
||||
const char *utf_in = s;
|
||||
while (*utf_in) {
|
||||
int codepoint = decode_utf8((char **)&utf_in);
|
||||
encode_kim(&kim_out, codepoint);
|
||||
}
|
||||
|
||||
size_t used = (size_t)(kim_out - p);
|
||||
size_t allocated = 1 + 10 + max_kim;
|
||||
|
||||
nb->size -= (allocated - used);
|
||||
}
|
||||
|
||||
void nota_write_array(NotaBuffer *nb, unsigned long long count)
|
||||
{
|
||||
char *p = nota_buffer_alloc(nb, 10);
|
||||
p[0] = NOTA_ARR;
|
||||
char *end = nota_continue_num(count, p, 4);
|
||||
size_t used = (size_t)(end - p);
|
||||
nb->size -= (10 - used);
|
||||
}
|
||||
|
||||
void nota_write_record(NotaBuffer *nb, unsigned long long count)
|
||||
{
|
||||
char *p = nota_buffer_alloc(nb, 10);
|
||||
p[0] = NOTA_REC;
|
||||
char *end = nota_continue_num(count, p, 4);
|
||||
size_t used = (size_t)(end - p);
|
||||
nb->size -= (10 - 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 */
|
||||
char *p = nota_buffer_alloc(nb, 10);
|
||||
char sign = 0;
|
||||
if (n < 0) {
|
||||
sign = 0x08; /* sign bit in the nibble */
|
||||
n = -n;
|
||||
}
|
||||
p[0] = NOTA_INT | sign;
|
||||
char *end = nota_continue_num(n, p, 3);
|
||||
size_t used = (size_t)(end - p);
|
||||
nb->size -= (10 - used);
|
||||
}
|
||||
|
||||
static void extract_mantissa_coefficient(double num, long *coefficient, long *exponent)
|
||||
{
|
||||
if (num == 0.0) {
|
||||
*coefficient = 0;
|
||||
*exponent = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Round to 12 decimal places to avoid floating artifacts. */
|
||||
double rounded = floor(fabs(num) * 1e12 + 0.5) / 1e12;
|
||||
if (num < 0) {
|
||||
rounded = -rounded;
|
||||
}
|
||||
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "%.14g", rounded);
|
||||
|
||||
char *exp_pos = strpbrk(buf, "eE");
|
||||
long exp_from_sci = 0;
|
||||
if (exp_pos) {
|
||||
exp_from_sci = atol(exp_pos + 1);
|
||||
*exp_pos = '\0';
|
||||
}
|
||||
|
||||
char *dec_point = strchr(buf, '.');
|
||||
int digits_after_decimal = 0;
|
||||
if (dec_point) {
|
||||
digits_after_decimal = (int)strlen(dec_point + 1);
|
||||
memmove(dec_point, dec_point + 1, strlen(dec_point));
|
||||
}
|
||||
|
||||
long long coeff_ll = atoll(buf);
|
||||
*coefficient = (long)coeff_ll;
|
||||
*exponent = exp_from_sci - digits_after_decimal;
|
||||
}
|
||||
|
||||
static void nota_write_float_buf(NotaBuffer *nb, double d)
|
||||
{
|
||||
if (d == 0.0) {
|
||||
nota_write_int_buf(nb, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
long coef, exp;
|
||||
extract_mantissa_coefficient(d, &coef, &exp);
|
||||
|
||||
if (coef == 0) {
|
||||
nota_write_int_buf(nb, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int neg = (d < 0.0);
|
||||
if (exp == 0) {
|
||||
nota_write_int_buf(nb, neg ? -coef : coef);
|
||||
return;
|
||||
}
|
||||
|
||||
char *p = nota_buffer_alloc(nb, 21);
|
||||
|
||||
p[0] = NOTA_FLOAT;
|
||||
if (neg) p[0] |= (1 << 3);
|
||||
if (exp < 0) {
|
||||
p[0] |= (1 << 4);
|
||||
exp = -exp;
|
||||
}
|
||||
|
||||
char *c = nota_continue_num(exp, p, 3);
|
||||
char *end = nota_continue_num(labs(coef), c, 7);
|
||||
|
||||
size_t used = (size_t)(end - p);
|
||||
nb->size -= (21 - used);
|
||||
}
|
||||
|
||||
#endif /* NOTA_IMPLEMENTATION */
|
||||
|
||||
#endif /* NOTA_H */
|
||||
|
||||
@@ -4,6 +4,45 @@
|
||||
#define NOTA_IMPLEMENTATION
|
||||
#include "nota.h"
|
||||
|
||||
typedef struct NotaEncodeContext {
|
||||
JSContext *ctx;
|
||||
JSValue visitedStack;
|
||||
NotaBuffer nb; // use the dynamic NotaBuffer
|
||||
int cycle;
|
||||
} NotaEncodeContext;
|
||||
|
||||
static void nota_stack_push(NotaEncodeContext *enc, JSValueConst val)
|
||||
{
|
||||
JSContext *ctx = enc->ctx;
|
||||
int len = JS_ArrayLength(ctx, enc->visitedStack);
|
||||
JS_SetPropertyInt64(ctx, enc->visitedStack, len, JS_DupValue(ctx, val));
|
||||
}
|
||||
|
||||
static void nota_stack_pop(NotaEncodeContext *enc)
|
||||
{
|
||||
JSContext *ctx = enc->ctx;
|
||||
int len = JS_ArrayLength(ctx, enc->visitedStack);
|
||||
JS_SetPropertyStr(ctx, enc->visitedStack, "length", JS_NewUint32(ctx, len - 1));
|
||||
}
|
||||
|
||||
static int nota_stack_has(NotaEncodeContext *enc, JSValueConst val)
|
||||
{
|
||||
JSContext *ctx = enc->ctx;
|
||||
|
||||
int len = JS_ArrayLength(ctx, enc->visitedStack);
|
||||
for (int i = 0; i < len; i++) {
|
||||
JSValue elem = JS_GetPropertyUint32(ctx, enc->visitedStack, i);
|
||||
if (JS_IsObject(elem) && JS_IsObject(val)) {
|
||||
if (JS_VALUE_GET_OBJ(elem) == JS_VALUE_GET_OBJ(val)) {
|
||||
JS_FreeValue(ctx, elem);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
JS_FreeValue(ctx, elem);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSValue number;
|
||||
|
||||
char *js_do_nota_decode(JSContext *js, JSValue *tmp, char *nota)
|
||||
@@ -18,14 +57,13 @@ char *js_do_nota_decode(JSContext *js, JSValue *tmp, char *nota)
|
||||
|
||||
switch(type) {
|
||||
case NOTA_BLOB:
|
||||
nota = nota_read_blob(&n, &blob, nota);
|
||||
nota = nota_read_blob(&n, (char**)&blob, nota);
|
||||
*tmp = JS_NewArrayBufferCopy(js, blob, n);
|
||||
free(blob);
|
||||
break;
|
||||
case NOTA_TEXT:
|
||||
nota = nota_read_text(&str, nota);
|
||||
*tmp = JS_NewString(js, str);
|
||||
/* TODO: Avoid malloc and free here */
|
||||
free(str);
|
||||
break;
|
||||
case NOTA_ARR:
|
||||
@@ -74,95 +112,149 @@ char *js_do_nota_decode(JSContext *js, JSValue *tmp, char *nota)
|
||||
return nota;
|
||||
}
|
||||
|
||||
// Writers the JSValue v into the buffer of char *nota, returning a pointer to the next byte in nota to be written
|
||||
char *js_do_nota_encode(JSContext *js, JSValue v, char *nota)
|
||||
static void nota_encode_value(NotaEncodeContext *enc, JSValueConst val);
|
||||
|
||||
static void encode_object_properties(NotaEncodeContext *enc, JSValueConst val)
|
||||
{
|
||||
int tag = JS_VALUE_GET_TAG(v);
|
||||
const char *str = NULL;
|
||||
JSContext *ctx = enc->ctx;
|
||||
|
||||
JSPropertyEnum *ptab;
|
||||
uint32_t plen;
|
||||
double nval;
|
||||
JSValue val;
|
||||
void *blob;
|
||||
size_t bloblen;
|
||||
|
||||
switch(tag) {
|
||||
case JS_TAG_FLOAT64:
|
||||
case JS_TAG_INT:
|
||||
case JS_TAG_BIG_DECIMAL:
|
||||
case JS_TAG_BIG_INT:
|
||||
case JS_TAG_BIG_FLOAT:
|
||||
JS_ToFloat64(js, &nval, v);
|
||||
return nota_write_number(nval, nota);
|
||||
/* str = JS_ToCString(js,v);
|
||||
nota = nota_write_decimal_str(str, nota);
|
||||
JS_FreeCString(js,str);
|
||||
return nota;
|
||||
*/
|
||||
case JS_TAG_STRING:
|
||||
str = JS_ToCString(js, v);
|
||||
nota = nota_write_text(str, nota);
|
||||
JS_FreeCString(js, str);
|
||||
return nota;
|
||||
case JS_TAG_BOOL:
|
||||
if (JS_VALUE_GET_BOOL(v)) return nota_write_sym(NOTA_TRUE, nota);
|
||||
else
|
||||
return nota_write_sym(NOTA_FALSE, nota);
|
||||
case JS_TAG_UNDEFINED:
|
||||
case JS_TAG_NULL:
|
||||
return nota_write_sym(NOTA_NULL, nota);
|
||||
case JS_TAG_OBJECT:
|
||||
blob = JS_GetArrayBuffer(js,&bloblen, v);
|
||||
if (blob)
|
||||
return nota_write_blob(bloblen*8, blob, nota);
|
||||
|
||||
if (JS_IsArray(js, v)) {
|
||||
JSValue lengthVal = JS_GetPropertyStr(js, v, "length");
|
||||
int n;
|
||||
JS_ToInt32(js, &n, lengthVal);
|
||||
JS_FreeValue(js, lengthVal);
|
||||
|
||||
nota = nota_write_array(n, nota);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
JSValue elemVal = JS_GetPropertyUint32(js, v, i);
|
||||
nota = js_do_nota_encode(js, elemVal, nota);
|
||||
JS_FreeValue(js, elemVal);
|
||||
}
|
||||
return nota;
|
||||
if (JS_GetOwnPropertyNames(ctx, &ptab, &plen, val, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK) < 0) {
|
||||
nota_write_sym(&enc->nb, NOTA_NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
JS_GetOwnPropertyNames(js, &ptab, &plen, v, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);
|
||||
nota = nota_write_record(plen, nota);
|
||||
nota_write_record(&enc->nb, plen);
|
||||
|
||||
for (int i = 0; i < plen; i++) {
|
||||
val = JS_GetProperty(js,v,ptab[i].atom);
|
||||
str = JS_AtomToCString(js, ptab[i].atom);
|
||||
JS_FreeAtom(js, ptab[i].atom);
|
||||
for (uint32_t i = 0; i < plen; i++) {
|
||||
// property name
|
||||
const char *propName = JS_AtomToCString(ctx, ptab[i].atom);
|
||||
nota_write_text(&enc->nb, propName);
|
||||
JS_FreeCString(ctx, propName);
|
||||
|
||||
nota = nota_write_text(str, nota);
|
||||
JS_FreeCString(js, str);
|
||||
// property value
|
||||
JSValue propVal = JS_GetProperty(ctx, val, ptab[i].atom);
|
||||
nota_encode_value(enc, propVal);
|
||||
JS_FreeValue(ctx, propVal);
|
||||
|
||||
nota = js_do_nota_encode(js, val, nota);
|
||||
JS_FreeValue(js,val);
|
||||
// free the atom
|
||||
JS_FreeAtom(ctx, ptab[i].atom);
|
||||
}
|
||||
js_free(js, ptab);
|
||||
return nota;
|
||||
default:
|
||||
return nota;
|
||||
}
|
||||
return nota;
|
||||
js_free(ctx, ptab);
|
||||
}
|
||||
|
||||
JSValue js_nota_encode(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
static void nota_encode_value(NotaEncodeContext *enc, JSValueConst val)
|
||||
{
|
||||
JSContext *ctx = enc->ctx;
|
||||
int tag = JS_VALUE_GET_TAG(val);
|
||||
|
||||
switch (tag) {
|
||||
case JS_TAG_INT:
|
||||
case JS_TAG_BIG_INT:
|
||||
case JS_TAG_FLOAT64:
|
||||
case JS_TAG_BIG_DECIMAL:
|
||||
case JS_TAG_BIG_FLOAT: {
|
||||
double d;
|
||||
JS_ToFloat64(ctx, &d, val);
|
||||
nota_write_number(&enc->nb, d);
|
||||
return;
|
||||
}
|
||||
|
||||
case JS_TAG_STRING: {
|
||||
const char *str = JS_ToCString(ctx, val);
|
||||
nota_write_text(&enc->nb, str);
|
||||
JS_FreeCString(ctx, str);
|
||||
return;
|
||||
}
|
||||
|
||||
case JS_TAG_BOOL: {
|
||||
if (JS_VALUE_GET_BOOL(val))
|
||||
nota_write_sym(&enc->nb, NOTA_TRUE);
|
||||
else
|
||||
nota_write_sym(&enc->nb, NOTA_FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
case JS_TAG_NULL:
|
||||
case JS_TAG_UNDEFINED:
|
||||
nota_write_sym(&enc->nb, NOTA_NULL);
|
||||
return;
|
||||
|
||||
case JS_TAG_OBJECT: {
|
||||
size_t bufLen;
|
||||
void *bufData = JS_GetArrayBuffer(ctx, &bufLen, val);
|
||||
if (bufData) {
|
||||
/* Write as a blob of bits (bufLen * 8). */
|
||||
nota_write_blob(&enc->nb, (unsigned long long)bufLen * 8, (const char*)bufData);
|
||||
return;
|
||||
}
|
||||
|
||||
if (JS_IsArray(ctx, val)) {
|
||||
if (nota_stack_has(enc, val)) {
|
||||
enc->cycle = 1;
|
||||
return; // bail out
|
||||
}
|
||||
nota_stack_push(enc, val);
|
||||
|
||||
int arrLen = JS_ArrayLength(ctx, val);
|
||||
nota_write_array(&enc->nb, arrLen);
|
||||
for (int i = 0; i < arrLen; i++) {
|
||||
JSValue elemVal = JS_GetPropertyUint32(ctx, val, i);
|
||||
nota_encode_value(enc, elemVal);
|
||||
JS_FreeValue(ctx, elemVal);
|
||||
}
|
||||
|
||||
nota_stack_pop(enc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nota_stack_has(enc, val)) {
|
||||
enc->cycle = 1;
|
||||
return; // bail out
|
||||
}
|
||||
|
||||
nota_stack_push(enc, val);
|
||||
encode_object_properties(enc, val);
|
||||
nota_stack_pop(enc);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
nota_write_sym(&enc->nb, NOTA_NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue js_nota_encode(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
if (argc < 1)
|
||||
JS_ThrowInternalError(js, "Expected at least one argument to encode.");
|
||||
return JS_ThrowTypeError(ctx, "nota.encode requires 1 argument");
|
||||
|
||||
JSValue obj = argv[0];
|
||||
char nota[1024*1024];
|
||||
char *e = js_do_nota_encode(js, obj, nota);
|
||||
return JS_NewArrayBufferCopy(js, (unsigned char*)nota, e-nota);
|
||||
NotaEncodeContext enc_s, *enc = &enc_s;
|
||||
enc->ctx = ctx;
|
||||
enc->visitedStack = JS_NewArray(ctx); // empty array initially
|
||||
enc->cycle = 0;
|
||||
|
||||
nota_buffer_init(&enc->nb, 128);
|
||||
|
||||
nota_encode_value(enc, argv[0]);
|
||||
|
||||
if (enc->cycle) {
|
||||
JS_FreeValue(ctx, enc->visitedStack);
|
||||
nota_buffer_free(&enc->nb);
|
||||
return JS_ThrowReferenceError(ctx, "Tried to encode something to nota with a cycle.");
|
||||
}
|
||||
|
||||
JS_FreeValue(ctx, enc->visitedStack);
|
||||
|
||||
size_t totalLen = enc->nb.size; // how many bytes used
|
||||
void* dataPtr = enc->nb.data; // pointer to the raw data
|
||||
JSValue ret = JS_NewArrayBufferCopy(ctx, (uint8_t*)dataPtr, totalLen);
|
||||
|
||||
nota_buffer_free(&enc->nb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSValue js_nota_decode(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
@@ -171,6 +263,8 @@ JSValue js_nota_decode(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
|
||||
size_t len;
|
||||
unsigned char *nota = JS_GetArrayBuffer(js, &len, argv[0]);
|
||||
if (!nota) return JS_UNDEFINED;
|
||||
|
||||
JSValue ret;
|
||||
js_do_nota_decode(js, &ret, (char*)nota);
|
||||
return ret;
|
||||
@@ -182,14 +276,17 @@ static const JSCFunctionListEntry js_nota_funcs[] = {
|
||||
};
|
||||
|
||||
static int js_nota_init(JSContext *ctx, JSModuleDef *m) {
|
||||
JS_SetModuleExportList(ctx, m, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
JS_SetModuleExportList(ctx, m, js_nota_funcs,
|
||||
sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSValue js_nota_use(JSContext *js)
|
||||
{
|
||||
JSValue export = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, export, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
JS_SetPropertyFunctionList(js, export,
|
||||
js_nota_funcs,
|
||||
sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
number = JS_GetPropertyStr(js, JS_GetGlobalObject(js), "Number");
|
||||
return export;
|
||||
}
|
||||
@@ -203,6 +300,7 @@ JSValue js_nota_use(JSContext *js)
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) {
|
||||
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_nota_init);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExportList(ctx, m, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
JS_AddModuleExportList(ctx, m, js_nota_funcs,
|
||||
sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return m;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user