diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7f43edbd..0be716dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -153,7 +153,6 @@ jobs: deploy-itch: needs: [package-dist] - if: ${{ false }} runs-on: ubuntu-latest steps: - name: Check Out Code diff --git a/source/nota.h b/source/nota.h index 6b747e67..0440e41b 100755 --- a/source/nota.h +++ b/source/nota.h @@ -362,6 +362,177 @@ 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); diff --git a/source/qjs_nota.c b/source/qjs_nota.c index 1b530e91..3e8cb8db 100755 --- a/source/qjs_nota.c +++ b/source/qjs_nota.c @@ -150,14 +150,19 @@ static void nota_encode_value(NotaEncodeContext *enc, JSValueConst val) int tag = JS_VALUE_GET_TAG(val); switch (tag) { - case JS_TAG_INT: + case JS_TAG_INT: { + double d; + JS_ToFloat64(ctx, &d, val); + nota_write_number(&enc->nb, d); + return; + } 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); + const char *str = JS_ToCString(ctx, val); + nota_write_number_str(&enc->nb, str); + JS_FreeCString(ctx, str); return; }