+++ /dev/null
-/*
- * Tiny arbitrary precision floating point library
- *
- * Copyright (c) 2017-2021 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include <stdlib.h>
-#include <stdio.h>
-#include <inttypes.h>
-#include <math.h>
-#include <string.h>
-#include <assert.h>
-
-#ifdef __AVX2__
-#include <immintrin.h>
-#endif
-
-#include "cutils.h"
-#include "libbf.h"
-
-/* enable it to check the multiplication result */
-//#define USE_MUL_CHECK
-#ifdef CONFIG_BIGNUM
-/* enable it to use FFT/NTT multiplication */
-#define USE_FFT_MUL
-/* enable decimal floating point support */
-#define USE_BF_DEC
-#endif
-
-//#define inline __attribute__((always_inline))
-
-#ifdef __AVX2__
-#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */
-#else
-#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */
-#endif
-
-/* XXX: adjust */
-#define DIVNORM_LARGE_THRESHOLD 50
-#define UDIV1NORM_THRESHOLD 3
-
-#if LIMB_BITS == 64
-#define FMT_LIMB1 "%" PRIx64
-#define FMT_LIMB "%016" PRIx64
-#define PRId_LIMB PRId64
-#define PRIu_LIMB PRIu64
-
-#else
-
-#define FMT_LIMB1 "%x"
-#define FMT_LIMB "%08x"
-#define PRId_LIMB "d"
-#define PRIu_LIMB "u"
-
-#endif
-
-typedef intptr_t mp_size_t;
-
-typedef int bf_op2_func_t(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
- bf_flags_t flags);
-
-#ifdef USE_FFT_MUL
-
-#define FFT_MUL_R_OVERLAP_A (1 << 0)
-#define FFT_MUL_R_OVERLAP_B (1 << 1)
-#define FFT_MUL_R_NORESIZE (1 << 2)
-
-static no_inline int fft_mul(bf_context_t *s,
- bf_t *res, limb_t *a_tab, limb_t a_len,
- limb_t *b_tab, limb_t b_len, int mul_flags);
-static void fft_clear_cache(bf_context_t *s);
-#endif
-#ifdef USE_BF_DEC
-static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos);
-#endif
-
-
-/* could leading zeros */
-static inline int clz(limb_t a)
-{
- if (a == 0) {
- return LIMB_BITS;
- } else {
-#if LIMB_BITS == 64
- return clz64(a);
-#else
- return clz32(a);
-#endif
- }
-}
-
-static inline int ctz(limb_t a)
-{
- if (a == 0) {
- return LIMB_BITS;
- } else {
-#if LIMB_BITS == 64
- return ctz64(a);
-#else
- return ctz32(a);
-#endif
- }
-}
-
-static inline int ceil_log2(limb_t a)
-{
- if (a <= 1)
- return 0;
- else
- return LIMB_BITS - clz(a - 1);
-}
-
-/* b must be >= 1 */
-static inline slimb_t ceil_div(slimb_t a, slimb_t b)
-{
- if (a >= 0)
- return (a + b - 1) / b;
- else
- return a / b;
-}
-
-#ifdef USE_BF_DEC
-/* b must be >= 1 */
-static inline slimb_t floor_div(slimb_t a, slimb_t b)
-{
- if (a >= 0) {
- return a / b;
- } else {
- return (a - b + 1) / b;
- }
-}
-#endif
-
-/* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */
-static inline limb_t smod(slimb_t a, slimb_t b)
-{
- a = a % (slimb_t)b;
- if (a < 0)
- a += b;
- return a;
-}
-
-/* signed addition with saturation */
-static inline slimb_t sat_add(slimb_t a, slimb_t b)
-{
- slimb_t r;
- r = a + b;
- /* overflow ? */
- if (((a ^ r) & (b ^ r)) < 0)
- r = (a >> (LIMB_BITS - 1)) ^ (((limb_t)1 << (LIMB_BITS - 1)) - 1);
- return r;
-}
-
-static inline __maybe_unused limb_t shrd(limb_t low, limb_t high, long shift)
-{
- if (shift != 0)
- low = (low >> shift) | (high << (LIMB_BITS - shift));
- return low;
-}
-
-static inline __maybe_unused limb_t shld(limb_t a1, limb_t a0, long shift)
-{
- if (shift != 0)
- return (a1 << shift) | (a0 >> (LIMB_BITS - shift));
- else
- return a1;
-}
-
-#define malloc(s) malloc_is_forbidden(s)
-#define free(p) free_is_forbidden(p)
-#define realloc(p, s) realloc_is_forbidden(p, s)
-
-void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func,
- void *realloc_opaque)
-{
- memset(s, 0, sizeof(*s));
- s->realloc_func = realloc_func;
- s->realloc_opaque = realloc_opaque;
-}
-
-void bf_context_end(bf_context_t *s)
-{
- bf_clear_cache(s);
-}
-
-void bf_init(bf_context_t *s, bf_t *r)
-{
- r->ctx = s;
- r->sign = 0;
- r->expn = BF_EXP_ZERO;
- r->len = 0;
- r->tab = NULL;
-}
-
-/* return 0 if OK, -1 if alloc error */
-int bf_resize(bf_t *r, limb_t len)
-{
- limb_t *tab;
-
- if (len != r->len) {
- tab = bf_realloc(r->ctx, r->tab, len * sizeof(limb_t));
- if (!tab && len != 0)
- return -1;
- r->tab = tab;
- r->len = len;
- }
- return 0;
-}
-
-/* return 0 or BF_ST_MEM_ERROR */
-int bf_set_ui(bf_t *r, uint64_t a)
-{
- r->sign = 0;
- if (a == 0) {
- r->expn = BF_EXP_ZERO;
- bf_resize(r, 0); /* cannot fail */
- }
-#if LIMB_BITS == 32
- else if (a <= 0xffffffff)
-#else
- else
-#endif
- {
- int shift;
- if (bf_resize(r, 1))
- goto fail;
- shift = clz(a);
- r->tab[0] = a << shift;
- r->expn = LIMB_BITS - shift;
- }
-#if LIMB_BITS == 32
- else {
- uint32_t a1, a0;
- int shift;
- if (bf_resize(r, 2))
- goto fail;
- a0 = a;
- a1 = a >> 32;
- shift = clz(a1);
- r->tab[0] = a0 << shift;
- r->tab[1] = shld(a1, a0, shift);
- r->expn = 2 * LIMB_BITS - shift;
- }
-#endif
- return 0;
- fail:
- bf_set_nan(r);
- return BF_ST_MEM_ERROR;
-}
-
-/* return 0 or BF_ST_MEM_ERROR */
-int bf_set_si(bf_t *r, int64_t a)
-{
- int ret;
-
- if (a < 0) {
- ret = bf_set_ui(r, -a);
- r->sign = 1;
- } else {
- ret = bf_set_ui(r, a);
- }
- return ret;
-}
-
-void bf_set_nan(bf_t *r)
-{
- bf_resize(r, 0); /* cannot fail */
- r->expn = BF_EXP_NAN;
- r->sign = 0;
-}
-
-void bf_set_zero(bf_t *r, int is_neg)
-{
- bf_resize(r, 0); /* cannot fail */
- r->expn = BF_EXP_ZERO;
- r->sign = is_neg;
-}
-
-void bf_set_inf(bf_t *r, int is_neg)
-{
- bf_resize(r, 0); /* cannot fail */
- r->expn = BF_EXP_INF;
- r->sign = is_neg;
-}
-
-/* return 0 or BF_ST_MEM_ERROR */
-int bf_set(bf_t *r, const bf_t *a)
-{
- if (r == a)
- return 0;
- if (bf_resize(r, a->len)) {
- bf_set_nan(r);
- return BF_ST_MEM_ERROR;
- }
- r->sign = a->sign;
- r->expn = a->expn;
- memcpy_no_ub(r->tab, a->tab, a->len * sizeof(limb_t));
- return 0;
-}
-
-/* equivalent to bf_set(r, a); bf_delete(a) */
-void bf_move(bf_t *r, bf_t *a)
-{
- bf_context_t *s = r->ctx;
- if (r == a)
- return;
- bf_free(s, r->tab);
- *r = *a;
-}
-
-static limb_t get_limbz(const bf_t *a, limb_t idx)
-{
- if (idx >= a->len)
- return 0;
- else
- return a->tab[idx];
-}
-
-/* get LIMB_BITS at bit position 'pos' in tab */
-static inline limb_t get_bits(const limb_t *tab, limb_t len, slimb_t pos)
-{
- limb_t i, a0, a1;
- int p;
-
- i = pos >> LIMB_LOG2_BITS;
- p = pos & (LIMB_BITS - 1);
- if (i < len)
- a0 = tab[i];
- else
- a0 = 0;
- if (p == 0) {
- return a0;
- } else {
- i++;
- if (i < len)
- a1 = tab[i];
- else
- a1 = 0;
- return (a0 >> p) | (a1 << (LIMB_BITS - p));
- }
-}
-
-static inline limb_t get_bit(const limb_t *tab, limb_t len, slimb_t pos)
-{
- slimb_t i;
- i = pos >> LIMB_LOG2_BITS;
- if (i < 0 || i >= len)
- return 0;
- return (tab[i] >> (pos & (LIMB_BITS - 1))) & 1;
-}
-
-static inline limb_t limb_mask(int start, int last)
-{
- limb_t v;
- int n;
- n = last - start + 1;
- if (n == LIMB_BITS)
- v = -1;
- else
- v = (((limb_t)1 << n) - 1) << start;
- return v;
-}
-
-static limb_t mp_scan_nz(const limb_t *tab, mp_size_t n)
-{
- mp_size_t i;
- for(i = 0; i < n; i++) {
- if (tab[i] != 0)
- return 1;
- }
- return 0;
-}
-
-/* return != 0 if one bit between 0 and bit_pos inclusive is not zero. */
-static inline limb_t scan_bit_nz(const bf_t *r, slimb_t bit_pos)
-{
- slimb_t pos;
- limb_t v;
-
- pos = bit_pos >> LIMB_LOG2_BITS;
- if (pos < 0)
- return 0;
- v = r->tab[pos] & limb_mask(0, bit_pos & (LIMB_BITS - 1));
- if (v != 0)
- return 1;
- pos--;
- while (pos >= 0) {
- if (r->tab[pos] != 0)
- return 1;
- pos--;
- }
- return 0;
-}
-
-/* return the addend for rounding. Note that prec can be <= 0 (for
- BF_FLAG_RADPNT_PREC) */
-static int bf_get_rnd_add(int *pret, const bf_t *r, limb_t l,
- slimb_t prec, int rnd_mode)
-{
- int add_one, inexact;
- limb_t bit1, bit0;
-
- if (rnd_mode == BF_RNDF) {
- bit0 = 1; /* faithful rounding does not honor the INEXACT flag */
- } else {
- /* starting limb for bit 'prec + 1' */
- bit0 = scan_bit_nz(r, l * LIMB_BITS - 1 - bf_max(0, prec + 1));
- }
-
- /* get the bit at 'prec' */
- bit1 = get_bit(r->tab, l, l * LIMB_BITS - 1 - prec);
- inexact = (bit1 | bit0) != 0;
-
- add_one = 0;
- switch(rnd_mode) {
- case BF_RNDZ:
- break;
- case BF_RNDN:
- if (bit1) {
- if (bit0) {
- add_one = 1;
- } else {
- /* round to even */
- add_one =
- get_bit(r->tab, l, l * LIMB_BITS - 1 - (prec - 1));
- }
- }
- break;
- case BF_RNDD:
- case BF_RNDU:
- if (r->sign == (rnd_mode == BF_RNDD))
- add_one = inexact;
- break;
- case BF_RNDA:
- add_one = inexact;
- break;
- case BF_RNDNA:
- case BF_RNDF:
- add_one = bit1;
- break;
- default:
- abort();
- }
-
- if (inexact)
- *pret |= BF_ST_INEXACT;
- return add_one;
-}
-
-static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags)
-{
- slimb_t i, l, e_max;
- int rnd_mode;
-
- rnd_mode = flags & BF_RND_MASK;
- if (prec == BF_PREC_INF ||
- rnd_mode == BF_RNDN ||
- rnd_mode == BF_RNDNA ||
- rnd_mode == BF_RNDA ||
- (rnd_mode == BF_RNDD && sign == 1) ||
- (rnd_mode == BF_RNDU && sign == 0)) {
- bf_set_inf(r, sign);
- } else {
- /* set to maximum finite number */
- l = (prec + LIMB_BITS - 1) / LIMB_BITS;
- if (bf_resize(r, l)) {
- bf_set_nan(r);
- return BF_ST_MEM_ERROR;
- }
- r->tab[0] = limb_mask((-prec) & (LIMB_BITS - 1),
- LIMB_BITS - 1);
- for(i = 1; i < l; i++)
- r->tab[i] = (limb_t)-1;
- e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1);
- r->expn = e_max;
- r->sign = sign;
- }
- return BF_ST_OVERFLOW | BF_ST_INEXACT;
-}
-
-/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is
- assumed to have length 'l' (1 <= l <= r->len). Note: 'prec1' can be
- infinite (BF_PREC_INF). 'ret' is 0 or BF_ST_INEXACT if the result
- is known to be inexact. Can fail with BF_ST_MEM_ERROR in case of
- overflow not returning infinity. */
-static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l,
- int ret)
-{
- limb_t v, a;
- int shift, add_one, rnd_mode;
- slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec;
-
- /* e_min and e_max are computed to match the IEEE 754 conventions */
- e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1);
- e_min = -e_range + 3;
- e_max = e_range;
-
- if (flags & BF_FLAG_RADPNT_PREC) {
- /* 'prec' is the precision after the radix point */
- if (prec1 != BF_PREC_INF)
- prec = r->expn + prec1;
- else
- prec = prec1;
- } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) {
- /* restrict the precision in case of potentially subnormal
- result */
- assert(prec1 != BF_PREC_INF);
- prec = prec1 - (e_min - r->expn);
- } else {
- prec = prec1;
- }
-
- /* round to prec bits */
- rnd_mode = flags & BF_RND_MASK;
- add_one = bf_get_rnd_add(&ret, r, l, prec, rnd_mode);
-
- if (prec <= 0) {
- if (add_one) {
- bf_resize(r, 1); /* cannot fail */
- r->tab[0] = (limb_t)1 << (LIMB_BITS - 1);
- r->expn += 1 - prec;
- ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT;
- return ret;
- } else {
- goto underflow;
- }
- } else if (add_one) {
- limb_t carry;
-
- /* add one starting at digit 'prec - 1' */
- bit_pos = l * LIMB_BITS - 1 - (prec - 1);
- pos = bit_pos >> LIMB_LOG2_BITS;
- carry = (limb_t)1 << (bit_pos & (LIMB_BITS - 1));
-
- for(i = pos; i < l; i++) {
- v = r->tab[i] + carry;
- carry = (v < carry);
- r->tab[i] = v;
- if (carry == 0)
- break;
- }
- if (carry) {
- /* shift right by one digit */
- v = 1;
- for(i = l - 1; i >= pos; i--) {
- a = r->tab[i];
- r->tab[i] = (a >> 1) | (v << (LIMB_BITS - 1));
- v = a;
- }
- r->expn++;
- }
- }
-
- /* check underflow */
- if (unlikely(r->expn < e_min)) {
- if (flags & BF_FLAG_SUBNORMAL) {
- /* if inexact, also set the underflow flag */
- if (ret & BF_ST_INEXACT)
- ret |= BF_ST_UNDERFLOW;
- } else {
- underflow:
- ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT;
- bf_set_zero(r, r->sign);
- return ret;
- }
- }
-
- /* check overflow */
- if (unlikely(r->expn > e_max))
- return bf_set_overflow(r, r->sign, prec1, flags);
-
- /* keep the bits starting at 'prec - 1' */
- bit_pos = l * LIMB_BITS - 1 - (prec - 1);
- i = bit_pos >> LIMB_LOG2_BITS;
- if (i >= 0) {
- shift = bit_pos & (LIMB_BITS - 1);
- if (shift != 0)
- r->tab[i] &= limb_mask(shift, LIMB_BITS - 1);
- } else {
- i = 0;
- }
- /* remove trailing zeros */
- while (r->tab[i] == 0)
- i++;
- if (i > 0) {
- l -= i;
- memmove(r->tab, r->tab + i, l * sizeof(limb_t));
- }
- bf_resize(r, l); /* cannot fail */
- return ret;
-}
-
-/* 'r' must be a finite number. */
-int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags)
-{
- limb_t l, v, a;
- int shift, ret;
- slimb_t i;
-
- // bf_print_str("bf_renorm", r);
- l = r->len;
- while (l > 0 && r->tab[l - 1] == 0)
- l--;
- if (l == 0) {
- /* zero */
- r->expn = BF_EXP_ZERO;
- bf_resize(r, 0); /* cannot fail */
- ret = 0;
- } else {
- r->expn -= (r->len - l) * LIMB_BITS;
- /* shift to have the MSB set to '1' */
- v = r->tab[l - 1];
- shift = clz(v);
- if (shift != 0) {
- v = 0;
- for(i = 0; i < l; i++) {
- a = r->tab[i];
- r->tab[i] = (a << shift) | (v >> (LIMB_BITS - shift));
- v = a;
- }
- r->expn -= shift;
- }
- ret = __bf_round(r, prec1, flags, l, 0);
- }
- // bf_print_str("r_final", r);
- return ret;
-}
-
-/* return true if rounding can be done at precision 'prec' assuming
- the exact result r is such that |r-a| <= 2^(EXP(a)-k). */
-/* XXX: check the case where the exponent would be incremented by the
- rounding */
-int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k)
-{
- BOOL is_rndn;
- slimb_t bit_pos, n;
- limb_t bit;
-
- if (a->expn == BF_EXP_INF || a->expn == BF_EXP_NAN)
- return FALSE;
- if (rnd_mode == BF_RNDF) {
- return (k >= (prec + 1));
- }
- if (a->expn == BF_EXP_ZERO)
- return FALSE;
- is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA);
- if (k < (prec + 2))
- return FALSE;
- bit_pos = a->len * LIMB_BITS - 1 - prec;
- n = k - prec;
- /* bit pattern for RNDN or RNDNA: 0111.. or 1000...
- for other rounding modes: 000... or 111...
- */
- bit = get_bit(a->tab, a->len, bit_pos);
- bit_pos--;
- n--;
- bit ^= is_rndn;
- /* XXX: slow, but a few iterations on average */
- while (n != 0) {
- if (get_bit(a->tab, a->len, bit_pos) != bit)
- return TRUE;
- bit_pos--;
- n--;
- }
- return FALSE;
-}
-
-/* Cannot fail with BF_ST_MEM_ERROR. */
-int bf_round(bf_t *r, limb_t prec, bf_flags_t flags)
-{
- if (r->len == 0)
- return 0;
- return __bf_round(r, prec, flags, r->len, 0);
-}
-
-/* for debugging */
-static __maybe_unused void dump_limbs(const char *str, const limb_t *tab, limb_t n)
-{
- limb_t i;
- printf("%s: len=%" PRId_LIMB "\n", str, n);
- for(i = 0; i < n; i++) {
- printf("%" PRId_LIMB ": " FMT_LIMB "\n",
- i, tab[i]);
- }
-}
-
-void mp_print_str(const char *str, const limb_t *tab, limb_t n)
-{
- slimb_t i;
- printf("%s= 0x", str);
- for(i = n - 1; i >= 0; i--) {
- if (i != (n - 1))
- printf("_");
- printf(FMT_LIMB, tab[i]);
- }
- printf("\n");
-}
-
-static __maybe_unused void mp_print_str_h(const char *str,
- const limb_t *tab, limb_t n,
- limb_t high)
-{
- slimb_t i;
- printf("%s= 0x", str);
- printf(FMT_LIMB, high);
- for(i = n - 1; i >= 0; i--) {
- printf("_");
- printf(FMT_LIMB, tab[i]);
- }
- printf("\n");
-}
-
-/* for debugging */
-void bf_print_str(const char *str, const bf_t *a)
-{
- slimb_t i;
- printf("%s=", str);
-
- if (a->expn == BF_EXP_NAN) {
- printf("NaN");
- } else {
- if (a->sign)
- putchar('-');
- if (a->expn == BF_EXP_ZERO) {
- putchar('0');
- } else if (a->expn == BF_EXP_INF) {
- printf("Inf");
- } else {
- printf("0x0.");
- for(i = a->len - 1; i >= 0; i--)
- printf(FMT_LIMB, a->tab[i]);
- printf("p%" PRId_LIMB, a->expn);
- }
- }
- printf("\n");
-}
-
-/* compare the absolute value of 'a' and 'b'. Return < 0 if a < b, 0
- if a = b and > 0 otherwise. */
-int bf_cmpu(const bf_t *a, const bf_t *b)
-{
- slimb_t i;
- limb_t len, v1, v2;
-
- if (a->expn != b->expn) {
- if (a->expn < b->expn)
- return -1;
- else
- return 1;
- }
- len = bf_max(a->len, b->len);
- for(i = len - 1; i >= 0; i--) {
- v1 = get_limbz(a, a->len - len + i);
- v2 = get_limbz(b, b->len - len + i);
- if (v1 != v2) {
- if (v1 < v2)
- return -1;
- else
- return 1;
- }
- }
- return 0;
-}
-
-/* Full order: -0 < 0, NaN == NaN and NaN is larger than all other numbers */
-int bf_cmp_full(const bf_t *a, const bf_t *b)
-{
- int res;
-
- if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
- if (a->expn == b->expn)
- res = 0;
- else if (a->expn == BF_EXP_NAN)
- res = 1;
- else
- res = -1;
- } else if (a->sign != b->sign) {
- res = 1 - 2 * a->sign;
- } else {
- res = bf_cmpu(a, b);
- if (a->sign)
- res = -res;
- }
- return res;
-}
-
-/* Standard floating point comparison: return 2 if one of the operands
- is NaN (unordered) or -1, 0, 1 depending on the ordering assuming
- -0 == +0 */
-int bf_cmp(const bf_t *a, const bf_t *b)
-{
- int res;
-
- if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
- res = 2;
- } else if (a->sign != b->sign) {
- if (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO)
- res = 0;
- else
- res = 1 - 2 * a->sign;
- } else {
- res = bf_cmpu(a, b);
- if (a->sign)
- res = -res;
- }
- return res;
-}
-
-/* Compute the number of bits 'n' matching the pattern:
- a= X1000..0
- b= X0111..1
-
- When computing a-b, the result will have at least n leading zero
- bits.
-
- Precondition: a > b and a.expn - b.expn = 0 or 1
-*/
-static limb_t count_cancelled_bits(const bf_t *a, const bf_t *b)
-{
- slimb_t bit_offset, b_offset, n;
- int p, p1;
- limb_t v1, v2, mask;
-
- bit_offset = a->len * LIMB_BITS - 1;
- b_offset = (b->len - a->len) * LIMB_BITS - (LIMB_BITS - 1) +
- a->expn - b->expn;
- n = 0;
-
- /* first search the equals bits */
- for(;;) {
- v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS);
- v2 = get_bits(b->tab, b->len, bit_offset + b_offset);
- // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2);
- if (v1 != v2)
- break;
- n += LIMB_BITS;
- bit_offset -= LIMB_BITS;
- }
- /* find the position of the first different bit */
- p = clz(v1 ^ v2) + 1;
- n += p;
- /* then search for '0' in a and '1' in b */
- p = LIMB_BITS - p;
- if (p > 0) {
- /* search in the trailing p bits of v1 and v2 */
- mask = limb_mask(0, p - 1);
- p1 = bf_min(clz(v1 & mask), clz((~v2) & mask)) - (LIMB_BITS - p);
- n += p1;
- if (p1 != p)
- goto done;
- }
- bit_offset -= LIMB_BITS;
- for(;;) {
- v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS);
- v2 = get_bits(b->tab, b->len, bit_offset + b_offset);
- // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2);
- if (v1 != 0 || v2 != -1) {
- /* different: count the matching bits */
- p1 = bf_min(clz(v1), clz(~v2));
- n += p1;
- break;
- }
- n += LIMB_BITS;
- bit_offset -= LIMB_BITS;
- }
- done:
- return n;
-}
-
-static int bf_add_internal(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
- bf_flags_t flags, int b_neg)
-{
- const bf_t *tmp;
- int is_sub, ret, cmp_res, a_sign, b_sign;
-
- a_sign = a->sign;
- b_sign = b->sign ^ b_neg;
- is_sub = a_sign ^ b_sign;
- cmp_res = bf_cmpu(a, b);
- if (cmp_res < 0) {
- tmp = a;
- a = b;
- b = tmp;
- a_sign = b_sign; /* b_sign is never used later */
- }
- /* abs(a) >= abs(b) */
- if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) {
- /* zero result */
- bf_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD);
- ret = 0;
- } else if (a->len == 0 || b->len == 0) {
- ret = 0;
- if (a->expn >= BF_EXP_INF) {
- if (a->expn == BF_EXP_NAN) {
- /* at least one operand is NaN */
- bf_set_nan(r);
- } else if (b->expn == BF_EXP_INF && is_sub) {
- /* infinities with different signs */
- bf_set_nan(r);
- ret = BF_ST_INVALID_OP;
- } else {
- bf_set_inf(r, a_sign);
- }
- } else {
- /* at least one zero and not subtract */
- bf_set(r, a);
- r->sign = a_sign;
- goto renorm;
- }
- } else {
- slimb_t d, a_offset, b_bit_offset, i, cancelled_bits;
- limb_t carry, v1, v2, u, r_len, carry1, precl, tot_len, z, sub_mask;
-
- r->sign = a_sign;
- r->expn = a->expn;
- d = a->expn - b->expn;
- /* must add more precision for the leading cancelled bits in
- subtraction */
- if (is_sub) {
- if (d <= 1)
- cancelled_bits = count_cancelled_bits(a, b);
- else
- cancelled_bits = 1;
- } else {
- cancelled_bits = 0;
- }
-
- /* add two extra bits for rounding */
- precl = (cancelled_bits + prec + 2 + LIMB_BITS - 1) / LIMB_BITS;
- tot_len = bf_max(a->len, b->len + (d + LIMB_BITS - 1) / LIMB_BITS);
- r_len = bf_min(precl, tot_len);
- if (bf_resize(r, r_len))
- goto fail;
- a_offset = a->len - r_len;
- b_bit_offset = (b->len - r_len) * LIMB_BITS + d;
-
- /* compute the bits before for the rounding */
- carry = is_sub;
- z = 0;
- sub_mask = -is_sub;
- i = r_len - tot_len;
- while (i < 0) {
- slimb_t ap, bp;
- BOOL inflag;
-
- ap = a_offset + i;
- bp = b_bit_offset + i * LIMB_BITS;
- inflag = FALSE;
- if (ap >= 0 && ap < a->len) {
- v1 = a->tab[ap];
- inflag = TRUE;
- } else {
- v1 = 0;
- }
- if (bp + LIMB_BITS > 0 && bp < (slimb_t)(b->len * LIMB_BITS)) {
- v2 = get_bits(b->tab, b->len, bp);
- inflag = TRUE;
- } else {
- v2 = 0;
- }
- if (!inflag) {
- /* outside 'a' and 'b': go directly to the next value
- inside a or b so that the running time does not
- depend on the exponent difference */
- i = 0;
- if (ap < 0)
- i = bf_min(i, -a_offset);
- /* b_bit_offset + i * LIMB_BITS + LIMB_BITS >= 1
- equivalent to
- i >= ceil(-b_bit_offset + 1 - LIMB_BITS) / LIMB_BITS)
- */
- if (bp + LIMB_BITS <= 0)
- i = bf_min(i, (-b_bit_offset) >> LIMB_LOG2_BITS);
- } else {
- i++;
- }
- v2 ^= sub_mask;
- u = v1 + v2;
- carry1 = u < v1;
- u += carry;
- carry = (u < carry) | carry1;
- z |= u;
- }
- /* and the result */
- for(i = 0; i < r_len; i++) {
- v1 = get_limbz(a, a_offset + i);
- v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS);
- v2 ^= sub_mask;
- u = v1 + v2;
- carry1 = u < v1;
- u += carry;
- carry = (u < carry) | carry1;
- r->tab[i] = u;
- }
- /* set the extra bits for the rounding */
- r->tab[0] |= (z != 0);
-
- /* carry is only possible in add case */
- if (!is_sub && carry) {
- if (bf_resize(r, r_len + 1))
- goto fail;
- r->tab[r_len] = 1;
- r->expn += LIMB_BITS;
- }
- renorm:
- ret = bf_normalize_and_round(r, prec, flags);
- }
- return ret;
- fail:
- bf_set_nan(r);
- return BF_ST_MEM_ERROR;
-}
-
-static int __bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
- bf_flags_t flags)
-{
- return bf_add_internal(r, a, b, prec, flags, 0);
-}
-
-static int __bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
- bf_flags_t flags)
-{
- return bf_add_internal(r, a, b, prec, flags, 1);
-}
-
-limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2,
- limb_t n, limb_t carry)
-{
- slimb_t i;
- limb_t k, a, v, k1;
-
- k = carry;
- for(i=0;i<n;i++) {
- v = op1[i];
- a = v + op2[i];
- k1 = a < v;
- a = a + k;
- k = (a < k) | k1;
- res[i] = a;
- }
- return k;
-}
-
-limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n)
-{
- size_t i;
- limb_t k, a;
-
- k=b;
- for(i=0;i<n;i++) {
- if (k == 0)
- break;
- a = tab[i] + k;
- k = (a < k);
- tab[i] = a;
- }
- return k;
-}
-
-limb_t mp_sub(limb_t *res, const limb_t *op1, const limb_t *op2,
- mp_size_t n, limb_t carry)
-{
- int i;
- limb_t k, a, v, k1;
-
- k = carry;
- for(i=0;i<n;i++) {
- v = op1[i];
- a = v - op2[i];
- k1 = a > v;
- v = a - k;
- k = (v > a) | k1;
- res[i] = v;
- }
- return k;
-}
-
-/* compute 0 - op2 */
-static limb_t mp_neg(limb_t *res, const limb_t *op2, mp_size_t n, limb_t carry)
-{
- int i;
- limb_t k, a, v, k1;
-
- k = carry;
- for(i=0;i<n;i++) {
- v = 0;
- a = v - op2[i];
- k1 = a > v;
- v = a - k;
- k = (v > a) | k1;
- res[i] = v;
- }
- return k;
-}
-
-limb_t mp_sub_ui(limb_t *tab, limb_t b, mp_size_t n)
-{
- mp_size_t i;
- limb_t k, a, v;
-
- k=b;
- for(i=0;i<n;i++) {
- v = tab[i];
- a = v - k;
- k = a > v;
- tab[i] = a;
- if (k == 0)
- break;
- }
- return k;
-}
-
-/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift).
- 1 <= shift <= LIMB_BITS - 1 */
-static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n,
- int shift, limb_t high)
-{
- mp_size_t i;
- limb_t l, a;
-
- assert(shift >= 1 && shift < LIMB_BITS);
- l = high;
- for(i = n - 1; i >= 0; i--) {
- a = tab[i];
- tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift));
- l = a;
- }
- return l & (((limb_t)1 << shift) - 1);
-}
-
-/* tabr[] = taba[] * b + l. Return the high carry */
-static limb_t mp_mul1(limb_t *tabr, const limb_t *taba, limb_t n,
- limb_t b, limb_t l)
-{
- limb_t i;
- dlimb_t t;
-
- for(i = 0; i < n; i++) {
- t = (dlimb_t)taba[i] * (dlimb_t)b + l;
- tabr[i] = t;
- l = t >> LIMB_BITS;
- }
- return l;
-}
-
-/* tabr[] += taba[] * b, return the high word. */
-static limb_t mp_add_mul1(limb_t *tabr, const limb_t *taba, limb_t n,
- limb_t b)
-{
- limb_t i, l;
- dlimb_t t;
-
- l = 0;
- for(i = 0; i < n; i++) {
- t = (dlimb_t)taba[i] * (dlimb_t)b + l + tabr[i];
- tabr[i] = t;
- l = t >> LIMB_BITS;
- }
- return l;
-}
-
-/* size of the result : op1_size + op2_size. */
-static void mp_mul_basecase(limb_t *result,
- const limb_t *op1, limb_t op1_size,
- const limb_t *op2, limb_t op2_size)
-{
- limb_t i, r;
-
- result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0);
- for(i=1;i<op2_size;i++) {
- r = mp_add_mul1(result + i, op1, op1_size, op2[i]);
- result[i + op1_size] = r;
- }
-}
-
-/* return 0 if OK, -1 if memory error */
-/* XXX: change API so that result can be allocated */
-int mp_mul(bf_context_t *s, limb_t *result,
- const limb_t *op1, limb_t op1_size,
- const limb_t *op2, limb_t op2_size)
-{
-#ifdef USE_FFT_MUL
- if (unlikely(bf_min(op1_size, op2_size) >= FFT_MUL_THRESHOLD)) {
- bf_t r_s, *r = &r_s;
- r->tab = result;
- /* XXX: optimize memory usage in API */
- if (fft_mul(s, r, (limb_t *)op1, op1_size,
- (limb_t *)op2, op2_size, FFT_MUL_R_NORESIZE))
- return -1;
- } else
-#endif
- {
- mp_mul_basecase(result, op1, op1_size, op2, op2_size);
- }
- return 0;
-}
-
-/* tabr[] -= taba[] * b. Return the value to substract to the high
- word. */
-static limb_t mp_sub_mul1(limb_t *tabr, const limb_t *taba, limb_t n,
- limb_t b)
-{
- limb_t i, l;
- dlimb_t t;
-
- l = 0;
- for(i = 0; i < n; i++) {
- t = tabr[i] - (dlimb_t)taba[i] * (dlimb_t)b - l;
- tabr[i] = t;
- l = -(t >> LIMB_BITS);
- }
- return l;
-}
-
-/* WARNING: d must be >= 2^(LIMB_BITS-1) */
-static inline limb_t udiv1norm_init(limb_t d)
-{
- limb_t a0, a1;
- a1 = -d - 1;
- a0 = -1;
- return (((dlimb_t)a1 << LIMB_BITS) | a0) / d;
-}
-
-/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0
- / d' with 0 <= a1 < d. */
-static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0,
- limb_t d, limb_t d_inv)
-{
- limb_t n1m, n_adj, q, r, ah;
- dlimb_t a;
- n1m = ((slimb_t)a0 >> (LIMB_BITS - 1));
- n_adj = a0 + (n1m & d);
- a = (dlimb_t)d_inv * (a1 - n1m) + n_adj;
- q = (a >> LIMB_BITS) + a1;
- /* compute a - q * r and update q so that the remainder is\
- between 0 and d - 1 */
- a = ((dlimb_t)a1 << LIMB_BITS) | a0;
- a = a - (dlimb_t)q * d - d;
- ah = a >> LIMB_BITS;
- q += 1 + ah;
- r = (limb_t)a + (ah & d);
- *pr = r;
- return q;
-}
-
-/* b must be >= 1 << (LIMB_BITS - 1) */
-static limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n,
- limb_t b, limb_t r)
-{
- slimb_t i;
-
- if (n >= UDIV1NORM_THRESHOLD) {
- limb_t b_inv;
- b_inv = udiv1norm_init(b);
- for(i = n - 1; i >= 0; i--) {
- tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv);
- }
- } else {
- dlimb_t a1;
- for(i = n - 1; i >= 0; i--) {
- a1 = ((dlimb_t)r << LIMB_BITS) | taba[i];
- tabr[i] = a1 / b;
- r = a1 % b;
- }
- }
- return r;
-}
-
-static int mp_divnorm_large(bf_context_t *s,
- limb_t *tabq, limb_t *taba, limb_t na,
- const limb_t *tabb, limb_t nb);
-
-/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb
- - 1] must be >= 1 << (LIMB_BITS - 1). na - nb must be >= 0. 'taba'
- is modified and contains the remainder (nb limbs). tabq[0..na-nb]
- contains the quotient with tabq[na - nb] <= 1. */
-static int mp_divnorm(bf_context_t *s, limb_t *tabq, limb_t *taba, limb_t na,
- const limb_t *tabb, limb_t nb)
-{
- limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r;
- slimb_t i, j;
-
- b1 = tabb[nb - 1];
- if (nb == 1) {
- taba[0] = mp_div1norm(tabq, taba, na, b1, 0);
- return 0;
- }
- n = na - nb;
- if (bf_min(n, nb) >= DIVNORM_LARGE_THRESHOLD) {
- return mp_divnorm_large(s, tabq, taba, na, tabb, nb);
- }
-
- if (n >= UDIV1NORM_THRESHOLD)
- b1_inv = udiv1norm_init(b1);
- else
- b1_inv = 0;
-
- /* first iteration: the quotient is only 0 or 1 */
- q = 1;
- for(j = nb - 1; j >= 0; j--) {
- if (taba[n + j] != tabb[j]) {
- if (taba[n + j] < tabb[j])
- q = 0;
- break;
- }
- }
- tabq[n] = q;
- if (q) {
- mp_sub(taba + n, taba + n, tabb, nb, 0);
- }
-
- for(i = n - 1; i >= 0; i--) {
- if (unlikely(taba[i + nb] >= b1)) {
- q = -1;
- } else if (b1_inv) {
- q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv);
- } else {
- dlimb_t al;
- al = ((dlimb_t)taba[i + nb] << LIMB_BITS) | taba[i + nb - 1];
- q = al / b1;
- r = al % b1;
- }
- r = mp_sub_mul1(taba + i, tabb, nb, q);
-
- v = taba[i + nb];
- a = v - r;
- c = (a > v);
- taba[i + nb] = a;
-
- if (c != 0) {
- /* negative result */
- for(;;) {
- q--;
- c = mp_add(taba + i, taba + i, tabb, nb, 0);
- /* propagate carry and test if positive result */
- if (c != 0) {
- if (++taba[i + nb] == 0) {
- break;
- }
- }
- }
- }
- tabq[i] = q;
- }
- return 0;
-}
-
-/* compute r=B^(2*n)/a such as a*r < B^(2*n) < a*r + 2 with n >= 1. 'a'
- has n limbs with a[n-1] >= B/2 and 'r' has n+1 limbs with r[n] = 1.
-
- See Modern Computer Arithmetic by Richard P. Brent and Paul
- Zimmermann, algorithm 3.5 */
-int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n)
-{
- mp_size_t l, h, k, i;
- limb_t *tabxh, *tabt, c, *tabu;
-
- if (n <= 2) {
- /* return ceil(B^(2*n)/a) - 1 */
- /* XXX: could avoid allocation */
- tabu = bf_malloc(s, sizeof(limb_t) * (2 * n + 1));
- tabt = bf_malloc(s, sizeof(limb_t) * (n + 2));
- if (!tabt || !tabu)
- goto fail;
- for(i = 0; i < 2 * n; i++)
- tabu[i] = 0;
- tabu[2 * n] = 1;
- if (mp_divnorm(s, tabt, tabu, 2 * n + 1, taba, n))
- goto fail;
- for(i = 0; i < n + 1; i++)
- tabr[i] = tabt[i];
- if (mp_scan_nz(tabu, n) == 0) {
- /* only happens for a=B^n/2 */
- mp_sub_ui(tabr, 1, n + 1);
- }
- } else {
- l = (n - 1) / 2;
- h = n - l;
- /* n=2p -> l=p-1, h = p + 1, k = p + 3
- n=2p+1-> l=p, h = p + 1; k = p + 2
- */
- tabt = bf_malloc(s, sizeof(limb_t) * (n + h + 1));
- tabu = bf_malloc(s, sizeof(limb_t) * (n + 2 * h - l + 2));
- if (!tabt || !tabu)
- goto fail;
- tabxh = tabr + l;
- if (mp_recip(s, tabxh, taba + l, h))
- goto fail;
- if (mp_mul(s, tabt, taba, n, tabxh, h + 1)) /* n + h + 1 limbs */
- goto fail;
- while (tabt[n + h] != 0) {
- mp_sub_ui(tabxh, 1, h + 1);
- c = mp_sub(tabt, tabt, taba, n, 0);
- mp_sub_ui(tabt + n, c, h + 1);
- }
- /* T = B^(n+h) - T */
- mp_neg(tabt, tabt, n + h + 1, 0);
- tabt[n + h]++;
- if (mp_mul(s, tabu, tabt + l, n + h + 1 - l, tabxh, h + 1))
- goto fail;
- /* n + 2*h - l + 2 limbs */
- k = 2 * h - l;
- for(i = 0; i < l; i++)
- tabr[i] = tabu[i + k];
- mp_add(tabr + l, tabr + l, tabu + 2 * h, h, 0);
- }
- bf_free(s, tabt);
- bf_free(s, tabu);
- return 0;
- fail:
- bf_free(s, tabt);
- bf_free(s, tabu);
- return -1;
-}
-
-/* return -1, 0 or 1 */
-static int mp_cmp(const limb_t *taba, const limb_t *tabb, mp_size_t n)
-{
- mp_size_t i;
- for(i = n - 1; i >= 0; i--) {
- if (taba[i] != tabb[i]) {
- if (taba[i] < tabb[i])
- return -1;
- else
- return 1;
- }
- }
- return 0;
-}
-
-//#define DEBUG_DIVNORM_LARGE
-//#define DEBUG_DIVNORM_LARGE2
-
-/* subquadratic divnorm */
-static int mp_divnorm_large(bf_context_t *s,
- limb_t *tabq, limb_t *taba, limb_t na,
- const limb_t *tabb, limb_t nb)
-{
- limb_t *tabb_inv, nq, *tabt, i, n;
- nq = na - nb;
-#ifdef DEBUG_DIVNORM_LARGE
- printf("na=%d nb=%d nq=%d\n", (int)na, (int)nb, (int)nq);
- mp_print_str("a", taba, na);
- mp_print_str("b", tabb, nb);
-#endif
- assert(nq >= 1);
- n = nq;
- if (nq < nb)
- n++;
- tabb_inv = bf_malloc(s, sizeof(limb_t) * (n + 1));
- tabt = bf_malloc(s, sizeof(limb_t) * 2 * (n + 1));
- if (!tabb_inv || !tabt)
- goto fail;
-
- if (n >= nb) {
- for(i = 0; i < n - nb; i++)
- tabt[i] = 0;
- for(i = 0; i < nb; i++)
- tabt[i + n - nb] = tabb[i];
- } else {
- /* truncate B: need to increment it so that the approximate
- inverse is smaller that the exact inverse */
- for(i = 0; i < n; i++)
- tabt[i] = tabb[i + nb - n];
- if (mp_add_ui(tabt, 1, n)) {
- /* tabt = B^n : tabb_inv = B^n */
- memset(tabb_inv, 0, n * sizeof(limb_t));
- tabb_inv[n] = 1;
- goto recip_done;
- }
- }
- if (mp_recip(s, tabb_inv, tabt, n))
- goto fail;
- recip_done:
- /* Q=A*B^-1 */
- if (mp_mul(s, tabt, tabb_inv, n + 1, taba + na - (n + 1), n + 1))
- goto fail;
-
- for(i = 0; i < nq + 1; i++)
- tabq[i] = tabt[i + 2 * (n + 1) - (nq + 1)];
-#ifdef DEBUG_DIVNORM_LARGE
- mp_print_str("q", tabq, nq + 1);
-#endif
-
- bf_free(s, tabt);
- bf_free(s, tabb_inv);
- tabb_inv = NULL;
-
- /* R=A-B*Q */
- tabt = bf_malloc(s, sizeof(limb_t) * (na + 1));
- if (!tabt)
- goto fail;
- if (mp_mul(s, tabt, tabq, nq + 1, tabb, nb))
- goto fail;
- /* we add one more limb for the result */
- mp_sub(taba, taba, tabt, nb + 1, 0);
- bf_free(s, tabt);
- /* the approximated quotient is smaller than than the exact one,
- hence we may have to increment it */
-#ifdef DEBUG_DIVNORM_LARGE2
- int cnt = 0;
- static int cnt_max;
-#endif
- for(;;) {
- if (taba[nb] == 0 && mp_cmp(taba, tabb, nb) < 0)
- break;
- taba[nb] -= mp_sub(taba, taba, tabb, nb, 0);
- mp_add_ui(tabq, 1, nq + 1);
-#ifdef DEBUG_DIVNORM_LARGE2
- cnt++;
-#endif
- }
-#ifdef DEBUG_DIVNORM_LARGE2
- if (cnt > cnt_max) {
- cnt_max = cnt;
- printf("\ncnt=%d nq=%d nb=%d\n", cnt_max, (int)nq, (int)nb);
- }
-#endif
- return 0;
- fail:
- bf_free(s, tabb_inv);
- bf_free(s, tabt);
- return -1;
-}
-
-int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
- bf_flags_t flags)
-{
- int ret, r_sign;
-
- if (a->len < b->len) {
- const bf_t *tmp = a;
- a = b;
- b = tmp;
- }
- r_sign = a->sign ^ b->sign;
- /* here b->len <= a->len */
- if (b->len == 0) {
- if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- ret = 0;
- } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) {
- if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) ||
- (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) {
- bf_set_nan(r);
- ret = BF_ST_INVALID_OP;
- } else {
- bf_set_inf(r, r_sign);
- ret = 0;
- }
- } else {
- bf_set_zero(r, r_sign);
- ret = 0;
- }
- } else {
- bf_t tmp, *r1 = NULL;
- limb_t a_len, b_len, precl;
- limb_t *a_tab, *b_tab;
-
- a_len = a->len;
- b_len = b->len;
-
- if ((flags & BF_RND_MASK) == BF_RNDF) {
- /* faithful rounding does not require using the full inputs */
- precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS;
- a_len = bf_min(a_len, precl);
- b_len = bf_min(b_len, precl);
- }
- a_tab = a->tab + a->len - a_len;
- b_tab = b->tab + b->len - b_len;
-
-#ifdef USE_FFT_MUL
- if (b_len >= FFT_MUL_THRESHOLD) {
- int mul_flags = 0;
- if (r == a)
- mul_flags |= FFT_MUL_R_OVERLAP_A;
- if (r == b)
- mul_flags |= FFT_MUL_R_OVERLAP_B;
- if (fft_mul(r->ctx, r, a_tab, a_len, b_tab, b_len, mul_flags))
- goto fail;
- } else
-#endif
- {
- if (r == a || r == b) {
- bf_init(r->ctx, &tmp);
- r1 = r;
- r = &tmp;
- }
- if (bf_resize(r, a_len + b_len)) {
-#ifdef USE_FFT_MUL
- fail:
-#endif
- bf_set_nan(r);
- ret = BF_ST_MEM_ERROR;
- goto done;
- }
- mp_mul_basecase(r->tab, a_tab, a_len, b_tab, b_len);
- }
- r->sign = r_sign;
- r->expn = a->expn + b->expn;
- ret = bf_normalize_and_round(r, prec, flags);
- done:
- if (r == &tmp)
- bf_move(r1, &tmp);
- }
- return ret;
-}
-
-/* multiply 'r' by 2^e */
-int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags)
-{
- slimb_t e_max;
- if (r->len == 0)
- return 0;
- e_max = ((limb_t)1 << BF_EXT_EXP_BITS_MAX) - 1;
- e = bf_max(e, -e_max);
- e = bf_min(e, e_max);
- r->expn += e;
- return __bf_round(r, prec, flags, r->len, 0);
-}
-
-/* Return e such as a=m*2^e with m odd integer. return 0 if a is zero,
- Infinite or Nan. */
-slimb_t bf_get_exp_min(const bf_t *a)
-{
- slimb_t i;
- limb_t v;
- int k;
-
- for(i = 0; i < a->len; i++) {
- v = a->tab[i];
- if (v != 0) {
- k = ctz(v);
- return a->expn - (a->len - i) * LIMB_BITS + k;
- }
- }
- return 0;
-}
-
-/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the
- integer defined as floor(a/b) and r = a - q * b. */
-static void bf_tdivremu(bf_t *q, bf_t *r,
- const bf_t *a, const bf_t *b)
-{
- if (bf_cmpu(a, b) < 0) {
- bf_set_ui(q, 0);
- bf_set(r, a);
- } else {
- bf_div(q, a, b, bf_max(a->expn - b->expn + 1, 2), BF_RNDZ);
- bf_rint(q, BF_RNDZ);
- bf_mul(r, q, b, BF_PREC_INF, BF_RNDZ);
- bf_sub(r, a, r, BF_PREC_INF, BF_RNDZ);
- }
-}
-
-static int __bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
- bf_flags_t flags)
-{
- bf_context_t *s = r->ctx;
- int ret, r_sign;
- limb_t n, nb, precl;
-
- r_sign = a->sign ^ b->sign;
- if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) {
- if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- return 0;
- } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) {
- bf_set_nan(r);
- return BF_ST_INVALID_OP;
- } else if (a->expn == BF_EXP_INF) {
- bf_set_inf(r, r_sign);
- return 0;
- } else {
- bf_set_zero(r, r_sign);
- return 0;
- }
- } else if (a->expn == BF_EXP_ZERO) {
- if (b->expn == BF_EXP_ZERO) {
- bf_set_nan(r);
- return BF_ST_INVALID_OP;
- } else {
- bf_set_zero(r, r_sign);
- return 0;
- }
- } else if (b->expn == BF_EXP_ZERO) {
- bf_set_inf(r, r_sign);
- return BF_ST_DIVIDE_ZERO;
- }
-
- /* number of limbs of the quotient (2 extra bits for rounding) */
- precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS;
- nb = b->len;
- n = bf_max(a->len, precl);
-
- {
- limb_t *taba, na;
- slimb_t d;
-
- na = n + nb;
- taba = bf_malloc(s, (na + 1) * sizeof(limb_t));
- if (!taba)
- goto fail;
- d = na - a->len;
- memset(taba, 0, d * sizeof(limb_t));
- memcpy(taba + d, a->tab, a->len * sizeof(limb_t));
- if (bf_resize(r, n + 1))
- goto fail1;
- if (mp_divnorm(s, r->tab, taba, na, b->tab, nb)) {
- fail1:
- bf_free(s, taba);
- goto fail;
- }
- /* see if non zero remainder */
- if (mp_scan_nz(taba, nb))
- r->tab[0] |= 1;
- bf_free(r->ctx, taba);
- r->expn = a->expn - b->expn + LIMB_BITS;
- r->sign = r_sign;
- ret = bf_normalize_and_round(r, prec, flags);
- }
- return ret;
- fail:
- bf_set_nan(r);
- return BF_ST_MEM_ERROR;
-}
-
-/* division and remainder.
-
- rnd_mode is the rounding mode for the quotient. The additional
- rounding mode BF_RND_EUCLIDIAN is supported.
-
- 'q' is an integer. 'r' is rounded with prec and flags (prec can be
- BF_PREC_INF).
-*/
-int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b,
- limb_t prec, bf_flags_t flags, int rnd_mode)
-{
- bf_t a1_s, *a1 = &a1_s;
- bf_t b1_s, *b1 = &b1_s;
- int q_sign, ret;
- BOOL is_ceil, is_rndn;
-
- assert(q != a && q != b);
- assert(r != a && r != b);
- assert(q != r);
-
- if (a->len == 0 || b->len == 0) {
- bf_set_zero(q, 0);
- if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- return 0;
- } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) {
- bf_set_nan(r);
- return BF_ST_INVALID_OP;
- } else {
- bf_set(r, a);
- return bf_round(r, prec, flags);
- }
- }
-
- q_sign = a->sign ^ b->sign;
- is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA);
- switch(rnd_mode) {
- default:
- case BF_RNDZ:
- case BF_RNDN:
- case BF_RNDNA:
- is_ceil = FALSE;
- break;
- case BF_RNDD:
- is_ceil = q_sign;
- break;
- case BF_RNDU:
- is_ceil = q_sign ^ 1;
- break;
- case BF_RNDA:
- is_ceil = TRUE;
- break;
- case BF_DIVREM_EUCLIDIAN:
- is_ceil = a->sign;
- break;
- }
-
- a1->expn = a->expn;
- a1->tab = a->tab;
- a1->len = a->len;
- a1->sign = 0;
-
- b1->expn = b->expn;
- b1->tab = b->tab;
- b1->len = b->len;
- b1->sign = 0;
-
- /* XXX: could improve to avoid having a large 'q' */
- bf_tdivremu(q, r, a1, b1);
- if (bf_is_nan(q) || bf_is_nan(r))
- goto fail;
-
- if (r->len != 0) {
- if (is_rndn) {
- int res;
- b1->expn--;
- res = bf_cmpu(r, b1);
- b1->expn++;
- if (res > 0 ||
- (res == 0 &&
- (rnd_mode == BF_RNDNA ||
- get_bit(q->tab, q->len, q->len * LIMB_BITS - q->expn)))) {
- goto do_sub_r;
- }
- } else if (is_ceil) {
- do_sub_r:
- ret = bf_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ);
- ret |= bf_sub(r, r, b1, BF_PREC_INF, BF_RNDZ);
- if (ret & BF_ST_MEM_ERROR)
- goto fail;
- }
- }
-
- r->sign ^= a->sign;
- q->sign = q_sign;
- return bf_round(r, prec, flags);
- fail:
- bf_set_nan(q);
- bf_set_nan(r);
- return BF_ST_MEM_ERROR;
-}
-
-int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
- bf_flags_t flags, int rnd_mode)
-{
- bf_t q_s, *q = &q_s;
- int ret;
-
- bf_init(r->ctx, q);
- ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode);
- bf_delete(q);
- return ret;
-}
-
-static inline int bf_get_limb(slimb_t *pres, const bf_t *a, int flags)
-{
-#if LIMB_BITS == 32
- return bf_get_int32(pres, a, flags);
-#else
- return bf_get_int64(pres, a, flags);
-#endif
-}
-
-int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
- bf_flags_t flags, int rnd_mode)
-{
- bf_t q_s, *q = &q_s;
- int ret;
-
- bf_init(r->ctx, q);
- ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode);
- bf_get_limb(pq, q, BF_GET_INT_MOD);
- bf_delete(q);
- return ret;
-}
-
-static __maybe_unused inline limb_t mul_mod(limb_t a, limb_t b, limb_t m)
-{
- dlimb_t t;
- t = (dlimb_t)a * (dlimb_t)b;
- return t % m;
-}
-
-#if defined(USE_MUL_CHECK)
-static limb_t mp_mod1(const limb_t *tab, limb_t n, limb_t m, limb_t r)
-{
- slimb_t i;
- dlimb_t t;
-
- for(i = n - 1; i >= 0; i--) {
- t = ((dlimb_t)r << LIMB_BITS) | tab[i];
- r = t % m;
- }
- return r;
-}
-#endif
-
-static const uint16_t sqrt_table[192] = {
-128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,157,158,159,160,160,161,162,163,163,164,165,166,167,167,168,169,170,170,171,172,173,173,174,175,176,176,177,178,178,179,180,181,181,182,183,183,184,185,185,186,187,187,188,189,189,190,191,192,192,193,193,194,195,195,196,197,197,198,199,199,200,201,201,202,203,203,204,204,205,206,206,207,208,208,209,209,210,211,211,212,212,213,214,214,215,215,216,217,217,218,218,219,219,220,221,221,222,222,223,224,224,225,225,226,226,227,227,228,229,229,230,230,231,231,232,232,233,234,234,235,235,236,236,237,237,238,238,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,253,254,254,255,
-};
-
-/* a >= 2^(LIMB_BITS - 2). Return (s, r) with s=floor(sqrt(a)) and
- r=a-s^2. 0 <= r <= 2 * s */
-static limb_t mp_sqrtrem1(limb_t *pr, limb_t a)
-{
- limb_t s1, r1, s, r, q, u, num;
-
- /* use a table for the 16 -> 8 bit sqrt */
- s1 = sqrt_table[(a >> (LIMB_BITS - 8)) - 64];
- r1 = (a >> (LIMB_BITS - 16)) - s1 * s1;
- if (r1 > 2 * s1) {
- r1 -= 2 * s1 + 1;
- s1++;
- }
-
- /* one iteration to get a 32 -> 16 bit sqrt */
- num = (r1 << 8) | ((a >> (LIMB_BITS - 32 + 8)) & 0xff);
- q = num / (2 * s1); /* q <= 2^8 */
- u = num % (2 * s1);
- s = (s1 << 8) + q;
- r = (u << 8) | ((a >> (LIMB_BITS - 32)) & 0xff);
- r -= q * q;
- if ((slimb_t)r < 0) {
- s--;
- r += 2 * s + 1;
- }
-
-#if LIMB_BITS == 64
- s1 = s;
- r1 = r;
- /* one more iteration for 64 -> 32 bit sqrt */
- num = (r1 << 16) | ((a >> (LIMB_BITS - 64 + 16)) & 0xffff);
- q = num / (2 * s1); /* q <= 2^16 */
- u = num % (2 * s1);
- s = (s1 << 16) + q;
- r = (u << 16) | ((a >> (LIMB_BITS - 64)) & 0xffff);
- r -= q * q;
- if ((slimb_t)r < 0) {
- s--;
- r += 2 * s + 1;
- }
-#endif
- *pr = r;
- return s;
-}
-
-/* return floor(sqrt(a)) */
-limb_t bf_isqrt(limb_t a)
-{
- limb_t s, r;
- int k;
-
- if (a == 0)
- return 0;
- k = clz(a) & ~1;
- s = mp_sqrtrem1(&r, a << k);
- s >>= (k >> 1);
- return s;
-}
-
-static limb_t mp_sqrtrem2(limb_t *tabs, limb_t *taba)
-{
- limb_t s1, r1, s, q, u, a0, a1;
- dlimb_t r, num;
- int l;
-
- a0 = taba[0];
- a1 = taba[1];
- s1 = mp_sqrtrem1(&r1, a1);
- l = LIMB_BITS / 2;
- num = ((dlimb_t)r1 << l) | (a0 >> l);
- q = num / (2 * s1);
- u = num % (2 * s1);
- s = (s1 << l) + q;
- r = ((dlimb_t)u << l) | (a0 & (((limb_t)1 << l) - 1));
- if (unlikely((q >> l) != 0))
- r -= (dlimb_t)1 << LIMB_BITS; /* special case when q=2^l */
- else
- r -= q * q;
- if ((slimb_t)(r >> LIMB_BITS) < 0) {
- s--;
- r += 2 * (dlimb_t)s + 1;
- }
- tabs[0] = s;
- taba[0] = r;
- return r >> LIMB_BITS;
-}
-
-//#define DEBUG_SQRTREM
-
-/* tmp_buf must contain (n / 2 + 1 limbs). *prh contains the highest
- limb of the remainder. */
-static int mp_sqrtrem_rec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n,
- limb_t *tmp_buf, limb_t *prh)
-{
- limb_t l, h, rh, ql, qh, c, i;
-
- if (n == 1) {
- *prh = mp_sqrtrem2(tabs, taba);
- return 0;
- }
-#ifdef DEBUG_SQRTREM
- mp_print_str("a", taba, 2 * n);
-#endif
- l = n / 2;
- h = n - l;
- if (mp_sqrtrem_rec(s, tabs + l, taba + 2 * l, h, tmp_buf, &qh))
- return -1;
-#ifdef DEBUG_SQRTREM
- mp_print_str("s1", tabs + l, h);
- mp_print_str_h("r1", taba + 2 * l, h, qh);
- mp_print_str_h("r2", taba + l, n, qh);
-#endif
-
- /* the remainder is in taba + 2 * l. Its high bit is in qh */
- if (qh) {
- mp_sub(taba + 2 * l, taba + 2 * l, tabs + l, h, 0);
- }
- /* instead of dividing by 2*s, divide by s (which is normalized)
- and update q and r */
- if (mp_divnorm(s, tmp_buf, taba + l, n, tabs + l, h))
- return -1;
- qh += tmp_buf[l];
- for(i = 0; i < l; i++)
- tabs[i] = tmp_buf[i];
- ql = mp_shr(tabs, tabs, l, 1, qh & 1);
- qh = qh >> 1; /* 0 or 1 */
- if (ql)
- rh = mp_add(taba + l, taba + l, tabs + l, h, 0);
- else
- rh = 0;
-#ifdef DEBUG_SQRTREM
- mp_print_str_h("q", tabs, l, qh);
- mp_print_str_h("u", taba + l, h, rh);
-#endif
-
- mp_add_ui(tabs + l, qh, h);
-#ifdef DEBUG_SQRTREM
- mp_print_str_h("s2", tabs, n, sh);
-#endif
-
- /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */
- /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */
- if (qh) {
- c = qh;
- } else {
- if (mp_mul(s, taba + n, tabs, l, tabs, l))
- return -1;
- c = mp_sub(taba, taba, taba + n, 2 * l, 0);
- }
- rh -= mp_sub_ui(taba + 2 * l, c, n - 2 * l);
- if ((slimb_t)rh < 0) {
- mp_sub_ui(tabs, 1, n);
- rh += mp_add_mul1(taba, tabs, n, 2);
- rh += mp_add_ui(taba, 1, n);
- }
- *prh = rh;
- return 0;
-}
-
-/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= 2 ^ (LIMB_BITS
- - 2). Return (s, r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2
- * s. tabs has n limbs. r is returned in the lower n limbs of
- taba. Its r[n] is the returned value of the function. */
-/* Algorithm from the article "Karatsuba Square Root" by Paul Zimmermann and
- inspirated from its GMP implementation */
-int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n)
-{
- limb_t tmp_buf1[8];
- limb_t *tmp_buf;
- mp_size_t n2;
- int ret;
- n2 = n / 2 + 1;
- if (n2 <= countof(tmp_buf1)) {
- tmp_buf = tmp_buf1;
- } else {
- tmp_buf = bf_malloc(s, sizeof(limb_t) * n2);
- if (!tmp_buf)
- return -1;
- }
- ret = mp_sqrtrem_rec(s, tabs, taba, n, tmp_buf, taba + n);
- if (tmp_buf != tmp_buf1)
- bf_free(s, tmp_buf);
- return ret;
-}
-
-/* Integer square root with remainder. 'a' must be an integer. r =
- floor(sqrt(a)) and rem = a - r^2. BF_ST_INEXACT is set if the result
- is inexact. 'rem' can be NULL if the remainder is not needed. */
-int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a)
-{
- int ret;
-
- if (a->len == 0) {
- if (a->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- } else if (a->expn == BF_EXP_INF && a->sign) {
- goto invalid_op;
- } else {
- bf_set(r, a);
- }
- if (rem1)
- bf_set_ui(rem1, 0);
- ret = 0;
- } else if (a->sign) {
- invalid_op:
- bf_set_nan(r);
- if (rem1)
- bf_set_ui(rem1, 0);
- ret = BF_ST_INVALID_OP;
- } else {
- bf_t rem_s, *rem;
-
- bf_sqrt(r, a, (a->expn + 1) / 2, BF_RNDZ);
- bf_rint(r, BF_RNDZ);
- /* see if the result is exact by computing the remainder */
- if (rem1) {
- rem = rem1;
- } else {
- rem = &rem_s;
- bf_init(r->ctx, rem);
- }
- /* XXX: could avoid recomputing the remainder */
- bf_mul(rem, r, r, BF_PREC_INF, BF_RNDZ);
- bf_neg(rem);
- bf_add(rem, rem, a, BF_PREC_INF, BF_RNDZ);
- if (bf_is_nan(rem)) {
- ret = BF_ST_MEM_ERROR;
- goto done;
- }
- if (rem->len != 0) {
- ret = BF_ST_INEXACT;
- } else {
- ret = 0;
- }
- done:
- if (!rem1)
- bf_delete(rem);
- }
- return ret;
-}
-
-int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
- bf_context_t *s = a->ctx;
- int ret;
-
- assert(r != a);
-
- if (a->len == 0) {
- if (a->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- } else if (a->expn == BF_EXP_INF && a->sign) {
- goto invalid_op;
- } else {
- bf_set(r, a);
- }
- ret = 0;
- } else if (a->sign) {
- invalid_op:
- bf_set_nan(r);
- ret = BF_ST_INVALID_OP;
- } else {
- limb_t *a1;
- slimb_t n, n1;
- limb_t res;
-
- /* convert the mantissa to an integer with at least 2 *
- prec + 4 bits */
- n = (2 * (prec + 2) + 2 * LIMB_BITS - 1) / (2 * LIMB_BITS);
- if (bf_resize(r, n))
- goto fail;
- a1 = bf_malloc(s, sizeof(limb_t) * 2 * n);
- if (!a1)
- goto fail;
- n1 = bf_min(2 * n, a->len);
- memset(a1, 0, (2 * n - n1) * sizeof(limb_t));
- memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t));
- if (a->expn & 1) {
- res = mp_shr(a1, a1, 2 * n, 1, 0);
- } else {
- res = 0;
- }
- if (mp_sqrtrem(s, r->tab, a1, n)) {
- bf_free(s, a1);
- goto fail;
- }
- if (!res) {
- res = mp_scan_nz(a1, n + 1);
- }
- bf_free(s, a1);
- if (!res) {
- res = mp_scan_nz(a->tab, a->len - n1);
- }
- if (res != 0)
- r->tab[0] |= 1;
- r->sign = 0;
- r->expn = (a->expn + 1) >> 1;
- ret = bf_round(r, prec, flags);
- }
- return ret;
- fail:
- bf_set_nan(r);
- return BF_ST_MEM_ERROR;
-}
-
-static no_inline int bf_op2(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
- bf_flags_t flags, bf_op2_func_t *func)
-{
- bf_t tmp;
- int ret;
-
- if (r == a || r == b) {
- bf_init(r->ctx, &tmp);
- ret = func(&tmp, a, b, prec, flags);
- bf_move(r, &tmp);
- } else {
- ret = func(r, a, b, prec, flags);
- }
- return ret;
-}
-
-int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
- bf_flags_t flags)
-{
- return bf_op2(r, a, b, prec, flags, __bf_add);
-}
-
-int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
- bf_flags_t flags)
-{
- return bf_op2(r, a, b, prec, flags, __bf_sub);
-}
-
-int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
- bf_flags_t flags)
-{
- return bf_op2(r, a, b, prec, flags, __bf_div);
-}
-
-int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec,
- bf_flags_t flags)
-{
- bf_t b;
- int ret;
- bf_init(r->ctx, &b);
- ret = bf_set_ui(&b, b1);
- ret |= bf_mul(r, a, &b, prec, flags);
- bf_delete(&b);
- return ret;
-}
-
-int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec,
- bf_flags_t flags)
-{
- bf_t b;
- int ret;
- bf_init(r->ctx, &b);
- ret = bf_set_si(&b, b1);
- ret |= bf_mul(r, a, &b, prec, flags);
- bf_delete(&b);
- return ret;
-}
-
-int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec,
- bf_flags_t flags)
-{
- bf_t b;
- int ret;
-
- bf_init(r->ctx, &b);
- ret = bf_set_si(&b, b1);
- ret |= bf_add(r, a, &b, prec, flags);
- bf_delete(&b);
- return ret;
-}
-
-static int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec,
- bf_flags_t flags)
-{
- int ret, n_bits, i;
-
- assert(r != a);
- if (b == 0)
- return bf_set_ui(r, 1);
- ret = bf_set(r, a);
- n_bits = LIMB_BITS - clz(b);
- for(i = n_bits - 2; i >= 0; i--) {
- ret |= bf_mul(r, r, r, prec, flags);
- if ((b >> i) & 1)
- ret |= bf_mul(r, r, a, prec, flags);
- }
- return ret;
-}
-
-static int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b,
- limb_t prec, bf_flags_t flags)
-{
- bf_t a;
- int ret;
-
-#ifdef USE_BF_DEC
- if (a1 == 10 && b <= LIMB_DIGITS) {
- /* use precomputed powers. We do not round at this point
- because we expect the caller to do it */
- ret = bf_set_ui(r, mp_pow_dec[b]);
- } else
-#endif
- {
- bf_init(r->ctx, &a);
- ret = bf_set_ui(&a, a1);
- ret |= bf_pow_ui(r, &a, b, prec, flags);
- bf_delete(&a);
- }
- return ret;
-}
-
-/* convert to integer (infinite precision) */
-int bf_rint(bf_t *r, int rnd_mode)
-{
- return bf_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC);
-}
-
-/* logical operations */
-#define BF_LOGIC_OR 0
-#define BF_LOGIC_XOR 1
-#define BF_LOGIC_AND 2
-
-static inline limb_t bf_logic_op1(limb_t a, limb_t b, int op)
-{
- switch(op) {
- case BF_LOGIC_OR:
- return a | b;
- case BF_LOGIC_XOR:
- return a ^ b;
- default:
- case BF_LOGIC_AND:
- return a & b;
- }
-}
-
-static int bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op)
-{
- bf_t b1_s, a1_s, *a, *b;
- limb_t a_sign, b_sign, r_sign;
- slimb_t l, i, a_bit_offset, b_bit_offset;
- limb_t v1, v2, v1_mask, v2_mask, r_mask;
- int ret;
-
- assert(r != a1 && r != b1);
-
- if (a1->expn <= 0)
- a_sign = 0; /* minus zero is considered as positive */
- else
- a_sign = a1->sign;
-
- if (b1->expn <= 0)
- b_sign = 0; /* minus zero is considered as positive */
- else
- b_sign = b1->sign;
-
- if (a_sign) {
- a = &a1_s;
- bf_init(r->ctx, a);
- if (bf_add_si(a, a1, 1, BF_PREC_INF, BF_RNDZ)) {
- b = NULL;
- goto fail;
- }
- } else {
- a = (bf_t *)a1;
- }
-
- if (b_sign) {
- b = &b1_s;
- bf_init(r->ctx, b);
- if (bf_add_si(b, b1, 1, BF_PREC_INF, BF_RNDZ))
- goto fail;
- } else {
- b = (bf_t *)b1;
- }
-
- r_sign = bf_logic_op1(a_sign, b_sign, op);
- if (op == BF_LOGIC_AND && r_sign == 0) {
- /* no need to compute extra zeros for and */
- if (a_sign == 0 && b_sign == 0)
- l = bf_min(a->expn, b->expn);
- else if (a_sign == 0)
- l = a->expn;
- else
- l = b->expn;
- } else {
- l = bf_max(a->expn, b->expn);
- }
- /* Note: a or b can be zero */
- l = (bf_max(l, 1) + LIMB_BITS - 1) / LIMB_BITS;
- if (bf_resize(r, l))
- goto fail;
- a_bit_offset = a->len * LIMB_BITS - a->expn;
- b_bit_offset = b->len * LIMB_BITS - b->expn;
- v1_mask = -a_sign;
- v2_mask = -b_sign;
- r_mask = -r_sign;
- for(i = 0; i < l; i++) {
- v1 = get_bits(a->tab, a->len, a_bit_offset + i * LIMB_BITS) ^ v1_mask;
- v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS) ^ v2_mask;
- r->tab[i] = bf_logic_op1(v1, v2, op) ^ r_mask;
- }
- r->expn = l * LIMB_BITS;
- r->sign = r_sign;
- bf_normalize_and_round(r, BF_PREC_INF, BF_RNDZ); /* cannot fail */
- if (r_sign) {
- if (bf_add_si(r, r, -1, BF_PREC_INF, BF_RNDZ))
- goto fail;
- }
- ret = 0;
- done:
- if (a == &a1_s)
- bf_delete(a);
- if (b == &b1_s)
- bf_delete(b);
- return ret;
- fail:
- bf_set_nan(r);
- ret = BF_ST_MEM_ERROR;
- goto done;
-}
-
-/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */
-int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b)
-{
- return bf_logic_op(r, a, b, BF_LOGIC_OR);
-}
-
-/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */
-int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b)
-{
- return bf_logic_op(r, a, b, BF_LOGIC_XOR);
-}
-
-/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */
-int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b)
-{
- return bf_logic_op(r, a, b, BF_LOGIC_AND);
-}
-
-/* conversion between fixed size types */
-
-typedef union {
- double d;
- uint64_t u;
-} Float64Union;
-
-int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode)
-{
- Float64Union u;
- int e, ret;
- uint64_t m;
-
- ret = 0;
- if (a->expn == BF_EXP_NAN) {
- u.u = 0x7ff8000000000000; /* quiet nan */
- } else {
- bf_t b_s, *b = &b_s;
-
- bf_init(a->ctx, b);
- bf_set(b, a);
- if (bf_is_finite(b)) {
- ret = bf_round(b, 53, rnd_mode | BF_FLAG_SUBNORMAL | bf_set_exp_bits(11));
- }
- if (b->expn == BF_EXP_INF) {
- e = (1 << 11) - 1;
- m = 0;
- } else if (b->expn == BF_EXP_ZERO) {
- e = 0;
- m = 0;
- } else {
- e = b->expn + 1023 - 1;
-#if LIMB_BITS == 32
- if (b->len == 2) {
- m = ((uint64_t)b->tab[1] << 32) | b->tab[0];
- } else {
- m = ((uint64_t)b->tab[0] << 32);
- }
-#else
- m = b->tab[0];
-#endif
- if (e <= 0) {
- /* subnormal */
- m = m >> (12 - e);
- e = 0;
- } else {
- m = (m << 1) >> 12;
- }
- }
- u.u = m | ((uint64_t)e << 52) | ((uint64_t)b->sign << 63);
- bf_delete(b);
- }
- *pres = u.d;
- return ret;
-}
-
-int bf_set_float64(bf_t *a, double d)
-{
- Float64Union u;
- uint64_t m;
- int shift, e, sgn;
-
- u.d = d;
- sgn = u.u >> 63;
- e = (u.u >> 52) & ((1 << 11) - 1);
- m = u.u & (((uint64_t)1 << 52) - 1);
- if (e == ((1 << 11) - 1)) {
- if (m != 0) {
- bf_set_nan(a);
- } else {
- bf_set_inf(a, sgn);
- }
- } else if (e == 0) {
- if (m == 0) {
- bf_set_zero(a, sgn);
- } else {
- /* subnormal number */
- m <<= 12;
- shift = clz64(m);
- m <<= shift;
- e = -shift;
- goto norm;
- }
- } else {
- m = (m << 11) | ((uint64_t)1 << 63);
- norm:
- a->expn = e - 1023 + 1;
-#if LIMB_BITS == 32
- if (bf_resize(a, 2))
- goto fail;
- a->tab[0] = m;
- a->tab[1] = m >> 32;
-#else
- if (bf_resize(a, 1))
- goto fail;
- a->tab[0] = m;
-#endif
- a->sign = sgn;
- }
- return 0;
-fail:
- bf_set_nan(a);
- return BF_ST_MEM_ERROR;
-}
-
-/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there
- is an overflow and 0 otherwise. */
-int bf_get_int32(int *pres, const bf_t *a, int flags)
-{
- uint32_t v;
- int ret;
- if (a->expn >= BF_EXP_INF) {
- ret = BF_ST_INVALID_OP;
- if (flags & BF_GET_INT_MOD) {
- v = 0;
- } else if (a->expn == BF_EXP_INF) {
- v = (uint32_t)INT32_MAX + a->sign;
- } else {
- v = INT32_MAX;
- }
- } else if (a->expn <= 0) {
- v = 0;
- ret = 0;
- } else if (a->expn <= 31) {
- v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);
- if (a->sign)
- v = -v;
- ret = 0;
- } else if (!(flags & BF_GET_INT_MOD)) {
- ret = BF_ST_INVALID_OP;
- if (a->sign) {
- v = (uint32_t)INT32_MAX + 1;
- if (a->expn == 32 &&
- (a->tab[a->len - 1] >> (LIMB_BITS - 32)) == v) {
- ret = 0;
- }
- } else {
- v = INT32_MAX;
- }
- } else {
- v = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn);
- if (a->sign)
- v = -v;
- ret = 0;
- }
- *pres = v;
- return ret;
-}
-
-/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there
- is an overflow and 0 otherwise. */
-int bf_get_int64(int64_t *pres, const bf_t *a, int flags)
-{
- uint64_t v;
- int ret;
- if (a->expn >= BF_EXP_INF) {
- ret = BF_ST_INVALID_OP;
- if (flags & BF_GET_INT_MOD) {
- v = 0;
- } else if (a->expn == BF_EXP_INF) {
- v = (uint64_t)INT64_MAX + a->sign;
- } else {
- v = INT64_MAX;
- }
- } else if (a->expn <= 0) {
- v = 0;
- ret = 0;
- } else if (a->expn <= 63) {
-#if LIMB_BITS == 32
- if (a->expn <= 32)
- v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);
- else
- v = (((uint64_t)a->tab[a->len - 1] << 32) |
- get_limbz(a, a->len - 2)) >> (64 - a->expn);
-#else
- v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);
-#endif
- if (a->sign)
- v = -v;
- ret = 0;
- } else if (!(flags & BF_GET_INT_MOD)) {
- ret = BF_ST_INVALID_OP;
- if (a->sign) {
- uint64_t v1;
- v = (uint64_t)INT64_MAX + 1;
- if (a->expn == 64) {
- v1 = a->tab[a->len - 1];
-#if LIMB_BITS == 32
- v1 = (v1 << 32) | get_limbz(a, a->len - 2);
-#endif
- if (v1 == v)
- ret = 0;
- }
- } else {
- v = INT64_MAX;
- }
- } else {
- slimb_t bit_pos = a->len * LIMB_BITS - a->expn;
- v = get_bits(a->tab, a->len, bit_pos);
-#if LIMB_BITS == 32
- v |= (uint64_t)get_bits(a->tab, a->len, bit_pos + 32) << 32;
-#endif
- if (a->sign)
- v = -v;
- ret = 0;
- }
- *pres = v;
- return ret;
-}
-
-/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there
- is an overflow and 0 otherwise. */
-int bf_get_uint64(uint64_t *pres, const bf_t *a)
-{
- uint64_t v;
- int ret;
- if (a->expn == BF_EXP_NAN) {
- goto overflow;
- } else if (a->expn <= 0) {
- v = 0;
- ret = 0;
- } else if (a->sign) {
- v = 0;
- ret = BF_ST_INVALID_OP;
- } else if (a->expn <= 64) {
-#if LIMB_BITS == 32
- if (a->expn <= 32)
- v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);
- else
- v = (((uint64_t)a->tab[a->len - 1] << 32) |
- get_limbz(a, a->len - 2)) >> (64 - a->expn);
-#else
- v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn);
-#endif
- ret = 0;
- } else {
- overflow:
- v = UINT64_MAX;
- ret = BF_ST_INVALID_OP;
- }
- *pres = v;
- return ret;
-}
-
-/* base conversion from radix */
-
-static const uint8_t digits_per_limb_table[BF_RADIX_MAX - 1] = {
-#if LIMB_BITS == 32
-32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
-#else
-64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12,
-#endif
-};
-
-static limb_t get_limb_radix(int radix)
-{
- int i, k;
- limb_t radixl;
-
- k = digits_per_limb_table[radix - 2];
- radixl = radix;
- for(i = 1; i < k; i++)
- radixl *= radix;
- return radixl;
-}
-
-/* return != 0 if error */
-static int bf_integer_from_radix_rec(bf_t *r, const limb_t *tab,
- limb_t n, int level, limb_t n0,
- limb_t radix, bf_t *pow_tab)
-{
- int ret;
- if (n == 1) {
- ret = bf_set_ui(r, tab[0]);
- } else {
- bf_t T_s, *T = &T_s, *B;
- limb_t n1, n2;
-
- n2 = (((n0 * 2) >> (level + 1)) + 1) / 2;
- n1 = n - n2;
- // printf("level=%d n0=%ld n1=%ld n2=%ld\n", level, n0, n1, n2);
- B = &pow_tab[level];
- if (B->len == 0) {
- ret = bf_pow_ui_ui(B, radix, n2, BF_PREC_INF, BF_RNDZ);
- if (ret)
- return ret;
- }
- ret = bf_integer_from_radix_rec(r, tab + n2, n1, level + 1, n0,
- radix, pow_tab);
- if (ret)
- return ret;
- ret = bf_mul(r, r, B, BF_PREC_INF, BF_RNDZ);
- if (ret)
- return ret;
- bf_init(r->ctx, T);
- ret = bf_integer_from_radix_rec(T, tab, n2, level + 1, n0,
- radix, pow_tab);
- if (!ret)
- ret = bf_add(r, r, T, BF_PREC_INF, BF_RNDZ);
- bf_delete(T);
- }
- return ret;
- // bf_print_str(" r=", r);
-}
-
-/* return 0 if OK != 0 if memory error */
-static int bf_integer_from_radix(bf_t *r, const limb_t *tab,
- limb_t n, limb_t radix)
-{
- bf_context_t *s = r->ctx;
- int pow_tab_len, i, ret;
- limb_t radixl;
- bf_t *pow_tab;
-
- radixl = get_limb_radix(radix);
- pow_tab_len = ceil_log2(n) + 2; /* XXX: check */
- pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len);
- if (!pow_tab)
- return -1;
- for(i = 0; i < pow_tab_len; i++)
- bf_init(r->ctx, &pow_tab[i]);
- ret = bf_integer_from_radix_rec(r, tab, n, 0, n, radixl, pow_tab);
- for(i = 0; i < pow_tab_len; i++) {
- bf_delete(&pow_tab[i]);
- }
- bf_free(s, pow_tab);
- return ret;
-}
-
-/* compute and round T * radix^expn. */
-int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
- slimb_t expn, limb_t prec, bf_flags_t flags)
-{
- int ret, expn_sign, overflow;
- slimb_t e, extra_bits, prec1, ziv_extra_bits;
- bf_t B_s, *B = &B_s;
-
- if (T->len == 0) {
- return bf_set(r, T);
- } else if (expn == 0) {
- ret = bf_set(r, T);
- ret |= bf_round(r, prec, flags);
- return ret;
- }
-
- e = expn;
- expn_sign = 0;
- if (e < 0) {
- e = -e;
- expn_sign = 1;
- }
- bf_init(r->ctx, B);
- if (prec == BF_PREC_INF) {
- /* infinite precision: only used if the result is known to be exact */
- ret = bf_pow_ui_ui(B, radix, e, BF_PREC_INF, BF_RNDN);
- if (expn_sign) {
- ret |= bf_div(r, T, B, T->len * LIMB_BITS, BF_RNDN);
- } else {
- ret |= bf_mul(r, T, B, BF_PREC_INF, BF_RNDN);
- }
- } else {
- ziv_extra_bits = 16;
- for(;;) {
- prec1 = prec + ziv_extra_bits;
- /* XXX: correct overflow/underflow handling */
- /* XXX: rigorous error analysis needed */
- extra_bits = ceil_log2(e) * 2 + 1;
- ret = bf_pow_ui_ui(B, radix, e, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP);
- overflow = !bf_is_finite(B);
- /* XXX: if bf_pow_ui_ui returns an exact result, can stop
- after the next operation */
- if (expn_sign)
- ret |= bf_div(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP);
- else
- ret |= bf_mul(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP);
- if (ret & BF_ST_MEM_ERROR)
- break;
- if ((ret & BF_ST_INEXACT) &&
- !bf_can_round(r, prec, flags & BF_RND_MASK, prec1) &&
- !overflow) {
- /* and more precision and retry */
- ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2);
- } else {
- /* XXX: need to use __bf_round() to pass the inexact
- flag for the subnormal case */
- ret = bf_round(r, prec, flags) | (ret & BF_ST_INEXACT);
- break;
- }
- }
- }
- bf_delete(B);
- return ret;
-}
-
-static inline int to_digit(int c)
-{
- if (c >= '0' && c <= '9')
- return c - '0';
- else if (c >= 'A' && c <= 'Z')
- return c - 'A' + 10;
- else if (c >= 'a' && c <= 'z')
- return c - 'a' + 10;
- else
- return 36;
-}
-
-/* add a limb at 'pos' and decrement pos. new space is created if
- needed. Return 0 if OK, -1 if memory error */
-static int bf_add_limb(bf_t *a, slimb_t *ppos, limb_t v)
-{
- slimb_t pos;
- pos = *ppos;
- if (unlikely(pos < 0)) {
- limb_t new_size, d, *new_tab;
- new_size = bf_max(a->len + 1, a->len * 3 / 2);
- new_tab = bf_realloc(a->ctx, a->tab, sizeof(limb_t) * new_size);
- if (!new_tab)
- return -1;
- a->tab = new_tab;
- d = new_size - a->len;
- memmove(a->tab + d, a->tab, a->len * sizeof(limb_t));
- a->len = new_size;
- pos += d;
- }
- a->tab[pos--] = v;
- *ppos = pos;
- return 0;
-}
-
-static int bf_tolower(int c)
-{
- if (c >= 'A' && c <= 'Z')
- c = c - 'A' + 'a';
- return c;
-}
-
-static int strcasestart(const char *str, const char *val, const char **ptr)
-{
- const char *p, *q;
- p = str;
- q = val;
- while (*q != '\0') {
- if (bf_tolower(*p) != *q)
- return 0;
- p++;
- q++;
- }
- if (ptr)
- *ptr = p;
- return 1;
-}
-
-static int bf_atof_internal(bf_t *r, slimb_t *pexponent,
- const char *str, const char **pnext, int radix,
- limb_t prec, bf_flags_t flags, BOOL is_dec)
-{
- const char *p, *p_start;
- int is_neg, radix_bits, exp_is_neg, ret, digits_per_limb, shift;
- limb_t cur_limb;
- slimb_t pos, expn, int_len, digit_count;
- BOOL has_decpt, is_bin_exp;
- bf_t a_s, *a;
-
- *pexponent = 0;
- p = str;
- if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 &&
- strcasestart(p, "nan", &p)) {
- bf_set_nan(r);
- ret = 0;
- goto done;
- }
- is_neg = 0;
-
- if (p[0] == '+') {
- p++;
- p_start = p;
- } else if (p[0] == '-') {
- is_neg = 1;
- p++;
- p_start = p;
- } else {
- p_start = p;
- }
- if (p[0] == '0') {
- if ((p[1] == 'x' || p[1] == 'X') &&
- (radix == 0 || radix == 16) &&
- !(flags & BF_ATOF_NO_HEX)) {
- radix = 16;
- p += 2;
- } else if ((p[1] == 'o' || p[1] == 'O') &&
- radix == 0 && (flags & BF_ATOF_BIN_OCT)) {
- p += 2;
- radix = 8;
- } else if ((p[1] == 'b' || p[1] == 'B') &&
- radix == 0 && (flags & BF_ATOF_BIN_OCT)) {
- p += 2;
- radix = 2;
- } else {
- goto no_prefix;
- }
- /* there must be a digit after the prefix */
- if (to_digit((uint8_t)*p) >= radix) {
- bf_set_nan(r);
- ret = 0;
- goto done;
- }
- no_prefix: ;
- } else {
- if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 &&
- strcasestart(p, "inf", &p)) {
- bf_set_inf(r, is_neg);
- ret = 0;
- goto done;
- }
- }
-
- if (radix == 0)
- radix = 10;
- if (is_dec) {
- assert(radix == 10);
- radix_bits = 0;
- a = r;
- } else if ((radix & (radix - 1)) != 0) {
- radix_bits = 0; /* base is not a power of two */
- a = &a_s;
- bf_init(r->ctx, a);
- } else {
- radix_bits = ceil_log2(radix);
- a = r;
- }
-
- /* skip leading zeros */
- /* XXX: could also skip zeros after the decimal point */
- while (*p == '0')
- p++;
-
- if (radix_bits) {
- shift = digits_per_limb = LIMB_BITS;
- } else {
- radix_bits = 0;
- shift = digits_per_limb = digits_per_limb_table[radix - 2];
- }
- cur_limb = 0;
- bf_resize(a, 1);
- pos = 0;
- has_decpt = FALSE;
- int_len = digit_count = 0;
- for(;;) {
- limb_t c;
- if (*p == '.' && (p > p_start || to_digit(p[1]) < radix)) {
- if (has_decpt)
- break;
- has_decpt = TRUE;
- int_len = digit_count;
- p++;
- }
- c = to_digit(*p);
- if (c >= radix)
- break;
- digit_count++;
- p++;
- if (radix_bits) {
- shift -= radix_bits;
- if (shift <= 0) {
- cur_limb |= c >> (-shift);
- if (bf_add_limb(a, &pos, cur_limb))
- goto mem_error;
- if (shift < 0)
- cur_limb = c << (LIMB_BITS + shift);
- else
- cur_limb = 0;
- shift += LIMB_BITS;
- } else {
- cur_limb |= c << shift;
- }
- } else {
- cur_limb = cur_limb * radix + c;
- shift--;
- if (shift == 0) {
- if (bf_add_limb(a, &pos, cur_limb))
- goto mem_error;
- shift = digits_per_limb;
- cur_limb = 0;
- }
- }
- }
- if (!has_decpt)
- int_len = digit_count;
-
- /* add the last limb and pad with zeros */
- if (shift != digits_per_limb) {
- if (radix_bits == 0) {
- while (shift != 0) {
- cur_limb *= radix;
- shift--;
- }
- }
- if (bf_add_limb(a, &pos, cur_limb)) {
- mem_error:
- ret = BF_ST_MEM_ERROR;
- if (!radix_bits)
- bf_delete(a);
- bf_set_nan(r);
- goto done;
- }
- }
-
- /* reset the next limbs to zero (we prefer to reallocate in the
- renormalization) */
- memset(a->tab, 0, (pos + 1) * sizeof(limb_t));
-
- if (p == p_start) {
- ret = 0;
- if (!radix_bits)
- bf_delete(a);
- bf_set_nan(r);
- goto done;
- }
-
- /* parse the exponent, if any */
- expn = 0;
- is_bin_exp = FALSE;
- if (((radix == 10 && (*p == 'e' || *p == 'E')) ||
- (radix != 10 && (*p == '@' ||
- (radix_bits && (*p == 'p' || *p == 'P'))))) &&
- p > p_start) {
- is_bin_exp = (*p == 'p' || *p == 'P');
- p++;
- exp_is_neg = 0;
- if (*p == '+') {
- p++;
- } else if (*p == '-') {
- exp_is_neg = 1;
- p++;
- }
- for(;;) {
- int c;
- c = to_digit(*p);
- if (c >= 10)
- break;
- if (unlikely(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) {
- /* exponent overflow */
- if (exp_is_neg) {
- bf_set_zero(r, is_neg);
- ret = BF_ST_UNDERFLOW | BF_ST_INEXACT;
- } else {
- bf_set_inf(r, is_neg);
- ret = BF_ST_OVERFLOW | BF_ST_INEXACT;
- }
- goto done;
- }
- p++;
- expn = expn * 10 + c;
- }
- if (exp_is_neg)
- expn = -expn;
- }
- if (is_dec) {
- a->expn = expn + int_len;
- a->sign = is_neg;
- ret = bfdec_normalize_and_round((bfdec_t *)a, prec, flags);
- } else if (radix_bits) {
- /* XXX: may overflow */
- if (!is_bin_exp)
- expn *= radix_bits;
- a->expn = expn + (int_len * radix_bits);
- a->sign = is_neg;
- ret = bf_normalize_and_round(a, prec, flags);
- } else {
- limb_t l;
- pos++;
- l = a->len - pos; /* number of limbs */
- if (l == 0) {
- bf_set_zero(r, is_neg);
- ret = 0;
- } else {
- bf_t T_s, *T = &T_s;
-
- expn -= l * digits_per_limb - int_len;
- bf_init(r->ctx, T);
- if (bf_integer_from_radix(T, a->tab + pos, l, radix)) {
- bf_set_nan(r);
- ret = BF_ST_MEM_ERROR;
- } else {
- T->sign = is_neg;
- if (flags & BF_ATOF_EXPONENT) {
- /* return the exponent */
- *pexponent = expn;
- ret = bf_set(r, T);
- } else {
- ret = bf_mul_pow_radix(r, T, radix, expn, prec, flags);
- }
- }
- bf_delete(T);
- }
- bf_delete(a);
- }
- done:
- if (pnext)
- *pnext = p;
- return ret;
-}
-
-/*
- Return (status, n, exp). 'status' is the floating point status. 'n'
- is the parsed number.
-
- If (flags & BF_ATOF_EXPONENT) and if the radix is not a power of
- two, the parsed number is equal to r *
- (*pexponent)^radix. Otherwise *pexponent = 0.
-*/
-int bf_atof2(bf_t *r, slimb_t *pexponent,
- const char *str, const char **pnext, int radix,
- limb_t prec, bf_flags_t flags)
-{
- return bf_atof_internal(r, pexponent, str, pnext, radix, prec, flags,
- FALSE);
-}
-
-int bf_atof(bf_t *r, const char *str, const char **pnext, int radix,
- limb_t prec, bf_flags_t flags)
-{
- slimb_t dummy_exp;
- return bf_atof_internal(r, &dummy_exp, str, pnext, radix, prec, flags, FALSE);
-}
-
-/* base conversion to radix */
-
-#if LIMB_BITS == 64
-#define RADIXL_10 UINT64_C(10000000000000000000)
-#else
-#define RADIXL_10 UINT64_C(1000000000)
-#endif
-
-static const uint32_t inv_log2_radix[BF_RADIX_MAX - 1][LIMB_BITS / 32 + 1] = {
-#if LIMB_BITS == 32
-{ 0x80000000, 0x00000000,},
-{ 0x50c24e60, 0xd4d4f4a7,},
-{ 0x40000000, 0x00000000,},
-{ 0x372068d2, 0x0a1ee5ca,},
-{ 0x3184648d, 0xb8153e7a,},
-{ 0x2d983275, 0x9d5369c4,},
-{ 0x2aaaaaaa, 0xaaaaaaab,},
-{ 0x28612730, 0x6a6a7a54,},
-{ 0x268826a1, 0x3ef3fde6,},
-{ 0x25001383, 0xbac8a744,},
-{ 0x23b46706, 0x82c0c709,},
-{ 0x229729f1, 0xb2c83ded,},
-{ 0x219e7ffd, 0xa5ad572b,},
-{ 0x20c33b88, 0xda7c29ab,},
-{ 0x20000000, 0x00000000,},
-{ 0x1f50b57e, 0xac5884b3,},
-{ 0x1eb22cc6, 0x8aa6e26f,},
-{ 0x1e21e118, 0x0c5daab2,},
-{ 0x1d9dcd21, 0x439834e4,},
-{ 0x1d244c78, 0x367a0d65,},
-{ 0x1cb40589, 0xac173e0c,},
-{ 0x1c4bd95b, 0xa8d72b0d,},
-{ 0x1bead768, 0x98f8ce4c,},
-{ 0x1b903469, 0x050f72e5,},
-{ 0x1b3b433f, 0x2eb06f15,},
-{ 0x1aeb6f75, 0x9c46fc38,},
-{ 0x1aa038eb, 0x0e3bfd17,},
-{ 0x1a593062, 0xb38d8c56,},
-{ 0x1a15f4c3, 0x2b95a2e6,},
-{ 0x19d630dc, 0xcc7ddef9,},
-{ 0x19999999, 0x9999999a,},
-{ 0x195fec80, 0x8a609431,},
-{ 0x1928ee7b, 0x0b4f22f9,},
-{ 0x18f46acf, 0x8c06e318,},
-{ 0x18c23246, 0xdc0a9f3d,},
-#else
-{ 0x80000000, 0x00000000, 0x00000000,},
-{ 0x50c24e60, 0xd4d4f4a7, 0x021f57bc,},
-{ 0x40000000, 0x00000000, 0x00000000,},
-{ 0x372068d2, 0x0a1ee5ca, 0x19ea911b,},
-{ 0x3184648d, 0xb8153e7a, 0x7fc2d2e1,},
-{ 0x2d983275, 0x9d5369c4, 0x4dec1661,},
-{ 0x2aaaaaaa, 0xaaaaaaaa, 0xaaaaaaab,},
-{ 0x28612730, 0x6a6a7a53, 0x810fabde,},
-{ 0x268826a1, 0x3ef3fde6, 0x23e2566b,},
-{ 0x25001383, 0xbac8a744, 0x385a3349,},
-{ 0x23b46706, 0x82c0c709, 0x3f891718,},
-{ 0x229729f1, 0xb2c83ded, 0x15fba800,},
-{ 0x219e7ffd, 0xa5ad572a, 0xe169744b,},
-{ 0x20c33b88, 0xda7c29aa, 0x9bddee52,},
-{ 0x20000000, 0x00000000, 0x00000000,},
-{ 0x1f50b57e, 0xac5884b3, 0x70e28eee,},
-{ 0x1eb22cc6, 0x8aa6e26f, 0x06d1a2a2,},
-{ 0x1e21e118, 0x0c5daab1, 0x81b4f4bf,},
-{ 0x1d9dcd21, 0x439834e3, 0x81667575,},
-{ 0x1d244c78, 0x367a0d64, 0xc8204d6d,},
-{ 0x1cb40589, 0xac173e0c, 0x3b7b16ba,},
-{ 0x1c4bd95b, 0xa8d72b0d, 0x5879f25a,},
-{ 0x1bead768, 0x98f8ce4c, 0x66cc2858,},
-{ 0x1b903469, 0x050f72e5, 0x0cf5488e,},
-{ 0x1b3b433f, 0x2eb06f14, 0x8c89719c,},
-{ 0x1aeb6f75, 0x9c46fc37, 0xab5fc7e9,},
-{ 0x1aa038eb, 0x0e3bfd17, 0x1bd62080,},
-{ 0x1a593062, 0xb38d8c56, 0x7998ab45,},
-{ 0x1a15f4c3, 0x2b95a2e6, 0x46aed6a0,},
-{ 0x19d630dc, 0xcc7ddef9, 0x5aadd61b,},
-{ 0x19999999, 0x99999999, 0x9999999a,},
-{ 0x195fec80, 0x8a609430, 0xe1106014,},
-{ 0x1928ee7b, 0x0b4f22f9, 0x5f69791d,},
-{ 0x18f46acf, 0x8c06e318, 0x4d2aeb2c,},
-{ 0x18c23246, 0xdc0a9f3d, 0x3fe16970,},
-#endif
-};
-
-static const limb_t log2_radix[BF_RADIX_MAX - 1] = {
-#if LIMB_BITS == 32
-0x20000000,
-0x32b80347,
-0x40000000,
-0x4a4d3c26,
-0x52b80347,
-0x59d5d9fd,
-0x60000000,
-0x6570068e,
-0x6a4d3c26,
-0x6eb3a9f0,
-0x72b80347,
-0x766a008e,
-0x79d5d9fd,
-0x7d053f6d,
-0x80000000,
-0x82cc7edf,
-0x8570068e,
-0x87ef05ae,
-0x8a4d3c26,
-0x8c8ddd45,
-0x8eb3a9f0,
-0x90c10501,
-0x92b80347,
-0x949a784c,
-0x966a008e,
-0x982809d6,
-0x99d5d9fd,
-0x9b74948f,
-0x9d053f6d,
-0x9e88c6b3,
-0xa0000000,
-0xa16bad37,
-0xa2cc7edf,
-0xa4231623,
-0xa570068e,
-#else
-0x2000000000000000,
-0x32b803473f7ad0f4,
-0x4000000000000000,
-0x4a4d3c25e68dc57f,
-0x52b803473f7ad0f4,
-0x59d5d9fd5010b366,
-0x6000000000000000,
-0x6570068e7ef5a1e8,
-0x6a4d3c25e68dc57f,
-0x6eb3a9f01975077f,
-0x72b803473f7ad0f4,
-0x766a008e4788cbcd,
-0x79d5d9fd5010b366,
-0x7d053f6d26089673,
-0x8000000000000000,
-0x82cc7edf592262d0,
-0x8570068e7ef5a1e8,
-0x87ef05ae409a0289,
-0x8a4d3c25e68dc57f,
-0x8c8ddd448f8b845a,
-0x8eb3a9f01975077f,
-0x90c10500d63aa659,
-0x92b803473f7ad0f4,
-0x949a784bcd1b8afe,
-0x966a008e4788cbcd,
-0x982809d5be7072dc,
-0x99d5d9fd5010b366,
-0x9b74948f5532da4b,
-0x9d053f6d26089673,
-0x9e88c6b3626a72aa,
-0xa000000000000000,
-0xa16bad3758efd873,
-0xa2cc7edf592262d0,
-0xa4231623369e78e6,
-0xa570068e7ef5a1e8,
-#endif
-};
-
-/* compute floor(a*b) or ceil(a*b) with b = log2(radix) or
- b=1/log2(radix). For is_inv = 0, strict accuracy is not guaranteed
- when radix is not a power of two. */
-slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv,
- int is_ceil1)
-{
- int is_neg;
- limb_t a;
- BOOL is_ceil;
-
- is_ceil = is_ceil1;
- a = a1;
- if (a1 < 0) {
- a = -a;
- is_neg = 1;
- } else {
- is_neg = 0;
- }
- is_ceil ^= is_neg;
- if ((radix & (radix - 1)) == 0) {
- int radix_bits;
- /* radix is a power of two */
- radix_bits = ceil_log2(radix);
- if (is_inv) {
- if (is_ceil)
- a += radix_bits - 1;
- a = a / radix_bits;
- } else {
- a = a * radix_bits;
- }
- } else {
- const uint32_t *tab;
- limb_t b0, b1;
- dlimb_t t;
-
- if (is_inv) {
- tab = inv_log2_radix[radix - 2];
-#if LIMB_BITS == 32
- b1 = tab[0];
- b0 = tab[1];
-#else
- b1 = ((limb_t)tab[0] << 32) | tab[1];
- b0 = (limb_t)tab[2] << 32;
-#endif
- t = (dlimb_t)b0 * (dlimb_t)a;
- t = (dlimb_t)b1 * (dlimb_t)a + (t >> LIMB_BITS);
- a = t >> (LIMB_BITS - 1);
- } else {
- b0 = log2_radix[radix - 2];
- t = (dlimb_t)b0 * (dlimb_t)a;
- a = t >> (LIMB_BITS - 3);
- }
- /* a = floor(result) and 'result' cannot be an integer */
- a += is_ceil;
- }
- if (is_neg)
- a = -a;
- return a;
-}
-
-/* 'n' is the number of output limbs */
-static int bf_integer_to_radix_rec(bf_t *pow_tab,
- limb_t *out, const bf_t *a, limb_t n,
- int level, limb_t n0, limb_t radixl,
- unsigned int radixl_bits)
-{
- limb_t n1, n2, q_prec;
- int ret;
-
- assert(n >= 1);
- if (n == 1) {
- out[0] = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn);
- } else if (n == 2) {
- dlimb_t t;
- slimb_t pos;
- pos = a->len * LIMB_BITS - a->expn;
- t = ((dlimb_t)get_bits(a->tab, a->len, pos + LIMB_BITS) << LIMB_BITS) |
- get_bits(a->tab, a->len, pos);
- if (likely(radixl == RADIXL_10)) {
- /* use division by a constant when possible */
- out[0] = t % RADIXL_10;
- out[1] = t / RADIXL_10;
- } else {
- out[0] = t % radixl;
- out[1] = t / radixl;
- }
- } else {
- bf_t Q, R, *B, *B_inv;
- int q_add;
- bf_init(a->ctx, &Q);
- bf_init(a->ctx, &R);
- n2 = (((n0 * 2) >> (level + 1)) + 1) / 2;
- n1 = n - n2;
- B = &pow_tab[2 * level];
- B_inv = &pow_tab[2 * level + 1];
- ret = 0;
- if (B->len == 0) {
- /* compute BASE^n2 */
- ret |= bf_pow_ui_ui(B, radixl, n2, BF_PREC_INF, BF_RNDZ);
- /* we use enough bits for the maximum possible 'n1' value,
- i.e. n2 + 1 */
- ret |= bf_set_ui(&R, 1);
- ret |= bf_div(B_inv, &R, B, (n2 + 1) * radixl_bits + 2, BF_RNDN);
- }
- // printf("%d: n1=% " PRId64 " n2=%" PRId64 "\n", level, n1, n2);
- q_prec = n1 * radixl_bits;
- ret |= bf_mul(&Q, a, B_inv, q_prec, BF_RNDN);
- ret |= bf_rint(&Q, BF_RNDZ);
-
- ret |= bf_mul(&R, &Q, B, BF_PREC_INF, BF_RNDZ);
- ret |= bf_sub(&R, a, &R, BF_PREC_INF, BF_RNDZ);
-
- if (ret & BF_ST_MEM_ERROR)
- goto fail;
- /* adjust if necessary */
- q_add = 0;
- while (R.sign && R.len != 0) {
- if (bf_add(&R, &R, B, BF_PREC_INF, BF_RNDZ))
- goto fail;
- q_add--;
- }
- while (bf_cmpu(&R, B) >= 0) {
- if (bf_sub(&R, &R, B, BF_PREC_INF, BF_RNDZ))
- goto fail;
- q_add++;
- }
- if (q_add != 0) {
- if (bf_add_si(&Q, &Q, q_add, BF_PREC_INF, BF_RNDZ))
- goto fail;
- }
- if (bf_integer_to_radix_rec(pow_tab, out + n2, &Q, n1, level + 1, n0,
- radixl, radixl_bits))
- goto fail;
- if (bf_integer_to_radix_rec(pow_tab, out, &R, n2, level + 1, n0,
- radixl, radixl_bits)) {
- fail:
- bf_delete(&Q);
- bf_delete(&R);
- return -1;
- }
- bf_delete(&Q);
- bf_delete(&R);
- }
- return 0;
-}
-
-/* return 0 if OK != 0 if memory error */
-static int bf_integer_to_radix(bf_t *r, const bf_t *a, limb_t radixl)
-{
- bf_context_t *s = r->ctx;
- limb_t r_len;
- bf_t *pow_tab;
- int i, pow_tab_len, ret;
-
- r_len = r->len;
- pow_tab_len = (ceil_log2(r_len) + 2) * 2; /* XXX: check */
- pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len);
- if (!pow_tab)
- return -1;
- for(i = 0; i < pow_tab_len; i++)
- bf_init(r->ctx, &pow_tab[i]);
-
- ret = bf_integer_to_radix_rec(pow_tab, r->tab, a, r_len, 0, r_len, radixl,
- ceil_log2(radixl));
-
- for(i = 0; i < pow_tab_len; i++) {
- bf_delete(&pow_tab[i]);
- }
- bf_free(s, pow_tab);
- return ret;
-}
-
-/* a must be >= 0. 'P' is the wanted number of digits in radix
- 'radix'. 'r' is the mantissa represented as an integer. *pE
- contains the exponent. Return != 0 if memory error. */
-static int bf_convert_to_radix(bf_t *r, slimb_t *pE,
- const bf_t *a, int radix,
- limb_t P, bf_rnd_t rnd_mode,
- BOOL is_fixed_exponent)
-{
- slimb_t E, e, prec, extra_bits, ziv_extra_bits, prec0;
- bf_t B_s, *B = &B_s;
- int e_sign, ret, res;
-
- if (a->len == 0) {
- /* zero case */
- *pE = 0;
- return bf_set(r, a);
- }
-
- if (is_fixed_exponent) {
- E = *pE;
- } else {
- /* compute the new exponent */
- E = 1 + bf_mul_log2_radix(a->expn - 1, radix, TRUE, FALSE);
- }
- // bf_print_str("a", a);
- // printf("E=%ld P=%ld radix=%d\n", E, P, radix);
-
- for(;;) {
- e = P - E;
- e_sign = 0;
- if (e < 0) {
- e = -e;
- e_sign = 1;
- }
- /* Note: precision for log2(radix) is not critical here */
- prec0 = bf_mul_log2_radix(P, radix, FALSE, TRUE);
- ziv_extra_bits = 16;
- for(;;) {
- prec = prec0 + ziv_extra_bits;
- /* XXX: rigorous error analysis needed */
- extra_bits = ceil_log2(e) * 2 + 1;
- ret = bf_pow_ui_ui(r, radix, e, prec + extra_bits,
- BF_RNDN | BF_FLAG_EXT_EXP);
- if (!e_sign)
- ret |= bf_mul(r, r, a, prec + extra_bits,
- BF_RNDN | BF_FLAG_EXT_EXP);
- else
- ret |= bf_div(r, a, r, prec + extra_bits,
- BF_RNDN | BF_FLAG_EXT_EXP);
- if (ret & BF_ST_MEM_ERROR)
- return BF_ST_MEM_ERROR;
- /* if the result is not exact, check that it can be safely
- rounded to an integer */
- if ((ret & BF_ST_INEXACT) &&
- !bf_can_round(r, r->expn, rnd_mode, prec)) {
- /* and more precision and retry */
- ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2);
- continue;
- } else {
- ret = bf_rint(r, rnd_mode);
- if (ret & BF_ST_MEM_ERROR)
- return BF_ST_MEM_ERROR;
- break;
- }
- }
- if (is_fixed_exponent)
- break;
- /* check that the result is < B^P */
- /* XXX: do a fast approximate test first ? */
- bf_init(r->ctx, B);
- ret = bf_pow_ui_ui(B, radix, P, BF_PREC_INF, BF_RNDZ);
- if (ret) {
- bf_delete(B);
- return ret;
- }
- res = bf_cmpu(r, B);
- bf_delete(B);
- if (res < 0)
- break;
- /* try a larger exponent */
- E++;
- }
- *pE = E;
- return 0;
-}
-
-static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len)
-{
- int digit, i;
-
- if (radix == 10) {
- /* specific case with constant divisor */
- for(i = len - 1; i >= 0; i--) {
- digit = (limb_t)n % 10;
- n = (limb_t)n / 10;
- buf[i] = digit + '0';
- }
- } else {
- for(i = len - 1; i >= 0; i--) {
- digit = (limb_t)n % radix;
- n = (limb_t)n / radix;
- if (digit < 10)
- digit += '0';
- else
- digit += 'a' - 10;
- buf[i] = digit;
- }
- }
-}
-
-/* for power of 2 radixes */
-static void limb_to_a2(char *buf, limb_t n, unsigned int radix_bits, int len)
-{
- int digit, i;
- unsigned int mask;
-
- mask = (1 << radix_bits) - 1;
- for(i = len - 1; i >= 0; i--) {
- digit = n & mask;
- n >>= radix_bits;
- if (digit < 10)
- digit += '0';
- else
- digit += 'a' - 10;
- buf[i] = digit;
- }
-}
-
-/* 'a' must be an integer if the is_dec = FALSE or if the radix is not
- a power of two. A dot is added before the 'dot_pos' digit. dot_pos
- = n_digits does not display the dot. 0 <= dot_pos <=
- n_digits. n_digits >= 1. */
-static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits,
- limb_t dot_pos, BOOL is_dec)
-{
- limb_t i, v, l;
- slimb_t pos, pos_incr;
- int digits_per_limb, buf_pos, radix_bits, first_buf_pos;
- char buf[65];
- bf_t a_s, *a;
-
- if (is_dec) {
- digits_per_limb = LIMB_DIGITS;
- a = (bf_t *)a1;
- radix_bits = 0;
- pos = a->len;
- pos_incr = 1;
- first_buf_pos = 0;
- } else if ((radix & (radix - 1)) == 0) {
- a = (bf_t *)a1;
- radix_bits = ceil_log2(radix);
- digits_per_limb = LIMB_BITS / radix_bits;
- pos_incr = digits_per_limb * radix_bits;
- /* digits are aligned relative to the radix point */
- pos = a->len * LIMB_BITS + smod(-a->expn, radix_bits);
- first_buf_pos = 0;
- } else {
- limb_t n, radixl;
-
- digits_per_limb = digits_per_limb_table[radix - 2];
- radixl = get_limb_radix(radix);
- a = &a_s;
- bf_init(a1->ctx, a);
- n = (n_digits + digits_per_limb - 1) / digits_per_limb;
- if (bf_resize(a, n)) {
- dbuf_set_error(s);
- goto done;
- }
- if (bf_integer_to_radix(a, a1, radixl)) {
- dbuf_set_error(s);
- goto done;
- }
- radix_bits = 0;
- pos = n;
- pos_incr = 1;
- first_buf_pos = pos * digits_per_limb - n_digits;
- }
- buf_pos = digits_per_limb;
- i = 0;
- while (i < n_digits) {
- if (buf_pos == digits_per_limb) {
- pos -= pos_incr;
- if (radix_bits == 0) {
- v = get_limbz(a, pos);
- limb_to_a(buf, v, radix, digits_per_limb);
- } else {
- v = get_bits(a->tab, a->len, pos);
- limb_to_a2(buf, v, radix_bits, digits_per_limb);
- }
- buf_pos = first_buf_pos;
- first_buf_pos = 0;
- }
- if (i < dot_pos) {
- l = dot_pos;
- } else {
- if (i == dot_pos)
- dbuf_putc(s, '.');
- l = n_digits;
- }
- l = bf_min(digits_per_limb - buf_pos, l - i);
- dbuf_put(s, (uint8_t *)(buf + buf_pos), l);
- buf_pos += l;
- i += l;
- }
- done:
- if (a != a1)
- bf_delete(a);
-}
-
-static void *bf_dbuf_realloc(void *opaque, void *ptr, size_t size)
-{
- bf_context_t *s = opaque;
- return bf_realloc(s, ptr, size);
-}
-
-/* return the length in bytes. A trailing '\0' is added */
-static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix,
- limb_t prec, bf_flags_t flags, BOOL is_dec)
-{
- bf_context_t *ctx = a2->ctx;
- DynBuf s_s, *s = &s_s;
- int radix_bits;
-
- // bf_print_str("ftoa", a2);
- // printf("radix=%d\n", radix);
- dbuf_init2(s, ctx, bf_dbuf_realloc);
- if (a2->expn == BF_EXP_NAN) {
- dbuf_putstr(s, "NaN");
- } else {
- if (a2->sign)
- dbuf_putc(s, '-');
- if (a2->expn == BF_EXP_INF) {
- if (flags & BF_FTOA_JS_QUIRKS)
- dbuf_putstr(s, "Infinity");
- else
- dbuf_putstr(s, "Inf");
- } else {
- int fmt, ret;
- slimb_t n_digits, n, i, n_max, n1;
- bf_t a1_s, *a1 = &a1_s;
-
- if ((radix & (radix - 1)) != 0)
- radix_bits = 0;
- else
- radix_bits = ceil_log2(radix);
-
- fmt = flags & BF_FTOA_FORMAT_MASK;
- bf_init(ctx, a1);
- if (fmt == BF_FTOA_FORMAT_FRAC) {
- if (is_dec || radix_bits != 0) {
- if (bf_set(a1, a2))
- goto fail1;
-#ifdef USE_BF_DEC
- if (is_dec) {
- if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR)
- goto fail1;
- n = a1->expn;
- } else
-#endif
- {
- if (bf_round(a1, prec * radix_bits, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR)
- goto fail1;
- n = ceil_div(a1->expn, radix_bits);
- }
- if (flags & BF_FTOA_ADD_PREFIX) {
- if (radix == 16)
- dbuf_putstr(s, "0x");
- else if (radix == 8)
- dbuf_putstr(s, "0o");
- else if (radix == 2)
- dbuf_putstr(s, "0b");
- }
- if (a1->expn == BF_EXP_ZERO) {
- dbuf_putstr(s, "0");
- if (prec > 0) {
- dbuf_putstr(s, ".");
- for(i = 0; i < prec; i++) {
- dbuf_putc(s, '0');
- }
- }
- } else {
- n_digits = prec + n;
- if (n <= 0) {
- /* 0.x */
- dbuf_putstr(s, "0.");
- for(i = 0; i < -n; i++) {
- dbuf_putc(s, '0');
- }
- if (n_digits > 0) {
- output_digits(s, a1, radix, n_digits, n_digits, is_dec);
- }
- } else {
- output_digits(s, a1, radix, n_digits, n, is_dec);
- }
- }
- } else {
- size_t pos, start;
- bf_t a_s, *a = &a_s;
-
- /* make a positive number */
- a->tab = a2->tab;
- a->len = a2->len;
- a->expn = a2->expn;
- a->sign = 0;
-
- /* one more digit for the rounding */
- n = 1 + bf_mul_log2_radix(bf_max(a->expn, 0), radix, TRUE, TRUE);
- n_digits = n + prec;
- n1 = n;
- if (bf_convert_to_radix(a1, &n1, a, radix, n_digits,
- flags & BF_RND_MASK, TRUE))
- goto fail1;
- start = s->size;
- output_digits(s, a1, radix, n_digits, n, is_dec);
- /* remove leading zeros because we allocated one more digit */
- pos = start;
- while ((pos + 1) < s->size && s->buf[pos] == '0' &&
- s->buf[pos + 1] != '.')
- pos++;
- if (pos > start) {
- memmove(s->buf + start, s->buf + pos, s->size - pos);
- s->size -= (pos - start);
- }
- }
- } else {
-#ifdef USE_BF_DEC
- if (is_dec) {
- if (bf_set(a1, a2))
- goto fail1;
- if (fmt == BF_FTOA_FORMAT_FIXED) {
- n_digits = prec;
- n_max = n_digits;
- if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR)
- goto fail1;
- } else {
- /* prec is ignored */
- prec = n_digits = a1->len * LIMB_DIGITS;
- /* remove the trailing zero digits */
- while (n_digits > 1 &&
- get_digit(a1->tab, a1->len, prec - n_digits) == 0) {
- n_digits--;
- }
- n_max = n_digits + 4;
- }
- n = a1->expn;
- } else
-#endif
- if (radix_bits != 0) {
- if (bf_set(a1, a2))
- goto fail1;
- if (fmt == BF_FTOA_FORMAT_FIXED) {
- slimb_t prec_bits;
- n_digits = prec;
- n_max = n_digits;
- /* align to the radix point */
- prec_bits = prec * radix_bits -
- smod(-a1->expn, radix_bits);
- if (bf_round(a1, prec_bits,
- (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR)
- goto fail1;
- } else {
- limb_t digit_mask;
- slimb_t pos;
- /* position of the digit before the most
- significant digit in bits */
- pos = a1->len * LIMB_BITS +
- smod(-a1->expn, radix_bits);
- n_digits = ceil_div(pos, radix_bits);
- /* remove the trailing zero digits */
- digit_mask = ((limb_t)1 << radix_bits) - 1;
- while (n_digits > 1 &&
- (get_bits(a1->tab, a1->len, pos - n_digits * radix_bits) & digit_mask) == 0) {
- n_digits--;
- }
- n_max = n_digits + 4;
- }
- n = ceil_div(a1->expn, radix_bits);
- } else {
- bf_t a_s, *a = &a_s;
-
- /* make a positive number */
- a->tab = a2->tab;
- a->len = a2->len;
- a->expn = a2->expn;
- a->sign = 0;
-
- if (fmt == BF_FTOA_FORMAT_FIXED) {
- n_digits = prec;
- n_max = n_digits;
- } else {
- slimb_t n_digits_max, n_digits_min;
-
- assert(prec != BF_PREC_INF);
- n_digits = 1 + bf_mul_log2_radix(prec, radix, TRUE, TRUE);
- /* max number of digits for non exponential
- notation. The rational is to have the same rule
- as JS i.e. n_max = 21 for 64 bit float in base 10. */
- n_max = n_digits + 4;
- if (fmt == BF_FTOA_FORMAT_FREE_MIN) {
- bf_t b_s, *b = &b_s;
-
- /* find the minimum number of digits by
- dichotomy. */
- /* XXX: inefficient */
- n_digits_max = n_digits;
- n_digits_min = 1;
- bf_init(ctx, b);
- while (n_digits_min < n_digits_max) {
- n_digits = (n_digits_min + n_digits_max) / 2;
- if (bf_convert_to_radix(a1, &n, a, radix, n_digits,
- flags & BF_RND_MASK, FALSE)) {
- bf_delete(b);
- goto fail1;
- }
- /* convert back to a number and compare */
- ret = bf_mul_pow_radix(b, a1, radix, n - n_digits,
- prec,
- (flags & ~BF_RND_MASK) |
- BF_RNDN);
- if (ret & BF_ST_MEM_ERROR) {
- bf_delete(b);
- goto fail1;
- }
- if (bf_cmpu(b, a) == 0) {
- n_digits_max = n_digits;
- } else {
- n_digits_min = n_digits + 1;
- }
- }
- bf_delete(b);
- n_digits = n_digits_max;
- }
- }
- if (bf_convert_to_radix(a1, &n, a, radix, n_digits,
- flags & BF_RND_MASK, FALSE)) {
- fail1:
- bf_delete(a1);
- goto fail;
- }
- }
- if (a1->expn == BF_EXP_ZERO &&
- fmt != BF_FTOA_FORMAT_FIXED &&
- !(flags & BF_FTOA_FORCE_EXP)) {
- /* just output zero */
- dbuf_putstr(s, "0");
- } else {
- if (flags & BF_FTOA_ADD_PREFIX) {
- if (radix == 16)
- dbuf_putstr(s, "0x");
- else if (radix == 8)
- dbuf_putstr(s, "0o");
- else if (radix == 2)
- dbuf_putstr(s, "0b");
- }
- if (a1->expn == BF_EXP_ZERO)
- n = 1;
- if ((flags & BF_FTOA_FORCE_EXP) ||
- n <= -6 || n > n_max) {
- const char *fmt;
- /* exponential notation */
- output_digits(s, a1, radix, n_digits, 1, is_dec);
- if (radix_bits != 0 && radix <= 16) {
- if (flags & BF_FTOA_JS_QUIRKS)
- fmt = "p%+" PRId_LIMB;
- else
- fmt = "p%" PRId_LIMB;
- dbuf_printf(s, fmt, (n - 1) * radix_bits);
- } else {
- if (flags & BF_FTOA_JS_QUIRKS)
- fmt = "%c%+" PRId_LIMB;
- else
- fmt = "%c%" PRId_LIMB;
- dbuf_printf(s, fmt,
- radix <= 10 ? 'e' : '@', n - 1);
- }
- } else if (n <= 0) {
- /* 0.x */
- dbuf_putstr(s, "0.");
- for(i = 0; i < -n; i++) {
- dbuf_putc(s, '0');
- }
- output_digits(s, a1, radix, n_digits, n_digits, is_dec);
- } else {
- if (n_digits <= n) {
- /* no dot */
- output_digits(s, a1, radix, n_digits, n_digits, is_dec);
- for(i = 0; i < (n - n_digits); i++)
- dbuf_putc(s, '0');
- } else {
- output_digits(s, a1, radix, n_digits, n, is_dec);
- }
- }
- }
- }
- bf_delete(a1);
- }
- }
- dbuf_putc(s, '\0');
- if (dbuf_error(s))
- goto fail;
- if (plen)
- *plen = s->size - 1;
- return (char *)s->buf;
- fail:
- bf_free(ctx, s->buf);
- if (plen)
- *plen = 0;
- return NULL;
-}
-
-char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec,
- bf_flags_t flags)
-{
- return bf_ftoa_internal(plen, a, radix, prec, flags, FALSE);
-}
-
-/***************************************************************/
-/* transcendental functions */
-
-/* Note: the algorithm is from MPFR */
-static void bf_const_log2_rec(bf_t *T, bf_t *P, bf_t *Q, limb_t n1,
- limb_t n2, BOOL need_P)
-{
- bf_context_t *s = T->ctx;
- if ((n2 - n1) == 1) {
- if (n1 == 0) {
- bf_set_ui(P, 3);
- } else {
- bf_set_ui(P, n1);
- P->sign = 1;
- }
- bf_set_ui(Q, 2 * n1 + 1);
- Q->expn += 2;
- bf_set(T, P);
- } else {
- limb_t m;
- bf_t T1_s, *T1 = &T1_s;
- bf_t P1_s, *P1 = &P1_s;
- bf_t Q1_s, *Q1 = &Q1_s;
-
- m = n1 + ((n2 - n1) >> 1);
- bf_const_log2_rec(T, P, Q, n1, m, TRUE);
- bf_init(s, T1);
- bf_init(s, P1);
- bf_init(s, Q1);
- bf_const_log2_rec(T1, P1, Q1, m, n2, need_P);
- bf_mul(T, T, Q1, BF_PREC_INF, BF_RNDZ);
- bf_mul(T1, T1, P, BF_PREC_INF, BF_RNDZ);
- bf_add(T, T, T1, BF_PREC_INF, BF_RNDZ);
- if (need_P)
- bf_mul(P, P, P1, BF_PREC_INF, BF_RNDZ);
- bf_mul(Q, Q, Q1, BF_PREC_INF, BF_RNDZ);
- bf_delete(T1);
- bf_delete(P1);
- bf_delete(Q1);
- }
-}
-
-/* compute log(2) with faithful rounding at precision 'prec' */
-static void bf_const_log2_internal(bf_t *T, limb_t prec)
-{
- limb_t w, N;
- bf_t P_s, *P = &P_s;
- bf_t Q_s, *Q = &Q_s;
-
- w = prec + 15;
- N = w / 3 + 1;
- bf_init(T->ctx, P);
- bf_init(T->ctx, Q);
- bf_const_log2_rec(T, P, Q, 0, N, FALSE);
- bf_div(T, T, Q, prec, BF_RNDN);
- bf_delete(P);
- bf_delete(Q);
-}
-
-/* PI constant */
-
-#define CHUD_A 13591409
-#define CHUD_B 545140134
-#define CHUD_C 640320
-#define CHUD_BITS_PER_TERM 47
-
-static void chud_bs(bf_t *P, bf_t *Q, bf_t *G, int64_t a, int64_t b, int need_g,
- limb_t prec)
-{
- bf_context_t *s = P->ctx;
- int64_t c;
-
- if (a == (b - 1)) {
- bf_t T0, T1;
-
- bf_init(s, &T0);
- bf_init(s, &T1);
- bf_set_ui(G, 2 * b - 1);
- bf_mul_ui(G, G, 6 * b - 1, prec, BF_RNDN);
- bf_mul_ui(G, G, 6 * b - 5, prec, BF_RNDN);
- bf_set_ui(&T0, CHUD_B);
- bf_mul_ui(&T0, &T0, b, prec, BF_RNDN);
- bf_set_ui(&T1, CHUD_A);
- bf_add(&T0, &T0, &T1, prec, BF_RNDN);
- bf_mul(P, G, &T0, prec, BF_RNDN);
- P->sign = b & 1;
-
- bf_set_ui(Q, b);
- bf_mul_ui(Q, Q, b, prec, BF_RNDN);
- bf_mul_ui(Q, Q, b, prec, BF_RNDN);
- bf_mul_ui(Q, Q, (uint64_t)CHUD_C * CHUD_C * CHUD_C / 24, prec, BF_RNDN);
- bf_delete(&T0);
- bf_delete(&T1);
- } else {
- bf_t P2, Q2, G2;
-
- bf_init(s, &P2);
- bf_init(s, &Q2);
- bf_init(s, &G2);
-
- c = (a + b) / 2;
- chud_bs(P, Q, G, a, c, 1, prec);
- chud_bs(&P2, &Q2, &G2, c, b, need_g, prec);
-
- /* Q = Q1 * Q2 */
- /* G = G1 * G2 */
- /* P = P1 * Q2 + P2 * G1 */
- bf_mul(&P2, &P2, G, prec, BF_RNDN);
- if (!need_g)
- bf_set_ui(G, 0);
- bf_mul(P, P, &Q2, prec, BF_RNDN);
- bf_add(P, P, &P2, prec, BF_RNDN);
- bf_delete(&P2);
-
- bf_mul(Q, Q, &Q2, prec, BF_RNDN);
- bf_delete(&Q2);
- if (need_g)
- bf_mul(G, G, &G2, prec, BF_RNDN);
- bf_delete(&G2);
- }
-}
-
-/* compute Pi with faithful rounding at precision 'prec' using the
- Chudnovsky formula */
-static void bf_const_pi_internal(bf_t *Q, limb_t prec)
-{
- bf_context_t *s = Q->ctx;
- int64_t n, prec1;
- bf_t P, G;
-
- /* number of serie terms */
- n = prec / CHUD_BITS_PER_TERM + 1;
- /* XXX: precision analysis */
- prec1 = prec + 32;
-
- bf_init(s, &P);
- bf_init(s, &G);
-
- chud_bs(&P, Q, &G, 0, n, 0, BF_PREC_INF);
-
- bf_mul_ui(&G, Q, CHUD_A, prec1, BF_RNDN);
- bf_add(&P, &G, &P, prec1, BF_RNDN);
- bf_div(Q, Q, &P, prec1, BF_RNDF);
-
- bf_set_ui(&P, CHUD_C);
- bf_sqrt(&G, &P, prec1, BF_RNDF);
- bf_mul_ui(&G, &G, (uint64_t)CHUD_C / 12, prec1, BF_RNDF);
- bf_mul(Q, Q, &G, prec, BF_RNDN);
- bf_delete(&P);
- bf_delete(&G);
-}
-
-static int bf_const_get(bf_t *T, limb_t prec, bf_flags_t flags,
- BFConstCache *c,
- void (*func)(bf_t *res, limb_t prec), int sign)
-{
- limb_t ziv_extra_bits, prec1;
-
- ziv_extra_bits = 32;
- for(;;) {
- prec1 = prec + ziv_extra_bits;
- if (c->prec < prec1) {
- if (c->val.len == 0)
- bf_init(T->ctx, &c->val);
- func(&c->val, prec1);
- c->prec = prec1;
- } else {
- prec1 = c->prec;
- }
- bf_set(T, &c->val);
- T->sign = sign;
- if (!bf_can_round(T, prec, flags & BF_RND_MASK, prec1)) {
- /* and more precision and retry */
- ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2);
- } else {
- break;
- }
- }
- return bf_round(T, prec, flags);
-}
-
-static void bf_const_free(BFConstCache *c)
-{
- bf_delete(&c->val);
- memset(c, 0, sizeof(*c));
-}
-
-int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags)
-{
- bf_context_t *s = T->ctx;
- return bf_const_get(T, prec, flags, &s->log2_cache, bf_const_log2_internal, 0);
-}
-
-/* return rounded pi * (1 - 2 * sign) */
-static int bf_const_pi_signed(bf_t *T, int sign, limb_t prec, bf_flags_t flags)
-{
- bf_context_t *s = T->ctx;
- return bf_const_get(T, prec, flags, &s->pi_cache, bf_const_pi_internal,
- sign);
-}
-
-int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags)
-{
- return bf_const_pi_signed(T, 0, prec, flags);
-}
-
-void bf_clear_cache(bf_context_t *s)
-{
-#ifdef USE_FFT_MUL
- fft_clear_cache(s);
-#endif
- bf_const_free(&s->log2_cache);
- bf_const_free(&s->pi_cache);
-}
-
-/* ZivFunc should compute the result 'r' with faithful rounding at
- precision 'prec'. For efficiency purposes, the final bf_round()
- does not need to be done in the function. */
-typedef int ZivFunc(bf_t *r, const bf_t *a, limb_t prec, void *opaque);
-
-static int bf_ziv_rounding(bf_t *r, const bf_t *a,
- limb_t prec, bf_flags_t flags,
- ZivFunc *f, void *opaque)
-{
- int rnd_mode, ret;
- slimb_t prec1, ziv_extra_bits;
-
- rnd_mode = flags & BF_RND_MASK;
- if (rnd_mode == BF_RNDF) {
- /* no need to iterate */
- f(r, a, prec, opaque);
- ret = 0;
- } else {
- ziv_extra_bits = 32;
- for(;;) {
- prec1 = prec + ziv_extra_bits;
- ret = f(r, a, prec1, opaque);
- if (ret & (BF_ST_OVERFLOW | BF_ST_UNDERFLOW | BF_ST_MEM_ERROR)) {
- /* overflow or underflow should never happen because
- it indicates the rounding cannot be done correctly,
- but we do not catch all the cases */
- return ret;
- }
- /* if the result is exact, we can stop */
- if (!(ret & BF_ST_INEXACT)) {
- ret = 0;
- break;
- }
- if (bf_can_round(r, prec, rnd_mode, prec1)) {
- ret = BF_ST_INEXACT;
- break;
- }
- ziv_extra_bits = ziv_extra_bits * 2;
- // printf("ziv_extra_bits=%" PRId64 "\n", (int64_t)ziv_extra_bits);
- }
- }
- if (r->len == 0)
- return ret;
- else
- return __bf_round(r, prec, flags, r->len, ret);
-}
-
-/* add (1 - 2*e_sign) * 2^e */
-static int bf_add_epsilon(bf_t *r, const bf_t *a, slimb_t e, int e_sign,
- limb_t prec, int flags)
-{
- bf_t T_s, *T = &T_s;
- int ret;
- /* small argument case: result = 1 + epsilon * sign(x) */
- bf_init(a->ctx, T);
- bf_set_ui(T, 1);
- T->sign = e_sign;
- T->expn += e;
- ret = bf_add(r, r, T, prec, flags);
- bf_delete(T);
- return ret;
-}
-
-/* Compute the exponential using faithful rounding at precision 'prec'.
- Note: the algorithm is from MPFR */
-static int bf_exp_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
-{
- bf_context_t *s = r->ctx;
- bf_t T_s, *T = &T_s;
- slimb_t n, K, l, i, prec1;
-
- assert(r != a);
-
- /* argument reduction:
- T = a - n*log(2) with 0 <= T < log(2) and n integer.
- */
- bf_init(s, T);
- if (a->expn <= -1) {
- /* 0 <= abs(a) <= 0.5 */
- if (a->sign)
- n = -1;
- else
- n = 0;
- } else {
- bf_const_log2(T, LIMB_BITS, BF_RNDZ);
- bf_div(T, a, T, LIMB_BITS, BF_RNDD);
- bf_get_limb(&n, T, 0);
- }
-
- K = bf_isqrt((prec + 1) / 2);
- l = (prec - 1) / K + 1;
- /* XXX: precision analysis ? */
- prec1 = prec + (K + 2 * l + 18) + K + 8;
- if (a->expn > 0)
- prec1 += a->expn;
- // printf("n=%ld K=%ld prec1=%ld\n", n, K, prec1);
-
- bf_const_log2(T, prec1, BF_RNDF);
- bf_mul_si(T, T, n, prec1, BF_RNDN);
- bf_sub(T, a, T, prec1, BF_RNDN);
-
- /* reduce the range of T */
- bf_mul_2exp(T, -K, BF_PREC_INF, BF_RNDZ);
-
- /* Taylor expansion around zero :
- 1 + x + x^2/2 + ... + x^n/n!
- = (1 + x * (1 + x/2 * (1 + ... (x/n))))
- */
- {
- bf_t U_s, *U = &U_s;
-
- bf_init(s, U);
- bf_set_ui(r, 1);
- for(i = l ; i >= 1; i--) {
- bf_set_ui(U, i);
- bf_div(U, T, U, prec1, BF_RNDN);
- bf_mul(r, r, U, prec1, BF_RNDN);
- bf_add_si(r, r, 1, prec1, BF_RNDN);
- }
- bf_delete(U);
- }
- bf_delete(T);
-
- /* undo the range reduction */
- for(i = 0; i < K; i++) {
- bf_mul(r, r, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP);
- }
-
- /* undo the argument reduction */
- bf_mul_2exp(r, n, BF_PREC_INF, BF_RNDZ | BF_FLAG_EXT_EXP);
-
- return BF_ST_INEXACT;
-}
-
-/* crude overflow and underflow tests for exp(a). a_low <= a <= a_high */
-static int check_exp_underflow_overflow(bf_context_t *s, bf_t *r,
- const bf_t *a_low, const bf_t *a_high,
- limb_t prec, bf_flags_t flags)
-{
- bf_t T_s, *T = &T_s;
- bf_t log2_s, *log2 = &log2_s;
- slimb_t e_min, e_max;
-
- if (a_high->expn <= 0)
- return 0;
-
- e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1);
- e_min = -e_max + 3;
- if (flags & BF_FLAG_SUBNORMAL)
- e_min -= (prec - 1);
-
- bf_init(s, T);
- bf_init(s, log2);
- bf_const_log2(log2, LIMB_BITS, BF_RNDU);
- bf_mul_ui(T, log2, e_max, LIMB_BITS, BF_RNDU);
- /* a_low > e_max * log(2) implies exp(a) > e_max */
- if (bf_cmp_lt(T, a_low) > 0) {
- /* overflow */
- bf_delete(T);
- bf_delete(log2);
- return bf_set_overflow(r, 0, prec, flags);
- }
- /* a_high < (e_min - 2) * log(2) implies exp(a) < (e_min - 2) */
- bf_const_log2(log2, LIMB_BITS, BF_RNDD);
- bf_mul_si(T, log2, e_min - 2, LIMB_BITS, BF_RNDD);
- if (bf_cmp_lt(a_high, T)) {
- int rnd_mode = flags & BF_RND_MASK;
-
- /* underflow */
- bf_delete(T);
- bf_delete(log2);
- if (rnd_mode == BF_RNDU) {
- /* set the smallest value */
- bf_set_ui(r, 1);
- r->expn = e_min;
- } else {
- bf_set_zero(r, 0);
- }
- return BF_ST_UNDERFLOW | BF_ST_INEXACT;
- }
- bf_delete(log2);
- bf_delete(T);
- return 0;
-}
-
-int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
- bf_context_t *s = r->ctx;
- int ret;
- assert(r != a);
- if (a->len == 0) {
- if (a->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- } else if (a->expn == BF_EXP_INF) {
- if (a->sign)
- bf_set_zero(r, 0);
- else
- bf_set_inf(r, 0);
- } else {
- bf_set_ui(r, 1);
- }
- return 0;
- }
-
- ret = check_exp_underflow_overflow(s, r, a, a, prec, flags);
- if (ret)
- return ret;
- if (a->expn < 0 && (-a->expn) >= (prec + 2)) {
- /* small argument case: result = 1 + epsilon * sign(x) */
- bf_set_ui(r, 1);
- return bf_add_epsilon(r, r, -(prec + 2), a->sign, prec, flags);
- }
-
- return bf_ziv_rounding(r, a, prec, flags, bf_exp_internal, NULL);
-}
-
-static int bf_log_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
-{
- bf_context_t *s = r->ctx;
- bf_t T_s, *T = &T_s;
- bf_t U_s, *U = &U_s;
- bf_t V_s, *V = &V_s;
- slimb_t n, prec1, l, i, K;
-
- assert(r != a);
-
- bf_init(s, T);
- /* argument reduction 1 */
- /* T=a*2^n with 2/3 <= T <= 4/3 */
- {
- bf_t U_s, *U = &U_s;
- bf_set(T, a);
- n = T->expn;
- T->expn = 0;
- /* U= ~ 2/3 */
- bf_init(s, U);
- bf_set_ui(U, 0xaaaaaaaa);
- U->expn = 0;
- if (bf_cmp_lt(T, U)) {
- T->expn++;
- n--;
- }
- bf_delete(U);
- }
- // printf("n=%ld\n", n);
- // bf_print_str("T", T);
-
- /* XXX: precision analysis */
- /* number of iterations for argument reduction 2 */
- K = bf_isqrt((prec + 1) / 2);
- /* order of Taylor expansion */
- l = prec / (2 * K) + 1;
- /* precision of the intermediate computations */
- prec1 = prec + K + 2 * l + 32;
-
- bf_init(s, U);
- bf_init(s, V);
-
- /* Note: cancellation occurs here, so we use more precision (XXX:
- reduce the precision by computing the exact cancellation) */
- bf_add_si(T, T, -1, BF_PREC_INF, BF_RNDN);
-
- /* argument reduction 2 */
- for(i = 0; i < K; i++) {
- /* T = T / (1 + sqrt(1 + T)) */
- bf_add_si(U, T, 1, prec1, BF_RNDN);
- bf_sqrt(V, U, prec1, BF_RNDF);
- bf_add_si(U, V, 1, prec1, BF_RNDN);
- bf_div(T, T, U, prec1, BF_RNDN);
- }
-
- {
- bf_t Y_s, *Y = &Y_s;
- bf_t Y2_s, *Y2 = &Y2_s;
- bf_init(s, Y);
- bf_init(s, Y2);
-
- /* compute ln(1+x) = ln((1+y)/(1-y)) with y=x/(2+x)
- = y + y^3/3 + ... + y^(2*l + 1) / (2*l+1)
- with Y=Y^2
- = y*(1+Y/3+Y^2/5+...) = y*(1+Y*(1/3+Y*(1/5 + ...)))
- */
- bf_add_si(Y, T, 2, prec1, BF_RNDN);
- bf_div(Y, T, Y, prec1, BF_RNDN);
-
- bf_mul(Y2, Y, Y, prec1, BF_RNDN);
- bf_set_ui(r, 0);
- for(i = l; i >= 1; i--) {
- bf_set_ui(U, 1);
- bf_set_ui(V, 2 * i + 1);
- bf_div(U, U, V, prec1, BF_RNDN);
- bf_add(r, r, U, prec1, BF_RNDN);
- bf_mul(r, r, Y2, prec1, BF_RNDN);
- }
- bf_add_si(r, r, 1, prec1, BF_RNDN);
- bf_mul(r, r, Y, prec1, BF_RNDN);
- bf_delete(Y);
- bf_delete(Y2);
- }
- bf_delete(V);
- bf_delete(U);
-
- /* multiplication by 2 for the Taylor expansion and undo the
- argument reduction 2*/
- bf_mul_2exp(r, K + 1, BF_PREC_INF, BF_RNDZ);
-
- /* undo the argument reduction 1 */
- bf_const_log2(T, prec1, BF_RNDF);
- bf_mul_si(T, T, n, prec1, BF_RNDN);
- bf_add(r, r, T, prec1, BF_RNDN);
-
- bf_delete(T);
- return BF_ST_INEXACT;
-}
-
-int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
- bf_context_t *s = r->ctx;
- bf_t T_s, *T = &T_s;
-
- assert(r != a);
- if (a->len == 0) {
- if (a->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- return 0;
- } else if (a->expn == BF_EXP_INF) {
- if (a->sign) {
- bf_set_nan(r);
- return BF_ST_INVALID_OP;
- } else {
- bf_set_inf(r, 0);
- return 0;
- }
- } else {
- bf_set_inf(r, 1);
- return 0;
- }
- }
- if (a->sign) {
- bf_set_nan(r);
- return BF_ST_INVALID_OP;
- }
- bf_init(s, T);
- bf_set_ui(T, 1);
- if (bf_cmp_eq(a, T)) {
- bf_set_zero(r, 0);
- bf_delete(T);
- return 0;
- }
- bf_delete(T);
-
- return bf_ziv_rounding(r, a, prec, flags, bf_log_internal, NULL);
-}
-
-/* x and y finite and x > 0 */
-static int bf_pow_generic(bf_t *r, const bf_t *x, limb_t prec, void *opaque)
-{
- bf_context_t *s = r->ctx;
- const bf_t *y = opaque;
- bf_t T_s, *T = &T_s;
- limb_t prec1;
-
- bf_init(s, T);
- /* XXX: proof for the added precision */
- prec1 = prec + 32;
- bf_log(T, x, prec1, BF_RNDF | BF_FLAG_EXT_EXP);
- bf_mul(T, T, y, prec1, BF_RNDF | BF_FLAG_EXT_EXP);
- if (bf_is_nan(T))
- bf_set_nan(r);
- else
- bf_exp_internal(r, T, prec1, NULL); /* no overflow/underlow test needed */
- bf_delete(T);
- return BF_ST_INEXACT;
-}
-
-/* x and y finite, x > 0, y integer and y fits on one limb */
-static int bf_pow_int(bf_t *r, const bf_t *x, limb_t prec, void *opaque)
-{
- bf_context_t *s = r->ctx;
- const bf_t *y = opaque;
- bf_t T_s, *T = &T_s;
- limb_t prec1;
- int ret;
- slimb_t y1;
-
- bf_get_limb(&y1, y, 0);
- if (y1 < 0)
- y1 = -y1;
- /* XXX: proof for the added precision */
- prec1 = prec + ceil_log2(y1) * 2 + 8;
- ret = bf_pow_ui(r, x, y1 < 0 ? -y1 : y1, prec1, BF_RNDN | BF_FLAG_EXT_EXP);
- if (y->sign) {
- bf_init(s, T);
- bf_set_ui(T, 1);
- ret |= bf_div(r, T, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP);
- bf_delete(T);
- }
- return ret;
-}
-
-/* x must be a finite non zero float. Return TRUE if there is a
- floating point number r such as x=r^(2^n) and return this floating
- point number 'r'. Otherwise return FALSE and r is undefined. */
-static BOOL check_exact_power2n(bf_t *r, const bf_t *x, slimb_t n)
-{
- bf_context_t *s = r->ctx;
- bf_t T_s, *T = &T_s;
- slimb_t e, i, er;
- limb_t v;
-
- /* x = m*2^e with m odd integer */
- e = bf_get_exp_min(x);
- /* fast check on the exponent */
- if (n > (LIMB_BITS - 1)) {
- if (e != 0)
- return FALSE;
- er = 0;
- } else {
- if ((e & (((limb_t)1 << n) - 1)) != 0)
- return FALSE;
- er = e >> n;
- }
- /* every perfect odd square = 1 modulo 8 */
- v = get_bits(x->tab, x->len, x->len * LIMB_BITS - x->expn + e);
- if ((v & 7) != 1)
- return FALSE;
-
- bf_init(s, T);
- bf_set(T, x);
- T->expn -= e;
- for(i = 0; i < n; i++) {
- if (i != 0)
- bf_set(T, r);
- if (bf_sqrtrem(r, NULL, T) != 0)
- return FALSE;
- }
- r->expn += er;
- return TRUE;
-}
-
-/* prec = BF_PREC_INF is accepted for x and y integers and y >= 0 */
-int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags)
-{
- bf_context_t *s = r->ctx;
- bf_t T_s, *T = &T_s;
- bf_t ytmp_s;
- BOOL y_is_int, y_is_odd;
- int r_sign, ret, rnd_mode;
- slimb_t y_emin;
-
- if (x->len == 0 || y->len == 0) {
- if (y->expn == BF_EXP_ZERO) {
- /* pow(x, 0) = 1 */
- bf_set_ui(r, 1);
- } else if (x->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- } else {
- int cmp_x_abs_1;
- bf_set_ui(r, 1);
- cmp_x_abs_1 = bf_cmpu(x, r);
- if (cmp_x_abs_1 == 0 && (flags & BF_POW_JS_QUIRKS) &&
- (y->expn >= BF_EXP_INF)) {
- bf_set_nan(r);
- } else if (cmp_x_abs_1 == 0 &&
- (!x->sign || y->expn != BF_EXP_NAN)) {
- /* pow(1, y) = 1 even if y = NaN */
- /* pow(-1, +/-inf) = 1 */
- } else if (y->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- } else if (y->expn == BF_EXP_INF) {
- if (y->sign == (cmp_x_abs_1 > 0)) {
- bf_set_zero(r, 0);
- } else {
- bf_set_inf(r, 0);
- }
- } else {
- y_emin = bf_get_exp_min(y);
- y_is_odd = (y_emin == 0);
- if (y->sign == (x->expn == BF_EXP_ZERO)) {
- bf_set_inf(r, y_is_odd & x->sign);
- if (y->sign) {
- /* pow(0, y) with y < 0 */
- return BF_ST_DIVIDE_ZERO;
- }
- } else {
- bf_set_zero(r, y_is_odd & x->sign);
- }
- }
- }
- return 0;
- }
- bf_init(s, T);
- bf_set(T, x);
- y_emin = bf_get_exp_min(y);
- y_is_int = (y_emin >= 0);
- rnd_mode = flags & BF_RND_MASK;
- if (x->sign) {
- if (!y_is_int) {
- bf_set_nan(r);
- bf_delete(T);
- return BF_ST_INVALID_OP;
- }
- y_is_odd = (y_emin == 0);
- r_sign = y_is_odd;
- /* change the directed rounding mode if the sign of the result
- is changed */
- if (r_sign && (rnd_mode == BF_RNDD || rnd_mode == BF_RNDU))
- flags ^= 1;
- bf_neg(T);
- } else {
- r_sign = 0;
- }
-
- bf_set_ui(r, 1);
- if (bf_cmp_eq(T, r)) {
- /* abs(x) = 1: nothing more to do */
- ret = 0;
- } else {
- /* check the overflow/underflow cases */
- {
- bf_t al_s, *al = &al_s;
- bf_t ah_s, *ah = &ah_s;
- limb_t precl = LIMB_BITS;
-
- bf_init(s, al);
- bf_init(s, ah);
- /* compute bounds of log(abs(x)) * y with a low precision */
- /* XXX: compute bf_log() once */
- /* XXX: add a fast test before this slow test */
- bf_log(al, T, precl, BF_RNDD);
- bf_log(ah, T, precl, BF_RNDU);
- bf_mul(al, al, y, precl, BF_RNDD ^ y->sign);
- bf_mul(ah, ah, y, precl, BF_RNDU ^ y->sign);
- ret = check_exp_underflow_overflow(s, r, al, ah, prec, flags);
- bf_delete(al);
- bf_delete(ah);
- if (ret)
- goto done;
- }
-
- if (y_is_int) {
- slimb_t T_bits, e;
- int_pow:
- T_bits = T->expn - bf_get_exp_min(T);
- if (T_bits == 1) {
- /* pow(2^b, y) = 2^(b*y) */
- bf_mul_si(T, y, T->expn - 1, LIMB_BITS, BF_RNDZ);
- bf_get_limb(&e, T, 0);
- bf_set_ui(r, 1);
- ret = bf_mul_2exp(r, e, prec, flags);
- } else if (prec == BF_PREC_INF) {
- slimb_t y1;
- /* specific case for infinite precision (integer case) */
- bf_get_limb(&y1, y, 0);
- assert(!y->sign);
- /* x must be an integer, so abs(x) >= 2 */
- if (y1 >= ((slimb_t)1 << BF_EXP_BITS_MAX)) {
- bf_delete(T);
- return bf_set_overflow(r, 0, BF_PREC_INF, flags);
- }
- ret = bf_pow_ui(r, T, y1, BF_PREC_INF, BF_RNDZ);
- } else {
- if (y->expn <= 31) {
- /* small enough power: use exponentiation in all cases */
- } else if (y->sign) {
- /* cannot be exact */
- goto general_case;
- } else {
- if (rnd_mode == BF_RNDF)
- goto general_case; /* no need to track exact results */
- /* see if the result has a chance to be exact:
- if x=a*2^b (a odd), x^y=a^y*2^(b*y)
- x^y needs a precision of at least floor_log2(a)*y bits
- */
- bf_mul_si(r, y, T_bits - 1, LIMB_BITS, BF_RNDZ);
- bf_get_limb(&e, r, 0);
- if (prec < e)
- goto general_case;
- }
- ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_int, (void *)y);
- }
- } else {
- if (rnd_mode != BF_RNDF) {
- bf_t *y1;
- if (y_emin < 0 && check_exact_power2n(r, T, -y_emin)) {
- /* the problem is reduced to a power to an integer */
-#if 0
- printf("\nn=%" PRId64 "\n", -(int64_t)y_emin);
- bf_print_str("T", T);
- bf_print_str("r", r);
-#endif
- bf_set(T, r);
- y1 = &ytmp_s;
- y1->tab = y->tab;
- y1->len = y->len;
- y1->sign = y->sign;
- y1->expn = y->expn - y_emin;
- y = y1;
- goto int_pow;
- }
- }
- general_case:
- ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_generic, (void *)y);
- }
- }
- done:
- bf_delete(T);
- r->sign = r_sign;
- return ret;
-}
-
-/* compute sqrt(-2*x-x^2) to get |sin(x)| from cos(x) - 1. */
-static void bf_sqrt_sin(bf_t *r, const bf_t *x, limb_t prec1)
-{
- bf_context_t *s = r->ctx;
- bf_t T_s, *T = &T_s;
- bf_init(s, T);
- bf_set(T, x);
- bf_mul(r, T, T, prec1, BF_RNDN);
- bf_mul_2exp(T, 1, BF_PREC_INF, BF_RNDZ);
- bf_add(T, T, r, prec1, BF_RNDN);
- bf_neg(T);
- bf_sqrt(r, T, prec1, BF_RNDF);
- bf_delete(T);
-}
-
-static int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec)
-{
- bf_context_t *s1 = a->ctx;
- bf_t T_s, *T = &T_s;
- bf_t U_s, *U = &U_s;
- bf_t r_s, *r = &r_s;
- slimb_t K, prec1, i, l, mod, prec2;
- int is_neg;
-
- assert(c != a && s != a);
-
- bf_init(s1, T);
- bf_init(s1, U);
- bf_init(s1, r);
-
- /* XXX: precision analysis */
- K = bf_isqrt(prec / 2);
- l = prec / (2 * K) + 1;
- prec1 = prec + 2 * K + l + 8;
-
- /* after the modulo reduction, -pi/4 <= T <= pi/4 */
- if (a->expn <= -1) {
- /* abs(a) <= 0.25: no modulo reduction needed */
- bf_set(T, a);
- mod = 0;
- } else {
- slimb_t cancel;
- cancel = 0;
- for(;;) {
- prec2 = prec1 + a->expn + cancel;
- bf_const_pi(U, prec2, BF_RNDF);
- bf_mul_2exp(U, -1, BF_PREC_INF, BF_RNDZ);
- bf_remquo(&mod, T, a, U, prec2, BF_RNDN, BF_RNDN);
- // printf("T.expn=%ld prec2=%ld\n", T->expn, prec2);
- if (mod == 0 || (T->expn != BF_EXP_ZERO &&
- (T->expn + prec2) >= (prec1 - 1)))
- break;
- /* increase the number of bits until the precision is good enough */
- cancel = bf_max(-T->expn, (cancel + 1) * 3 / 2);
- }
- mod &= 3;
- }
-
- is_neg = T->sign;
-
- /* compute cosm1(x) = cos(x) - 1 */
- bf_mul(T, T, T, prec1, BF_RNDN);
- bf_mul_2exp(T, -2 * K, BF_PREC_INF, BF_RNDZ);
-
- /* Taylor expansion:
- -x^2/2 + x^4/4! - x^6/6! + ...
- */
- bf_set_ui(r, 1);
- for(i = l ; i >= 1; i--) {
- bf_set_ui(U, 2 * i - 1);
- bf_mul_ui(U, U, 2 * i, BF_PREC_INF, BF_RNDZ);
- bf_div(U, T, U, prec1, BF_RNDN);
- bf_mul(r, r, U, prec1, BF_RNDN);
- bf_neg(r);
- if (i != 1)
- bf_add_si(r, r, 1, prec1, BF_RNDN);
- }
- bf_delete(U);
-
- /* undo argument reduction:
- cosm1(2*x)= 2*(2*cosm1(x)+cosm1(x)^2)
- */
- for(i = 0; i < K; i++) {
- bf_mul(T, r, r, prec1, BF_RNDN);
- bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ);
- bf_add(r, r, T, prec1, BF_RNDN);
- bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ);
- }
- bf_delete(T);
-
- if (c) {
- if ((mod & 1) == 0) {
- bf_add_si(c, r, 1, prec1, BF_RNDN);
- } else {
- bf_sqrt_sin(c, r, prec1);
- c->sign = is_neg ^ 1;
- }
- c->sign ^= mod >> 1;
- }
- if (s) {
- if ((mod & 1) == 0) {
- bf_sqrt_sin(s, r, prec1);
- s->sign = is_neg;
- } else {
- bf_add_si(s, r, 1, prec1, BF_RNDN);
- }
- s->sign ^= mod >> 1;
- }
- bf_delete(r);
- return BF_ST_INEXACT;
-}
-
-static int bf_cos_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
-{
- return bf_sincos(NULL, r, a, prec);
-}
-
-int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
- if (a->len == 0) {
- if (a->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- return 0;
- } else if (a->expn == BF_EXP_INF) {
- bf_set_nan(r);
- return BF_ST_INVALID_OP;
- } else {
- bf_set_ui(r, 1);
- return 0;
- }
- }
-
- /* small argument case: result = 1+r(x) with r(x) = -x^2/2 +
- O(X^4). We assume r(x) < 2^(2*EXP(x) - 1). */
- if (a->expn < 0) {
- slimb_t e;
- e = 2 * a->expn - 1;
- if (e < -(prec + 2)) {
- bf_set_ui(r, 1);
- return bf_add_epsilon(r, r, e, 1, prec, flags);
- }
- }
-
- return bf_ziv_rounding(r, a, prec, flags, bf_cos_internal, NULL);
-}
-
-static int bf_sin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
-{
- return bf_sincos(r, NULL, a, prec);
-}
-
-int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
- if (a->len == 0) {
- if (a->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- return 0;
- } else if (a->expn == BF_EXP_INF) {
- bf_set_nan(r);
- return BF_ST_INVALID_OP;
- } else {
- bf_set_zero(r, a->sign);
- return 0;
- }
- }
-
- /* small argument case: result = x+r(x) with r(x) = -x^3/6 +
- O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */
- if (a->expn < 0) {
- slimb_t e;
- e = sat_add(2 * a->expn, a->expn - 2);
- if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) {
- bf_set(r, a);
- return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags);
- }
- }
-
- return bf_ziv_rounding(r, a, prec, flags, bf_sin_internal, NULL);
-}
-
-static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
-{
- bf_context_t *s = r->ctx;
- bf_t T_s, *T = &T_s;
- limb_t prec1;
-
- /* XXX: precision analysis */
- prec1 = prec + 8;
- bf_init(s, T);
- bf_sincos(r, T, a, prec1);
- bf_div(r, r, T, prec1, BF_RNDF);
- bf_delete(T);
- return BF_ST_INEXACT;
-}
-
-int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
- assert(r != a);
- if (a->len == 0) {
- if (a->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- return 0;
- } else if (a->expn == BF_EXP_INF) {
- bf_set_nan(r);
- return BF_ST_INVALID_OP;
- } else {
- bf_set_zero(r, a->sign);
- return 0;
- }
- }
-
- /* small argument case: result = x+r(x) with r(x) = x^3/3 +
- O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */
- if (a->expn < 0) {
- slimb_t e;
- e = sat_add(2 * a->expn, a->expn - 1);
- if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) {
- bf_set(r, a);
- return bf_add_epsilon(r, r, e, a->sign, prec, flags);
- }
- }
-
- return bf_ziv_rounding(r, a, prec, flags, bf_tan_internal, NULL);
-}
-
-/* if add_pi2 is true, add pi/2 to the result (used for acos(x) to
- avoid cancellation) */
-static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec,
- void *opaque)
-{
- bf_context_t *s = r->ctx;
- BOOL add_pi2 = (BOOL)(intptr_t)opaque;
- bf_t T_s, *T = &T_s;
- bf_t U_s, *U = &U_s;
- bf_t V_s, *V = &V_s;
- bf_t X2_s, *X2 = &X2_s;
- int cmp_1;
- slimb_t prec1, i, K, l;
-
- /* XXX: precision analysis */
- K = bf_isqrt((prec + 1) / 2);
- l = prec / (2 * K) + 1;
- prec1 = prec + K + 2 * l + 32;
- // printf("prec=%d K=%d l=%d prec1=%d\n", (int)prec, (int)K, (int)l, (int)prec1);
-
- bf_init(s, T);
- cmp_1 = (a->expn >= 1); /* a >= 1 */
- if (cmp_1) {
- bf_set_ui(T, 1);
- bf_div(T, T, a, prec1, BF_RNDN);
- } else {
- bf_set(T, a);
- }
-
- /* abs(T) <= 1 */
-
- /* argument reduction */
-
- bf_init(s, U);
- bf_init(s, V);
- bf_init(s, X2);
- for(i = 0; i < K; i++) {
- /* T = T / (1 + sqrt(1 + T^2)) */
- bf_mul(U, T, T, prec1, BF_RNDN);
- bf_add_si(U, U, 1, prec1, BF_RNDN);
- bf_sqrt(V, U, prec1, BF_RNDN);
- bf_add_si(V, V, 1, prec1, BF_RNDN);
- bf_div(T, T, V, prec1, BF_RNDN);
- }
-
- /* Taylor series:
- x - x^3/3 + ... + (-1)^ l * y^(2*l + 1) / (2*l+1)
- */
- bf_mul(X2, T, T, prec1, BF_RNDN);
- bf_set_ui(r, 0);
- for(i = l; i >= 1; i--) {
- bf_set_si(U, 1);
- bf_set_ui(V, 2 * i + 1);
- bf_div(U, U, V, prec1, BF_RNDN);
- bf_neg(r);
- bf_add(r, r, U, prec1, BF_RNDN);
- bf_mul(r, r, X2, prec1, BF_RNDN);
- }
- bf_neg(r);
- bf_add_si(r, r, 1, prec1, BF_RNDN);
- bf_mul(r, r, T, prec1, BF_RNDN);
-
- /* undo the argument reduction */
- bf_mul_2exp(r, K, BF_PREC_INF, BF_RNDZ);
-
- bf_delete(U);
- bf_delete(V);
- bf_delete(X2);
-
- i = add_pi2;
- if (cmp_1 > 0) {
- /* undo the inversion : r = sign(a)*PI/2 - r */
- bf_neg(r);
- i += 1 - 2 * a->sign;
- }
- /* add i*(pi/2) with -1 <= i <= 2 */
- if (i != 0) {
- bf_const_pi(T, prec1, BF_RNDF);
- if (i != 2)
- bf_mul_2exp(T, -1, BF_PREC_INF, BF_RNDZ);
- T->sign = (i < 0);
- bf_add(r, T, r, prec1, BF_RNDN);
- }
-
- bf_delete(T);
- return BF_ST_INEXACT;
-}
-
-int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
- bf_context_t *s = r->ctx;
- bf_t T_s, *T = &T_s;
- int res;
-
- if (a->len == 0) {
- if (a->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- return 0;
- } else if (a->expn == BF_EXP_INF) {
- /* -PI/2 or PI/2 */
- bf_const_pi_signed(r, a->sign, prec, flags);
- bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ);
- return BF_ST_INEXACT;
- } else {
- bf_set_zero(r, a->sign);
- return 0;
- }
- }
-
- bf_init(s, T);
- bf_set_ui(T, 1);
- res = bf_cmpu(a, T);
- bf_delete(T);
- if (res == 0) {
- /* short cut: abs(a) == 1 -> +/-pi/4 */
- bf_const_pi_signed(r, a->sign, prec, flags);
- bf_mul_2exp(r, -2, BF_PREC_INF, BF_RNDZ);
- return BF_ST_INEXACT;
- }
-
- /* small argument case: result = x+r(x) with r(x) = -x^3/3 +
- O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */
- if (a->expn < 0) {
- slimb_t e;
- e = sat_add(2 * a->expn, a->expn - 1);
- if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) {
- bf_set(r, a);
- return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags);
- }
- }
-
- return bf_ziv_rounding(r, a, prec, flags, bf_atan_internal, (void *)FALSE);
-}
-
-static int bf_atan2_internal(bf_t *r, const bf_t *y, limb_t prec, void *opaque)
-{
- bf_context_t *s = r->ctx;
- const bf_t *x = opaque;
- bf_t T_s, *T = &T_s;
- limb_t prec1;
- int ret;
-
- if (y->expn == BF_EXP_NAN || x->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- return 0;
- }
-
- /* compute atan(y/x) assumming inf/inf = 1 and 0/0 = 0 */
- bf_init(s, T);
- prec1 = prec + 32;
- if (y->expn == BF_EXP_INF && x->expn == BF_EXP_INF) {
- bf_set_ui(T, 1);
- T->sign = y->sign ^ x->sign;
- } else if (y->expn == BF_EXP_ZERO && x->expn == BF_EXP_ZERO) {
- bf_set_zero(T, y->sign ^ x->sign);
- } else {
- bf_div(T, y, x, prec1, BF_RNDF);
- }
- ret = bf_atan(r, T, prec1, BF_RNDF);
-
- if (x->sign) {
- /* if x < 0 (it includes -0), return sign(y)*pi + atan(y/x) */
- bf_const_pi(T, prec1, BF_RNDF);
- T->sign = y->sign;
- bf_add(r, r, T, prec1, BF_RNDN);
- ret |= BF_ST_INEXACT;
- }
-
- bf_delete(T);
- return ret;
-}
-
-int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x,
- limb_t prec, bf_flags_t flags)
-{
- return bf_ziv_rounding(r, y, prec, flags, bf_atan2_internal, (void *)x);
-}
-
-static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque)
-{
- bf_context_t *s = r->ctx;
- BOOL is_acos = (BOOL)(intptr_t)opaque;
- bf_t T_s, *T = &T_s;
- limb_t prec1, prec2;
-
- /* asin(x) = atan(x/sqrt(1-x^2))
- acos(x) = pi/2 - asin(x) */
- prec1 = prec + 8;
- /* increase the precision in x^2 to compensate the cancellation in
- (1-x^2) if x is close to 1 */
- /* XXX: use less precision when possible */
- if (a->expn >= 0)
- prec2 = BF_PREC_INF;
- else
- prec2 = prec1;
- bf_init(s, T);
- bf_mul(T, a, a, prec2, BF_RNDN);
- bf_neg(T);
- bf_add_si(T, T, 1, prec2, BF_RNDN);
-
- bf_sqrt(r, T, prec1, BF_RNDN);
- bf_div(T, a, r, prec1, BF_RNDN);
- if (is_acos)
- bf_neg(T);
- bf_atan_internal(r, T, prec1, (void *)(intptr_t)is_acos);
- bf_delete(T);
- return BF_ST_INEXACT;
-}
-
-int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
- bf_context_t *s = r->ctx;
- bf_t T_s, *T = &T_s;
- int res;
-
- if (a->len == 0) {
- if (a->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- return 0;
- } else if (a->expn == BF_EXP_INF) {
- bf_set_nan(r);
- return BF_ST_INVALID_OP;
- } else {
- bf_set_zero(r, a->sign);
- return 0;
- }
- }
- bf_init(s, T);
- bf_set_ui(T, 1);
- res = bf_cmpu(a, T);
- bf_delete(T);
- if (res > 0) {
- bf_set_nan(r);
- return BF_ST_INVALID_OP;
- }
-
- /* small argument case: result = x+r(x) with r(x) = x^3/6 +
- O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */
- if (a->expn < 0) {
- slimb_t e;
- e = sat_add(2 * a->expn, a->expn - 2);
- if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) {
- bf_set(r, a);
- return bf_add_epsilon(r, r, e, a->sign, prec, flags);
- }
- }
-
- return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)FALSE);
-}
-
-int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags)
-{
- bf_context_t *s = r->ctx;
- bf_t T_s, *T = &T_s;
- int res;
-
- if (a->len == 0) {
- if (a->expn == BF_EXP_NAN) {
- bf_set_nan(r);
- return 0;
- } else if (a->expn == BF_EXP_INF) {
- bf_set_nan(r);
- return BF_ST_INVALID_OP;
- } else {
- bf_const_pi(r, prec, flags);
- bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ);
- return BF_ST_INEXACT;
- }
- }
- bf_init(s, T);
- bf_set_ui(T, 1);
- res = bf_cmpu(a, T);
- bf_delete(T);
- if (res > 0) {
- bf_set_nan(r);
- return BF_ST_INVALID_OP;
- } else if (res == 0 && a->sign == 0) {
- bf_set_zero(r, 0);
- return 0;
- }
-
- return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)TRUE);
-}
-
-/***************************************************************/
-/* decimal floating point numbers */
-
-#ifdef USE_BF_DEC
-
-#define adddq(r1, r0, a1, a0) \
- do { \
- limb_t __t = r0; \
- r0 += (a0); \
- r1 += (a1) + (r0 < __t); \
- } while (0)
-
-#define subdq(r1, r0, a1, a0) \
- do { \
- limb_t __t = r0; \
- r0 -= (a0); \
- r1 -= (a1) + (r0 > __t); \
- } while (0)
-
-#if LIMB_BITS == 64
-
-/* Note: we assume __int128 is available */
-#define muldq(r1, r0, a, b) \
- do { \
- unsigned __int128 __t; \
- __t = (unsigned __int128)(a) * (unsigned __int128)(b); \
- r0 = __t; \
- r1 = __t >> 64; \
- } while (0)
-
-#define divdq(q, r, a1, a0, b) \
- do { \
- unsigned __int128 __t; \
- limb_t __b = (b); \
- __t = ((unsigned __int128)(a1) << 64) | (a0); \
- q = __t / __b; \
- r = __t % __b; \
- } while (0)
-
-#else
-
-#define muldq(r1, r0, a, b) \
- do { \
- uint64_t __t; \
- __t = (uint64_t)(a) * (uint64_t)(b); \
- r0 = __t; \
- r1 = __t >> 32; \
- } while (0)
-
-#define divdq(q, r, a1, a0, b) \
- do { \
- uint64_t __t; \
- limb_t __b = (b); \
- __t = ((uint64_t)(a1) << 32) | (a0); \
- q = __t / __b; \
- r = __t % __b; \
- } while (0)
-
-#endif /* LIMB_BITS != 64 */
-
-#if LIMB_DIGITS == 19
-
-/* WARNING: hardcoded for b = 1e19. It is assumed that:
- 0 <= a1 < 2^63 */
-#define divdq_base(q, r, a1, a0)\
-do {\
- uint64_t __a0, __a1, __t0, __t1, __b = BF_DEC_BASE; \
- __a0 = a0;\
- __a1 = a1;\
- __t0 = __a1;\
- __t0 = shld(__t0, __a0, 1);\
- muldq(q, __t1, __t0, UINT64_C(17014118346046923173)); \
- muldq(__t1, __t0, q, __b);\
- subdq(__a1, __a0, __t1, __t0);\
- subdq(__a1, __a0, 1, __b * 2); \
- __t0 = (slimb_t)__a1 >> 1; \
- q += 2 + __t0;\
- adddq(__a1, __a0, 0, __b & __t0);\
- q += __a1; \
- __a0 += __b & __a1; \
- r = __a0;\
-} while(0)
-
-#elif LIMB_DIGITS == 9
-
-/* WARNING: hardcoded for b = 1e9. It is assumed that:
- 0 <= a1 < 2^29 */
-#define divdq_base(q, r, a1, a0)\
-do {\
- uint32_t __t0, __t1, __b = BF_DEC_BASE; \
- __t0 = a1;\
- __t1 = a0;\
- __t0 = (__t0 << 3) | (__t1 >> (32 - 3)); \
- muldq(q, __t1, __t0, 2305843009U);\
- r = a0 - q * __b;\
- __t1 = (r >= __b);\
- q += __t1;\
- if (__t1)\
- r -= __b;\
-} while(0)
-
-#endif
-
-/* fast integer division by a fixed constant */
-
-typedef struct FastDivData {
- limb_t m1; /* multiplier */
- int8_t shift1;
- int8_t shift2;
-} FastDivData;
-
-/* From "Division by Invariant Integers using Multiplication" by
- Torborn Granlund and Peter L. Montgomery */
-/* d must be != 0 */
-static inline __maybe_unused void fast_udiv_init(FastDivData *s, limb_t d)
-{
- int l;
- limb_t q, r, m1;
- if (d == 1)
- l = 0;
- else
- l = 64 - clz64(d - 1);
- divdq(q, r, ((limb_t)1 << l) - d, 0, d);
- (void)r;
- m1 = q + 1;
- // printf("d=%lu l=%d m1=0x%016lx\n", d, l, m1);
- s->m1 = m1;
- s->shift1 = l;
- if (s->shift1 > 1)
- s->shift1 = 1;
- s->shift2 = l - 1;
- if (s->shift2 < 0)
- s->shift2 = 0;
-}
-
-static inline limb_t fast_udiv(limb_t a, const FastDivData *s)
-{
- limb_t t0, t1;
- muldq(t1, t0, s->m1, a);
- t0 = (a - t1) >> s->shift1;
- return (t1 + t0) >> s->shift2;
-}
-
-/* contains 10^i */
-const limb_t mp_pow_dec[LIMB_DIGITS + 1] = {
- 1U,
- 10U,
- 100U,
- 1000U,
- 10000U,
- 100000U,
- 1000000U,
- 10000000U,
- 100000000U,
- 1000000000U,
-#if LIMB_BITS == 64
- 10000000000U,
- 100000000000U,
- 1000000000000U,
- 10000000000000U,
- 100000000000000U,
- 1000000000000000U,
- 10000000000000000U,
- 100000000000000000U,
- 1000000000000000000U,
- 10000000000000000000U,
-#endif
-};
-
-/* precomputed from fast_udiv_init(10^i) */
-static const FastDivData mp_pow_div[LIMB_DIGITS + 1] = {
-#if LIMB_BITS == 32
- { 0x00000001, 0, 0 },
- { 0x9999999a, 1, 3 },
- { 0x47ae147b, 1, 6 },
- { 0x0624dd30, 1, 9 },
- { 0xa36e2eb2, 1, 13 },
- { 0x4f8b588f, 1, 16 },
- { 0x0c6f7a0c, 1, 19 },
- { 0xad7f29ac, 1, 23 },
- { 0x5798ee24, 1, 26 },
- { 0x12e0be83, 1, 29 },
-#else
- { 0x0000000000000001, 0, 0 },
- { 0x999999999999999a, 1, 3 },
- { 0x47ae147ae147ae15, 1, 6 },
- { 0x0624dd2f1a9fbe77, 1, 9 },
- { 0xa36e2eb1c432ca58, 1, 13 },
- { 0x4f8b588e368f0847, 1, 16 },
- { 0x0c6f7a0b5ed8d36c, 1, 19 },
- { 0xad7f29abcaf48579, 1, 23 },
- { 0x5798ee2308c39dfa, 1, 26 },
- { 0x12e0be826d694b2f, 1, 29 },
- { 0xb7cdfd9d7bdbab7e, 1, 33 },
- { 0x5fd7fe17964955fe, 1, 36 },
- { 0x19799812dea11198, 1, 39 },
- { 0xc25c268497681c27, 1, 43 },
- { 0x6849b86a12b9b01f, 1, 46 },
- { 0x203af9ee756159b3, 1, 49 },
- { 0xcd2b297d889bc2b7, 1, 53 },
- { 0x70ef54646d496893, 1, 56 },
- { 0x2725dd1d243aba0f, 1, 59 },
- { 0xd83c94fb6d2ac34d, 1, 63 },
-#endif
-};
-
-/* divide by 10^shift with 0 <= shift <= LIMB_DIGITS */
-static inline limb_t fast_shr_dec(limb_t a, int shift)
-{
- return fast_udiv(a, &mp_pow_div[shift]);
-}
-
-/* division and remainder by 10^shift */
-#define fast_shr_rem_dec(q, r, a, shift) q = fast_shr_dec(a, shift), r = a - q * mp_pow_dec[shift]
-
-limb_t mp_add_dec(limb_t *res, const limb_t *op1, const limb_t *op2,
- mp_size_t n, limb_t carry)
-{
- limb_t base = BF_DEC_BASE;
- mp_size_t i;
- limb_t k, a, v;
-
- k=carry;
- for(i=0;i<n;i++) {
- /* XXX: reuse the trick in add_mod */
- v = op1[i];
- a = v + op2[i] + k - base;
- k = a <= v;
- if (!k)
- a += base;
- res[i]=a;
- }
- return k;
-}
-
-limb_t mp_add_ui_dec(limb_t *tab, limb_t b, mp_size_t n)
-{
- limb_t base = BF_DEC_BASE;
- mp_size_t i;
- limb_t k, a, v;
-
- k=b;
- for(i=0;i<n;i++) {
- v = tab[i];
- a = v + k - base;
- k = a <= v;
- if (!k)
- a += base;
- tab[i] = a;
- if (k == 0)
- break;
- }
- return k;
-}
-
-limb_t mp_sub_dec(limb_t *res, const limb_t *op1, const limb_t *op2,
- mp_size_t n, limb_t carry)
-{
- limb_t base = BF_DEC_BASE;
- mp_size_t i;
- limb_t k, v, a;
-
- k=carry;
- for(i=0;i<n;i++) {
- v = op1[i];
- a = v - op2[i] - k;
- k = a > v;
- if (k)
- a += base;
- res[i] = a;
- }
- return k;
-}
-
-limb_t mp_sub_ui_dec(limb_t *tab, limb_t b, mp_size_t n)
-{
- limb_t base = BF_DEC_BASE;
- mp_size_t i;
- limb_t k, v, a;
-
- k=b;
- for(i=0;i<n;i++) {
- v = tab[i];
- a = v - k;
- k = a > v;
- if (k)
- a += base;
- tab[i]=a;
- if (k == 0)
- break;
- }
- return k;
-}
-
-/* taba[] = taba[] * b + l. 0 <= b, l <= base - 1. Return the high carry */
-limb_t mp_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n,
- limb_t b, limb_t l)
-{
- mp_size_t i;
- limb_t t0, t1, r;
-
- for(i = 0; i < n; i++) {
- muldq(t1, t0, taba[i], b);
- adddq(t1, t0, 0, l);
- divdq_base(l, r, t1, t0);
- tabr[i] = r;
- }
- return l;
-}
-
-/* tabr[] += taba[] * b. 0 <= b <= base - 1. Return the value to add
- to the high word */
-limb_t mp_add_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n,
- limb_t b)
-{
- mp_size_t i;
- limb_t l, t0, t1, r;
-
- l = 0;
- for(i = 0; i < n; i++) {
- muldq(t1, t0, taba[i], b);
- adddq(t1, t0, 0, l);
- adddq(t1, t0, 0, tabr[i]);
- divdq_base(l, r, t1, t0);
- tabr[i] = r;
- }
- return l;
-}
-
-/* tabr[] -= taba[] * b. 0 <= b <= base - 1. Return the value to
- substract to the high word. */
-limb_t mp_sub_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n,
- limb_t b)
-{
- limb_t base = BF_DEC_BASE;
- mp_size_t i;
- limb_t l, t0, t1, r, a, v, c;
-
- /* XXX: optimize */
- l = 0;
- for(i = 0; i < n; i++) {
- muldq(t1, t0, taba[i], b);
- adddq(t1, t0, 0, l);
- divdq_base(l, r, t1, t0);
- v = tabr[i];
- a = v - r;
- c = a > v;
- if (c)
- a += base;
- /* never bigger than base because r = 0 when l = base - 1 */
- l += c;
- tabr[i] = a;
- }
- return l;
-}
-
-/* size of the result : op1_size + op2_size. */
-void mp_mul_basecase_dec(limb_t *result,
- const limb_t *op1, mp_size_t op1_size,
- const limb_t *op2, mp_size_t op2_size)
-{
- mp_size_t i;
- limb_t r;
-
- result[op1_size] = mp_mul1_dec(result, op1, op1_size, op2[0], 0);
-
- for(i=1;i<op2_size;i++) {
- r = mp_add_mul1_dec(result + i, op1, op1_size, op2[i]);
- result[i + op1_size] = r;
- }
-}
-
-/* taba[] = (taba[] + r*base^na) / b. 0 <= b < base. 0 <= r <
- b. Return the remainder. */
-limb_t mp_div1_dec(limb_t *tabr, const limb_t *taba, mp_size_t na,
- limb_t b, limb_t r)
-{
- limb_t base = BF_DEC_BASE;
- mp_size_t i;
- limb_t t0, t1, q;
- int shift;
-
-#if (BF_DEC_BASE % 2) == 0
- if (b == 2) {
- limb_t base_div2;
- /* Note: only works if base is even */
- base_div2 = base >> 1;
- if (r)
- r = base_div2;
- for(i = na - 1; i >= 0; i--) {
- t0 = taba[i];
- tabr[i] = (t0 >> 1) + r;
- r = 0;
- if (t0 & 1)
- r = base_div2;
- }
- if (r)
- r = 1;
- } else
-#endif
- if (na >= UDIV1NORM_THRESHOLD) {
- shift = clz(b);
- if (shift == 0) {
- /* normalized case: b >= 2^(LIMB_BITS-1) */
- limb_t b_inv;
- b_inv = udiv1norm_init(b);
- for(i = na - 1; i >= 0; i--) {
- muldq(t1, t0, r, base);
- adddq(t1, t0, 0, taba[i]);
- q = udiv1norm(&r, t1, t0, b, b_inv);
- tabr[i] = q;
- }
- } else {
- limb_t b_inv;
- b <<= shift;
- b_inv = udiv1norm_init(b);
- for(i = na - 1; i >= 0; i--) {
- muldq(t1, t0, r, base);
- adddq(t1, t0, 0, taba[i]);
- t1 = (t1 << shift) | (t0 >> (LIMB_BITS - shift));
- t0 <<= shift;
- q = udiv1norm(&r, t1, t0, b, b_inv);
- r >>= shift;
- tabr[i] = q;
- }
- }
- } else {
- for(i = na - 1; i >= 0; i--) {
- muldq(t1, t0, r, base);
- adddq(t1, t0, 0, taba[i]);
- divdq(q, r, t1, t0, b);
- tabr[i] = q;
- }
- }
- return r;
-}
-
-static __maybe_unused void mp_print_str_dec(const char *str,
- const limb_t *tab, slimb_t n)
-{
- slimb_t i;
- printf("%s=", str);
- for(i = n - 1; i >= 0; i--) {
- if (i != n - 1)
- printf("_");
- printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]);
- }
- printf("\n");
-}
-
-static __maybe_unused void mp_print_str_h_dec(const char *str,
- const limb_t *tab, slimb_t n,
- limb_t high)
-{
- slimb_t i;
- printf("%s=", str);
- printf("%0*" PRIu_LIMB, LIMB_DIGITS, high);
- for(i = n - 1; i >= 0; i--) {
- printf("_");
- printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]);
- }
- printf("\n");
-}
-
-//#define DEBUG_DIV_SLOW
-
-#define DIV_STATIC_ALLOC_LEN 16
-
-/* return q = a / b and r = a % b.
-
- taba[na] must be allocated if tabb1[nb - 1] < B / 2. tabb1[nb - 1]
- must be != zero. na must be >= nb. 's' can be NULL if tabb1[nb - 1]
- >= B / 2.
-
- The remainder is is returned in taba and contains nb libms. tabq
- contains na - nb + 1 limbs. No overlap is permitted.
-
- Running time of the standard method: (na - nb + 1) * nb
- Return 0 if OK, -1 if memory alloc error
-*/
-/* XXX: optimize */
-static int mp_div_dec(bf_context_t *s, limb_t *tabq,
- limb_t *taba, mp_size_t na,
- const limb_t *tabb1, mp_size_t nb)
-{
- limb_t base = BF_DEC_BASE;
- limb_t r, mult, t0, t1, a, c, q, v, *tabb;
- mp_size_t i, j;
- limb_t static_tabb[DIV_STATIC_ALLOC_LEN];
-
-#ifdef DEBUG_DIV_SLOW
- mp_print_str_dec("a", taba, na);
- mp_print_str_dec("b", tabb1, nb);
-#endif
-
- /* normalize tabb */
- r = tabb1[nb - 1];
- assert(r != 0);
- i = na - nb;
- if (r >= BF_DEC_BASE / 2) {
- mult = 1;
- tabb = (limb_t *)tabb1;
- q = 1;
- for(j = nb - 1; j >= 0; j--) {
- if (taba[i + j] != tabb[j]) {
- if (taba[i + j] < tabb[j])
- q = 0;
- break;
- }
- }
- tabq[i] = q;
- if (q) {
- mp_sub_dec(taba + i, taba + i, tabb, nb, 0);
- }
- i--;
- } else {
- mult = base / (r + 1);
- if (likely(nb <= DIV_STATIC_ALLOC_LEN)) {
- tabb = static_tabb;
- } else {
- tabb = bf_malloc(s, sizeof(limb_t) * nb);
- if (!tabb)
- return -1;
- }
- mp_mul1_dec(tabb, tabb1, nb, mult, 0);
- taba[na] = mp_mul1_dec(taba, taba, na, mult, 0);
- }
-
-#ifdef DEBUG_DIV_SLOW
- printf("mult=" FMT_LIMB "\n", mult);
- mp_print_str_dec("a_norm", taba, na + 1);
- mp_print_str_dec("b_norm", tabb, nb);
-#endif
-
- for(; i >= 0; i--) {
- if (unlikely(taba[i + nb] >= tabb[nb - 1])) {
- /* XXX: check if it is really possible */
- q = base - 1;
- } else {
- muldq(t1, t0, taba[i + nb], base);
- adddq(t1, t0, 0, taba[i + nb - 1]);
- divdq(q, r, t1, t0, tabb[nb - 1]);
- }
- // printf("i=%d q1=%ld\n", i, q);
-
- r = mp_sub_mul1_dec(taba + i, tabb, nb, q);
- // mp_dump("r1", taba + i, nb, bd);
- // printf("r2=%ld\n", r);
-
- v = taba[i + nb];
- a = v - r;
- c = a > v;
- if (c)
- a += base;
- taba[i + nb] = a;
-
- if (c != 0) {
- /* negative result */
- for(;;) {
- q--;
- c = mp_add_dec(taba + i, taba + i, tabb, nb, 0);
- /* propagate carry and test if positive result */
- if (c != 0) {
- if (++taba[i + nb] == base) {
- break;
- }
- }
- }
- }
- tabq[i] = q;
- }
-
-#ifdef DEBUG_DIV_SLOW
- mp_print_str_dec("q", tabq, na - nb + 1);
- mp_print_str_dec("r", taba, nb);
-#endif
-
- /* remove the normalization */
- if (mult != 1) {
- mp_div1_dec(taba, taba, nb, mult, 0);
- if (unlikely(tabb != static_tabb))
- bf_free(s, tabb);
- }
- return 0;
-}
-
-/* divide by 10^shift */
-static limb_t mp_shr_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n,
- limb_t shift, limb_t high)
-{
- mp_size_t i;
- limb_t l, a, q, r;
-
- assert(shift >= 1 && shift < LIMB_DIGITS);
- l = high;
- for(i = n - 1; i >= 0; i--) {
- a = tab[i];
- fast_shr_rem_dec(q, r, a, shift);
- tab_r[i] = q + l * mp_pow_dec[LIMB_DIGITS - shift];
- l = r;
- }
- return l;
-}
-
-/* multiply by 10^shift */
-static limb_t mp_shl_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n,
- limb_t shift, limb_t low)
-{
- mp_size_t i;
- limb_t l, a, q, r;
-
- assert(shift >= 1 && shift < LIMB_DIGITS);
- l = low;
- for(i = 0; i < n; i++) {
- a = tab[i];
- fast_shr_rem_dec(q, r, a, LIMB_DIGITS - shift);
- tab_r[i] = r * mp_pow_dec[shift] + l;
- l = q;
- }
- return l;
-}
-
-static limb_t mp_sqrtrem2_dec(limb_t *tabs, limb_t *taba)
-{
- int k;
- dlimb_t a, b, r;
- limb_t taba1[2], s, r0, r1;
-
- /* convert to binary and normalize */
- a = (dlimb_t)taba[1] * BF_DEC_BASE + taba[0];
- k = clz(a >> LIMB_BITS) & ~1;
- b = a << k;
- taba1[0] = b;
- taba1[1] = b >> LIMB_BITS;
- mp_sqrtrem2(&s, taba1);
- s >>= (k >> 1);
- /* convert the remainder back to decimal */
- r = a - (dlimb_t)s * (dlimb_t)s;
- divdq_base(r1, r0, r >> LIMB_BITS, r);
- taba[0] = r0;
- tabs[0] = s;
- return r1;
-}
-
-//#define DEBUG_SQRTREM_DEC
-
-/* tmp_buf must contain (n / 2 + 1 limbs) */
-static limb_t mp_sqrtrem_rec_dec(limb_t *tabs, limb_t *taba, limb_t n,
- limb_t *tmp_buf)
-{
- limb_t l, h, rh, ql, qh, c, i;
-
- if (n == 1)
- return mp_sqrtrem2_dec(tabs, taba);
-#ifdef DEBUG_SQRTREM_DEC
- mp_print_str_dec("a", taba, 2 * n);
-#endif
- l = n / 2;
- h = n - l;
- qh = mp_sqrtrem_rec_dec(tabs + l, taba + 2 * l, h, tmp_buf);
-#ifdef DEBUG_SQRTREM_DEC
- mp_print_str_dec("s1", tabs + l, h);
- mp_print_str_h_dec("r1", taba + 2 * l, h, qh);
- mp_print_str_h_dec("r2", taba + l, n, qh);
-#endif
-
- /* the remainder is in taba + 2 * l. Its high bit is in qh */
- if (qh) {
- mp_sub_dec(taba + 2 * l, taba + 2 * l, tabs + l, h, 0);
- }
- /* instead of dividing by 2*s, divide by s (which is normalized)
- and update q and r */
- mp_div_dec(NULL, tmp_buf, taba + l, n, tabs + l, h);
- qh += tmp_buf[l];
- for(i = 0; i < l; i++)
- tabs[i] = tmp_buf[i];
- ql = mp_div1_dec(tabs, tabs, l, 2, qh & 1);
- qh = qh >> 1; /* 0 or 1 */
- if (ql)
- rh = mp_add_dec(taba + l, taba + l, tabs + l, h, 0);
- else
- rh = 0;
-#ifdef DEBUG_SQRTREM_DEC
- mp_print_str_h_dec("q", tabs, l, qh);
- mp_print_str_h_dec("u", taba + l, h, rh);
-#endif
-
- mp_add_ui_dec(tabs + l, qh, h);
-#ifdef DEBUG_SQRTREM_DEC
- mp_print_str_dec("s2", tabs, n);
-#endif
-
- /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */
- /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */
- if (qh) {
- c = qh;
- } else {
- mp_mul_basecase_dec(taba + n, tabs, l, tabs, l);
- c = mp_sub_dec(taba, taba, taba + n, 2 * l, 0);
- }
- rh -= mp_sub_ui_dec(taba + 2 * l, c, n - 2 * l);
- if ((slimb_t)rh < 0) {
- mp_sub_ui_dec(tabs, 1, n);
- rh += mp_add_mul1_dec(taba, tabs, n, 2);
- rh += mp_add_ui_dec(taba, 1, n);
- }
- return rh;
-}
-
-/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= B/4. Return (s,
- r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 * s. tabs has n
- limbs. r is returned in the lower n limbs of taba. Its r[n] is the
- returned value of the function. */
-int mp_sqrtrem_dec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n)
-{
- limb_t tmp_buf1[8];
- limb_t *tmp_buf;
- mp_size_t n2;
- n2 = n / 2 + 1;
- if (n2 <= countof(tmp_buf1)) {
- tmp_buf = tmp_buf1;
- } else {
- tmp_buf = bf_malloc(s, sizeof(limb_t) * n2);
- if (!tmp_buf)
- return -1;
- }
- taba[n] = mp_sqrtrem_rec_dec(tabs, taba, n, tmp_buf);
- if (tmp_buf != tmp_buf1)
- bf_free(s, tmp_buf);
- return 0;
-}
-
-/* return the number of leading zero digits, from 0 to LIMB_DIGITS */
-static int clz_dec(limb_t a)
-{
- if (a == 0)
- return LIMB_DIGITS;
- switch(LIMB_BITS - 1 - clz(a)) {
- case 0: /* 1-1 */
- return LIMB_DIGITS - 1;
- case 1: /* 2-3 */
- return LIMB_DIGITS - 1;
- case 2: /* 4-7 */
- return LIMB_DIGITS - 1;
- case 3: /* 8-15 */
- if (a < 10)
- return LIMB_DIGITS - 1;
- else
- return LIMB_DIGITS - 2;
- case 4: /* 16-31 */
- return LIMB_DIGITS - 2;
- case 5: /* 32-63 */
- return LIMB_DIGITS - 2;
- case 6: /* 64-127 */
- if (a < 100)
- return LIMB_DIGITS - 2;
- else
- return LIMB_DIGITS - 3;
- case 7: /* 128-255 */
- return LIMB_DIGITS - 3;
- case 8: /* 256-511 */
- return LIMB_DIGITS - 3;
- case 9: /* 512-1023 */
- if (a < 1000)
- return LIMB_DIGITS - 3;
- else
- return LIMB_DIGITS - 4;
- case 10: /* 1024-2047 */
- return LIMB_DIGITS - 4;
- case 11: /* 2048-4095 */
- return LIMB_DIGITS - 4;
- case 12: /* 4096-8191 */
- return LIMB_DIGITS - 4;
- case 13: /* 8192-16383 */
- if (a < 10000)
- return LIMB_DIGITS - 4;
- else
- return LIMB_DIGITS - 5;
- case 14: /* 16384-32767 */
- return LIMB_DIGITS - 5;
- case 15: /* 32768-65535 */
- return LIMB_DIGITS - 5;
- case 16: /* 65536-131071 */
- if (a < 100000)
- return LIMB_DIGITS - 5;
- else
- return LIMB_DIGITS - 6;
- case 17: /* 131072-262143 */
- return LIMB_DIGITS - 6;
- case 18: /* 262144-524287 */
- return LIMB_DIGITS - 6;
- case 19: /* 524288-1048575 */
- if (a < 1000000)
- return LIMB_DIGITS - 6;
- else
- return LIMB_DIGITS - 7;
- case 20: /* 1048576-2097151 */
- return LIMB_DIGITS - 7;
- case 21: /* 2097152-4194303 */
- return LIMB_DIGITS - 7;
- case 22: /* 4194304-8388607 */
- return LIMB_DIGITS - 7;
- case 23: /* 8388608-16777215 */
- if (a < 10000000)
- return LIMB_DIGITS - 7;
- else
- return LIMB_DIGITS - 8;
- case 24: /* 16777216-33554431 */
- return LIMB_DIGITS - 8;
- case 25: /* 33554432-67108863 */
- return LIMB_DIGITS - 8;
- case 26: /* 67108864-134217727 */
- if (a < 100000000)
- return LIMB_DIGITS - 8;
- else
- return LIMB_DIGITS - 9;
-#if LIMB_BITS == 64
- case 27: /* 134217728-268435455 */
- return LIMB_DIGITS - 9;
- case 28: /* 268435456-536870911 */
- return LIMB_DIGITS - 9;
- case 29: /* 536870912-1073741823 */
- if (a < 1000000000)
- return LIMB_DIGITS - 9;
- else
- return LIMB_DIGITS - 10;
- case 30: /* 1073741824-2147483647 */
- return LIMB_DIGITS - 10;
- case 31: /* 2147483648-4294967295 */
- return LIMB_DIGITS - 10;
- case 32: /* 4294967296-8589934591 */
- return LIMB_DIGITS - 10;
- case 33: /* 8589934592-17179869183 */
- if (a < 10000000000)
- return LIMB_DIGITS - 10;
- else
- return LIMB_DIGITS - 11;
- case 34: /* 17179869184-34359738367 */
- return LIMB_DIGITS - 11;
- case 35: /* 34359738368-68719476735 */
- return LIMB_DIGITS - 11;
- case 36: /* 68719476736-137438953471 */
- if (a < 100000000000)
- return LIMB_DIGITS - 11;
- else
- return LIMB_DIGITS - 12;
- case 37: /* 137438953472-274877906943 */
- return LIMB_DIGITS - 12;
- case 38: /* 274877906944-549755813887 */
- return LIMB_DIGITS - 12;
- case 39: /* 549755813888-1099511627775 */
- if (a < 1000000000000)
- return LIMB_DIGITS - 12;
- else
- return LIMB_DIGITS - 13;
- case 40: /* 1099511627776-2199023255551 */
- return LIMB_DIGITS - 13;
- case 41: /* 2199023255552-4398046511103 */
- return LIMB_DIGITS - 13;
- case 42: /* 4398046511104-8796093022207 */
- return LIMB_DIGITS - 13;
- case 43: /* 8796093022208-17592186044415 */
- if (a < 10000000000000)
- return LIMB_DIGITS - 13;
- else
- return LIMB_DIGITS - 14;
- case 44: /* 17592186044416-35184372088831 */
- return LIMB_DIGITS - 14;
- case 45: /* 35184372088832-70368744177663 */
- return LIMB_DIGITS - 14;
- case 46: /* 70368744177664-140737488355327 */
- if (a < 100000000000000)
- return LIMB_DIGITS - 14;
- else
- return LIMB_DIGITS - 15;
- case 47: /* 140737488355328-281474976710655 */
- return LIMB_DIGITS - 15;
- case 48: /* 281474976710656-562949953421311 */
- return LIMB_DIGITS - 15;
- case 49: /* 562949953421312-1125899906842623 */
- if (a < 1000000000000000)
- return LIMB_DIGITS - 15;
- else
- return LIMB_DIGITS - 16;
- case 50: /* 1125899906842624-2251799813685247 */
- return LIMB_DIGITS - 16;
- case 51: /* 2251799813685248-4503599627370495 */
- return LIMB_DIGITS - 16;
- case 52: /* 4503599627370496-9007199254740991 */
- return LIMB_DIGITS - 16;
- case 53: /* 9007199254740992-18014398509481983 */
- if (a < 10000000000000000)
- return LIMB_DIGITS - 16;
- else
- return LIMB_DIGITS - 17;
- case 54: /* 18014398509481984-36028797018963967 */
- return LIMB_DIGITS - 17;
- case 55: /* 36028797018963968-72057594037927935 */
- return LIMB_DIGITS - 17;
- case 56: /* 72057594037927936-144115188075855871 */
- if (a < 100000000000000000)
- return LIMB_DIGITS - 17;
- else
- return LIMB_DIGITS - 18;
- case 57: /* 144115188075855872-288230376151711743 */
- return LIMB_DIGITS - 18;
- case 58: /* 288230376151711744-576460752303423487 */
- return LIMB_DIGITS - 18;
- case 59: /* 576460752303423488-1152921504606846975 */
- if (a < 1000000000000000000)
- return LIMB_DIGITS - 18;
- else
- return LIMB_DIGITS - 19;
-#endif
- default:
- return 0;
- }
-}
-
-/* for debugging */
-void bfdec_print_str(const char *str, const bfdec_t *a)
-{
- slimb_t i;
- printf("%s=", str);
-
- if (a->expn == BF_EXP_NAN) {
- printf("NaN");
- } else {
- if (a->sign)
- putchar('-');
- if (a->expn == BF_EXP_ZERO) {
- putchar('0');
- } else if (a->expn == BF_EXP_INF) {
- printf("Inf");
- } else {
- printf("0.");
- for(i = a->len - 1; i >= 0; i--)
- printf("%0*" PRIu_LIMB, LIMB_DIGITS, a->tab[i]);
- printf("e%" PRId_LIMB, a->expn);
- }
- }
- printf("\n");
-}
-
-/* return != 0 if one digit between 0 and bit_pos inclusive is not zero. */
-static inline limb_t scan_digit_nz(const bfdec_t *r, slimb_t bit_pos)
-{
- slimb_t pos;
- limb_t v, q;
- int shift;
-
- if (bit_pos < 0)
- return 0;
- pos = (limb_t)bit_pos / LIMB_DIGITS;
- shift = (limb_t)bit_pos % LIMB_DIGITS;
- fast_shr_rem_dec(q, v, r->tab[pos], shift + 1);
- (void)q;
- if (v != 0)
- return 1;
- pos--;
- while (pos >= 0) {
- if (r->tab[pos] != 0)
- return 1;
- pos--;
- }
- return 0;
-}
-
-static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos)
-{
- slimb_t i;
- int shift;
- i = floor_div(pos, LIMB_DIGITS);
- if (i < 0 || i >= len)
- return 0;
- shift = pos - i * LIMB_DIGITS;
- return fast_shr_dec(tab[i], shift) % 10;
-}
-
-#if 0
-static limb_t get_digits(const limb_t *tab, limb_t len, slimb_t pos)
-{
- limb_t a0, a1;
- int shift;
- slimb_t i;
-
- i = floor_div(pos, LIMB_DIGITS);
- shift = pos - i * LIMB_DIGITS;
- if (i >= 0 && i < len)
- a0 = tab[i];
- else
- a0 = 0;
- if (shift == 0) {
- return a0;
- } else {
- i++;
- if (i >= 0 && i < len)
- a1 = tab[i];
- else
- a1 = 0;
- return fast_shr_dec(a0, shift) +
- fast_urem(a1, &mp_pow_div[LIMB_DIGITS - shift]) *
- mp_pow_dec[shift];
- }
-}
-#endif
-
-/* return the addend for rounding. Note that prec can be <= 0 for bf_rint() */
-static int bfdec_get_rnd_add(int *pret, const bfdec_t *r, limb_t l,
- slimb_t prec, int rnd_mode)
-{
- int add_one, inexact;
- limb_t digit1, digit0;
-
- // bfdec_print_str("get_rnd_add", r);
- if (rnd_mode == BF_RNDF) {
- digit0 = 1; /* faithful rounding does not honor the INEXACT flag */
- } else {
- /* starting limb for bit 'prec + 1' */
- digit0 = scan_digit_nz(r, l * LIMB_DIGITS - 1 - bf_max(0, prec + 1));
- }
-
- /* get the digit at 'prec' */
- digit1 = get_digit(r->tab, l, l * LIMB_DIGITS - 1 - prec);
- inexact = (digit1 | digit0) != 0;
-
- add_one = 0;
- switch(rnd_mode) {
- case BF_RNDZ:
- break;
- case BF_RNDN:
- if (digit1 == 5) {
- if (digit0) {
- add_one = 1;
- } else {
- /* round to even */
- add_one =
- get_digit(r->tab, l, l * LIMB_DIGITS - 1 - (prec - 1)) & 1;
- }
- } else if (digit1 > 5) {
- add_one = 1;
- }
- break;
- case BF_RNDD:
- case BF_RNDU:
- if (r->sign == (rnd_mode == BF_RNDD))
- add_one = inexact;
- break;
- case BF_RNDNA:
- case BF_RNDF:
- add_one = (digit1 >= 5);
- break;
- case BF_RNDA:
- add_one = inexact;
- break;
- default:
- abort();
- }
-
- if (inexact)
- *pret |= BF_ST_INEXACT;
- return add_one;
-}
-
-/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is
- assumed to have length 'l' (1 <= l <= r->len). prec1 can be
- BF_PREC_INF. BF_FLAG_SUBNORMAL is not supported. Cannot fail with
- BF_ST_MEM_ERROR.
- */
-static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l)
-{
- int shift, add_one, rnd_mode, ret;
- slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec;
-
- /* XXX: align to IEEE 754 2008 for decimal numbers ? */
- e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1);
- e_min = -e_range + 3;
- e_max = e_range;
-
- if (flags & BF_FLAG_RADPNT_PREC) {
- /* 'prec' is the precision after the decimal point */
- if (prec1 != BF_PREC_INF)
- prec = r->expn + prec1;
- else
- prec = prec1;
- } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) {
- /* restrict the precision in case of potentially subnormal
- result */
- assert(prec1 != BF_PREC_INF);
- prec = prec1 - (e_min - r->expn);
- } else {
- prec = prec1;
- }
-
- /* round to prec bits */
- rnd_mode = flags & BF_RND_MASK;
- ret = 0;
- add_one = bfdec_get_rnd_add(&ret, r, l, prec, rnd_mode);
-
- if (prec <= 0) {
- if (add_one) {
- bfdec_resize(r, 1); /* cannot fail because r is non zero */
- r->tab[0] = BF_DEC_BASE / 10;
- r->expn += 1 - prec;
- ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT;
- return ret;
- } else {
- goto underflow;
- }
- } else if (add_one) {
- limb_t carry;
-
- /* add one starting at digit 'prec - 1' */
- bit_pos = l * LIMB_DIGITS - 1 - (prec - 1);
- pos = bit_pos / LIMB_DIGITS;
- carry = mp_pow_dec[bit_pos % LIMB_DIGITS];
- carry = mp_add_ui_dec(r->tab + pos, carry, l - pos);
- if (carry) {
- /* shift right by one digit */
- mp_shr_dec(r->tab + pos, r->tab + pos, l - pos, 1, 1);
- r->expn++;
- }
- }
-
- /* check underflow */
- if (unlikely(r->expn < e_min)) {
- if (flags & BF_FLAG_SUBNORMAL) {
- /* if inexact, also set the underflow flag */
- if (ret & BF_ST_INEXACT)
- ret |= BF_ST_UNDERFLOW;
- } else {
- underflow:
- bfdec_set_zero(r, r->sign);
- ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT;
- return ret;
- }
- }
-
- /* check overflow */
- if (unlikely(r->expn > e_max)) {
- bfdec_set_inf(r, r->sign);
- ret |= BF_ST_OVERFLOW | BF_ST_INEXACT;
- return ret;
- }
-
- /* keep the bits starting at 'prec - 1' */
- bit_pos = l * LIMB_DIGITS - 1 - (prec - 1);
- i = floor_div(bit_pos, LIMB_DIGITS);
- if (i >= 0) {
- shift = smod(bit_pos, LIMB_DIGITS);
- if (shift != 0) {
- r->tab[i] = fast_shr_dec(r->tab[i], shift) *
- mp_pow_dec[shift];
- }
- } else {
- i = 0;
- }
- /* remove trailing zeros */
- while (r->tab[i] == 0)
- i++;
- if (i > 0) {
- l -= i;
- memmove(r->tab, r->tab + i, l * sizeof(limb_t));
- }
- bfdec_resize(r, l); /* cannot fail */
- return ret;
-}
-
-/* Cannot fail with BF_ST_MEM_ERROR. */
-int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags)
-{
- if (r->len == 0)
- return 0;
- return __bfdec_round(r, prec, flags, r->len);
-}
-
-/* 'r' must be a finite number. Cannot fail with BF_ST_MEM_ERROR. */
-int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags)
-{
- limb_t l, v;
- int shift, ret;
-
- // bfdec_print_str("bf_renorm", r);
- l = r->len;
- while (l > 0 && r->tab[l - 1] == 0)
- l--;
- if (l == 0) {
- /* zero */
- r->expn = BF_EXP_ZERO;
- bfdec_resize(r, 0); /* cannot fail */
- ret = 0;
- } else {
- r->expn -= (r->len - l) * LIMB_DIGITS;
- /* shift to have the MSB set to '1' */
- v = r->tab[l - 1];
- shift = clz_dec(v);
- if (shift != 0) {
- mp_shl_dec(r->tab, r->tab, l, shift, 0);
- r->expn -= shift;
- }
- ret = __bfdec_round(r, prec1, flags, l);
- }
- // bf_print_str("r_final", r);
- return ret;
-}
-
-int bfdec_set_ui(bfdec_t *r, uint64_t v)
-{
-#if LIMB_BITS == 32
- if (v >= BF_DEC_BASE * BF_DEC_BASE) {
- if (bfdec_resize(r, 3))
- goto fail;
- r->tab[0] = v % BF_DEC_BASE;
- v /= BF_DEC_BASE;
- r->tab[1] = v % BF_DEC_BASE;
- r->tab[2] = v / BF_DEC_BASE;
- r->expn = 3 * LIMB_DIGITS;
- } else
-#endif
- if (v >= BF_DEC_BASE) {
- if (bfdec_resize(r, 2))
- goto fail;
- r->tab[0] = v % BF_DEC_BASE;
- r->tab[1] = v / BF_DEC_BASE;
- r->expn = 2 * LIMB_DIGITS;
- } else {
- if (bfdec_resize(r, 1))
- goto fail;
- r->tab[0] = v;
- r->expn = LIMB_DIGITS;
- }
- r->sign = 0;
- return bfdec_normalize_and_round(r, BF_PREC_INF, 0);
- fail:
- bfdec_set_nan(r);
- return BF_ST_MEM_ERROR;
-}
-
-int bfdec_set_si(bfdec_t *r, int64_t v)
-{
- int ret;
- if (v < 0) {
- ret = bfdec_set_ui(r, -v);
- r->sign = 1;
- } else {
- ret = bfdec_set_ui(r, v);
- }
- return ret;
-}
-
-static int bfdec_add_internal(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, bf_flags_t flags, int b_neg)
-{
- bf_context_t *s = r->ctx;
- int is_sub, cmp_res, a_sign, b_sign, ret;
-
- a_sign = a->sign;
- b_sign = b->sign ^ b_neg;
- is_sub = a_sign ^ b_sign;
- cmp_res = bfdec_cmpu(a, b);
- if (cmp_res < 0) {
- const bfdec_t *tmp;
- tmp = a;
- a = b;
- b = tmp;
- a_sign = b_sign; /* b_sign is never used later */
- }
- /* abs(a) >= abs(b) */
- if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) {
- /* zero result */
- bfdec_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD);
- ret = 0;
- } else if (a->len == 0 || b->len == 0) {
- ret = 0;
- if (a->expn >= BF_EXP_INF) {
- if (a->expn == BF_EXP_NAN) {
- /* at least one operand is NaN */
- bfdec_set_nan(r);
- ret = 0;
- } else if (b->expn == BF_EXP_INF && is_sub) {
- /* infinities with different signs */
- bfdec_set_nan(r);
- ret = BF_ST_INVALID_OP;
- } else {
- bfdec_set_inf(r, a_sign);
- }
- } else {
- /* at least one zero and not subtract */
- if (bfdec_set(r, a))
- return BF_ST_MEM_ERROR;
- r->sign = a_sign;
- goto renorm;
- }
- } else {
- slimb_t d, a_offset, b_offset, i, r_len;
- limb_t carry;
- limb_t *b1_tab;
- int b_shift;
- mp_size_t b1_len;
-
- d = a->expn - b->expn;
-
- /* XXX: not efficient in time and memory if the precision is
- not infinite */
- r_len = bf_max(a->len, b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS);
- if (bfdec_resize(r, r_len))
- goto fail;
- r->sign = a_sign;
- r->expn = a->expn;
-
- a_offset = r_len - a->len;
- for(i = 0; i < a_offset; i++)
- r->tab[i] = 0;
- for(i = 0; i < a->len; i++)
- r->tab[a_offset + i] = a->tab[i];
-
- b_shift = d % LIMB_DIGITS;
- if (b_shift == 0) {
- b1_len = b->len;
- b1_tab = (limb_t *)b->tab;
- } else {
- b1_len = b->len + 1;
- b1_tab = bf_malloc(s, sizeof(limb_t) * b1_len);
- if (!b1_tab)
- goto fail;
- b1_tab[0] = mp_shr_dec(b1_tab + 1, b->tab, b->len, b_shift, 0) *
- mp_pow_dec[LIMB_DIGITS - b_shift];
- }
- b_offset = r_len - (b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS);
-
- if (is_sub) {
- carry = mp_sub_dec(r->tab + b_offset, r->tab + b_offset,
- b1_tab, b1_len, 0);
- if (carry != 0) {
- carry = mp_sub_ui_dec(r->tab + b_offset + b1_len, carry,
- r_len - (b_offset + b1_len));
- assert(carry == 0);
- }
- } else {
- carry = mp_add_dec(r->tab + b_offset, r->tab + b_offset,
- b1_tab, b1_len, 0);
- if (carry != 0) {
- carry = mp_add_ui_dec(r->tab + b_offset + b1_len, carry,
- r_len - (b_offset + b1_len));
- }
- if (carry != 0) {
- if (bfdec_resize(r, r_len + 1)) {
- if (b_shift != 0)
- bf_free(s, b1_tab);
- goto fail;
- }
- r->tab[r_len] = 1;
- r->expn += LIMB_DIGITS;
- }
- }
- if (b_shift != 0)
- bf_free(s, b1_tab);
- renorm:
- ret = bfdec_normalize_and_round(r, prec, flags);
- }
- return ret;
- fail:
- bfdec_set_nan(r);
- return BF_ST_MEM_ERROR;
-}
-
-static int __bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
- bf_flags_t flags)
-{
- return bfdec_add_internal(r, a, b, prec, flags, 0);
-}
-
-static int __bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
- bf_flags_t flags)
-{
- return bfdec_add_internal(r, a, b, prec, flags, 1);
-}
-
-int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
- bf_flags_t flags)
-{
- return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags,
- (bf_op2_func_t *)__bfdec_add);
-}
-
-int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
- bf_flags_t flags)
-{
- return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags,
- (bf_op2_func_t *)__bfdec_sub);
-}
-
-int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
- bf_flags_t flags)
-{
- int ret, r_sign;
-
- if (a->len < b->len) {
- const bfdec_t *tmp = a;
- a = b;
- b = tmp;
- }
- r_sign = a->sign ^ b->sign;
- /* here b->len <= a->len */
- if (b->len == 0) {
- if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
- bfdec_set_nan(r);
- ret = 0;
- } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) {
- if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) ||
- (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) {
- bfdec_set_nan(r);
- ret = BF_ST_INVALID_OP;
- } else {
- bfdec_set_inf(r, r_sign);
- ret = 0;
- }
- } else {
- bfdec_set_zero(r, r_sign);
- ret = 0;
- }
- } else {
- bfdec_t tmp, *r1 = NULL;
- limb_t a_len, b_len;
- limb_t *a_tab, *b_tab;
-
- a_len = a->len;
- b_len = b->len;
- a_tab = a->tab;
- b_tab = b->tab;
-
- if (r == a || r == b) {
- bfdec_init(r->ctx, &tmp);
- r1 = r;
- r = &tmp;
- }
- if (bfdec_resize(r, a_len + b_len)) {
- bfdec_set_nan(r);
- ret = BF_ST_MEM_ERROR;
- goto done;
- }
- mp_mul_basecase_dec(r->tab, a_tab, a_len, b_tab, b_len);
- r->sign = r_sign;
- r->expn = a->expn + b->expn;
- ret = bfdec_normalize_and_round(r, prec, flags);
- done:
- if (r == &tmp)
- bfdec_move(r1, &tmp);
- }
- return ret;
-}
-
-int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
- bf_flags_t flags)
-{
- bfdec_t b;
- int ret;
- bfdec_init(r->ctx, &b);
- ret = bfdec_set_si(&b, b1);
- ret |= bfdec_mul(r, a, &b, prec, flags);
- bfdec_delete(&b);
- return ret;
-}
-
-int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
- bf_flags_t flags)
-{
- bfdec_t b;
- int ret;
-
- bfdec_init(r->ctx, &b);
- ret = bfdec_set_si(&b, b1);
- ret |= bfdec_add(r, a, &b, prec, flags);
- bfdec_delete(&b);
- return ret;
-}
-
-static int __bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
- limb_t prec, bf_flags_t flags)
-{
- int ret, r_sign;
- limb_t n, nb, precl;
-
- r_sign = a->sign ^ b->sign;
- if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) {
- if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
- bfdec_set_nan(r);
- return 0;
- } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) {
- bfdec_set_nan(r);
- return BF_ST_INVALID_OP;
- } else if (a->expn == BF_EXP_INF) {
- bfdec_set_inf(r, r_sign);
- return 0;
- } else {
- bfdec_set_zero(r, r_sign);
- return 0;
- }
- } else if (a->expn == BF_EXP_ZERO) {
- if (b->expn == BF_EXP_ZERO) {
- bfdec_set_nan(r);
- return BF_ST_INVALID_OP;
- } else {
- bfdec_set_zero(r, r_sign);
- return 0;
- }
- } else if (b->expn == BF_EXP_ZERO) {
- bfdec_set_inf(r, r_sign);
- return BF_ST_DIVIDE_ZERO;
- }
-
- nb = b->len;
- if (prec == BF_PREC_INF) {
- /* infinite precision: return BF_ST_INVALID_OP if not an exact
- result */
- /* XXX: check */
- precl = nb + 1;
- } else if (flags & BF_FLAG_RADPNT_PREC) {
- /* number of digits after the decimal point */
- /* XXX: check (2 extra digits for rounding + 2 digits) */
- precl = (bf_max(a->expn - b->expn, 0) + 2 +
- prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS;
- } else {
- /* number of limbs of the quotient (2 extra digits for rounding) */
- precl = (prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS;
- }
- n = bf_max(a->len, precl);
-
- {
- limb_t *taba, na, i;
- slimb_t d;
-
- na = n + nb;
- taba = bf_malloc(r->ctx, (na + 1) * sizeof(limb_t));
- if (!taba)
- goto fail;
- d = na - a->len;
- memset(taba, 0, d * sizeof(limb_t));
- memcpy(taba + d, a->tab, a->len * sizeof(limb_t));
- if (bfdec_resize(r, n + 1))
- goto fail1;
- if (mp_div_dec(r->ctx, r->tab, taba, na, b->tab, nb)) {
- fail1:
- bf_free(r->ctx, taba);
- goto fail;
- }
- /* see if non zero remainder */
- for(i = 0; i < nb; i++) {
- if (taba[i] != 0)
- break;
- }
- bf_free(r->ctx, taba);
- if (i != nb) {
- if (prec == BF_PREC_INF) {
- bfdec_set_nan(r);
- return BF_ST_INVALID_OP;
- } else {
- r->tab[0] |= 1;
- }
- }
- r->expn = a->expn - b->expn + LIMB_DIGITS;
- r->sign = r_sign;
- ret = bfdec_normalize_and_round(r, prec, flags);
- }
- return ret;
- fail:
- bfdec_set_nan(r);
- return BF_ST_MEM_ERROR;
-}
-
-int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
- bf_flags_t flags)
-{
- return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags,
- (bf_op2_func_t *)__bfdec_div);
-}
-
-/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the
- integer defined as floor(a/b) and r = a - q * b. */
-static void bfdec_tdivremu(bf_context_t *s, bfdec_t *q, bfdec_t *r,
- const bfdec_t *a, const bfdec_t *b)
-{
- if (bfdec_cmpu(a, b) < 0) {
- bfdec_set_ui(q, 0);
- bfdec_set(r, a);
- } else {
- bfdec_div(q, a, b, 0, BF_RNDZ | BF_FLAG_RADPNT_PREC);
- bfdec_mul(r, q, b, BF_PREC_INF, BF_RNDZ);
- bfdec_sub(r, a, r, BF_PREC_INF, BF_RNDZ);
- }
-}
-
-/* division and remainder.
-
- rnd_mode is the rounding mode for the quotient. The additional
- rounding mode BF_RND_EUCLIDIAN is supported.
-
- 'q' is an integer. 'r' is rounded with prec and flags (prec can be
- BF_PREC_INF).
-*/
-int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
- limb_t prec, bf_flags_t flags, int rnd_mode)
-{
- bf_context_t *s = q->ctx;
- bfdec_t a1_s, *a1 = &a1_s;
- bfdec_t b1_s, *b1 = &b1_s;
- bfdec_t r1_s, *r1 = &r1_s;
- int q_sign, res;
- BOOL is_ceil, is_rndn;
-
- assert(q != a && q != b);
- assert(r != a && r != b);
- assert(q != r);
-
- if (a->len == 0 || b->len == 0) {
- bfdec_set_zero(q, 0);
- if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
- bfdec_set_nan(r);
- return 0;
- } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) {
- bfdec_set_nan(r);
- return BF_ST_INVALID_OP;
- } else {
- bfdec_set(r, a);
- return bfdec_round(r, prec, flags);
- }
- }
-
- q_sign = a->sign ^ b->sign;
- is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA);
- switch(rnd_mode) {
- default:
- case BF_RNDZ:
- case BF_RNDN:
- case BF_RNDNA:
- is_ceil = FALSE;
- break;
- case BF_RNDD:
- is_ceil = q_sign;
- break;
- case BF_RNDU:
- is_ceil = q_sign ^ 1;
- break;
- case BF_RNDA:
- is_ceil = TRUE;
- break;
- case BF_DIVREM_EUCLIDIAN:
- is_ceil = a->sign;
- break;
- }
-
- a1->expn = a->expn;
- a1->tab = a->tab;
- a1->len = a->len;
- a1->sign = 0;
-
- b1->expn = b->expn;
- b1->tab = b->tab;
- b1->len = b->len;
- b1->sign = 0;
-
- // bfdec_print_str("a1", a1);
- // bfdec_print_str("b1", b1);
- /* XXX: could improve to avoid having a large 'q' */
- bfdec_tdivremu(s, q, r, a1, b1);
- if (bfdec_is_nan(q) || bfdec_is_nan(r))
- goto fail;
- // bfdec_print_str("q", q);
- // bfdec_print_str("r", r);
-
- if (r->len != 0) {
- if (is_rndn) {
- bfdec_init(s, r1);
- if (bfdec_set(r1, r))
- goto fail;
- if (bfdec_mul_si(r1, r1, 2, BF_PREC_INF, BF_RNDZ)) {
- bfdec_delete(r1);
- goto fail;
- }
- res = bfdec_cmpu(r1, b);
- bfdec_delete(r1);
- if (res > 0 ||
- (res == 0 &&
- (rnd_mode == BF_RNDNA ||
- (get_digit(q->tab, q->len, q->len * LIMB_DIGITS - q->expn) & 1) != 0))) {
- goto do_sub_r;
- }
- } else if (is_ceil) {
- do_sub_r:
- res = bfdec_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ);
- res |= bfdec_sub(r, r, b1, BF_PREC_INF, BF_RNDZ);
- if (res & BF_ST_MEM_ERROR)
- goto fail;
- }
- }
-
- r->sign ^= a->sign;
- q->sign = q_sign;
- return bfdec_round(r, prec, flags);
- fail:
- bfdec_set_nan(q);
- bfdec_set_nan(r);
- return BF_ST_MEM_ERROR;
-}
-
-int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
- bf_flags_t flags, int rnd_mode)
-{
- bfdec_t q_s, *q = &q_s;
- int ret;
-
- bfdec_init(r->ctx, q);
- ret = bfdec_divrem(q, r, a, b, prec, flags, rnd_mode);
- bfdec_delete(q);
- return ret;
-}
-
-/* convert to integer (infinite precision) */
-int bfdec_rint(bfdec_t *r, int rnd_mode)
-{
- return bfdec_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC);
-}
-
-int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags)
-{
- bf_context_t *s = a->ctx;
- int ret, k;
- limb_t *a1, v;
- slimb_t n, n1, prec1;
- limb_t res;
-
- assert(r != a);
-
- if (a->len == 0) {
- if (a->expn == BF_EXP_NAN) {
- bfdec_set_nan(r);
- } else if (a->expn == BF_EXP_INF && a->sign) {
- goto invalid_op;
- } else {
- bfdec_set(r, a);
- }
- ret = 0;
- } else if (a->sign || prec == BF_PREC_INF) {
- invalid_op:
- bfdec_set_nan(r);
- ret = BF_ST_INVALID_OP;
- } else {
- if (flags & BF_FLAG_RADPNT_PREC) {
- prec1 = bf_max(floor_div(a->expn + 1, 2) + prec, 1);
- } else {
- prec1 = prec;
- }
- /* convert the mantissa to an integer with at least 2 *
- prec + 4 digits */
- n = (2 * (prec1 + 2) + 2 * LIMB_DIGITS - 1) / (2 * LIMB_DIGITS);
- if (bfdec_resize(r, n))
- goto fail;
- a1 = bf_malloc(s, sizeof(limb_t) * 2 * n);
- if (!a1)
- goto fail;
- n1 = bf_min(2 * n, a->len);
- memset(a1, 0, (2 * n - n1) * sizeof(limb_t));
- memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t));
- if (a->expn & 1) {
- res = mp_shr_dec(a1, a1, 2 * n, 1, 0);
- } else {
- res = 0;
- }
- /* normalize so that a1 >= B^(2*n)/4. Not need for n = 1
- because mp_sqrtrem2_dec already does it */
- k = 0;
- if (n > 1) {
- v = a1[2 * n - 1];
- while (v < BF_DEC_BASE / 4) {
- k++;
- v *= 4;
- }
- if (k != 0)
- mp_mul1_dec(a1, a1, 2 * n, 1 << (2 * k), 0);
- }
- if (mp_sqrtrem_dec(s, r->tab, a1, n)) {
- bf_free(s, a1);
- goto fail;
- }
- if (k != 0)
- mp_div1_dec(r->tab, r->tab, n, 1 << k, 0);
- if (!res) {
- res = mp_scan_nz(a1, n + 1);
- }
- bf_free(s, a1);
- if (!res) {
- res = mp_scan_nz(a->tab, a->len - n1);
- }
- if (res != 0)
- r->tab[0] |= 1;
- r->sign = 0;
- r->expn = (a->expn + 1) >> 1;
- ret = bfdec_round(r, prec, flags);
- }
- return ret;
- fail:
- bfdec_set_nan(r);
- return BF_ST_MEM_ERROR;
-}
-
-/* The rounding mode is always BF_RNDZ. Return BF_ST_OVERFLOW if there
- is an overflow and 0 otherwise. No memory error is possible. */
-int bfdec_get_int32(int *pres, const bfdec_t *a)
-{
- uint32_t v;
- int ret;
- if (a->expn >= BF_EXP_INF) {
- ret = 0;
- if (a->expn == BF_EXP_INF) {
- v = (uint32_t)INT32_MAX + a->sign;
- /* XXX: return overflow ? */
- } else {
- v = INT32_MAX;
- }
- } else if (a->expn <= 0) {
- v = 0;
- ret = 0;
- } else if (a->expn <= 9) {
- v = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn);
- if (a->sign)
- v = -v;
- ret = 0;
- } else if (a->expn == 10) {
- uint64_t v1;
- uint32_t v_max;
-#if LIMB_BITS == 64
- v1 = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn);
-#else
- v1 = (uint64_t)a->tab[a->len - 1] * 10 +
- get_digit(a->tab, a->len, (a->len - 1) * LIMB_DIGITS - 1);
-#endif
- v_max = (uint32_t)INT32_MAX + a->sign;
- if (v1 > v_max) {
- v = v_max;
- ret = BF_ST_OVERFLOW;
- } else {
- v = v1;
- if (a->sign)
- v = -v;
- ret = 0;
- }
- } else {
- v = (uint32_t)INT32_MAX + a->sign;
- ret = BF_ST_OVERFLOW;
- }
- *pres = v;
- return ret;
-}
-
-/* power to an integer with infinite precision */
-int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b)
-{
- int ret, n_bits, i;
-
- assert(r != a);
- if (b == 0)
- return bfdec_set_ui(r, 1);
- ret = bfdec_set(r, a);
- n_bits = LIMB_BITS - clz(b);
- for(i = n_bits - 2; i >= 0; i--) {
- ret |= bfdec_mul(r, r, r, BF_PREC_INF, BF_RNDZ);
- if ((b >> i) & 1)
- ret |= bfdec_mul(r, r, a, BF_PREC_INF, BF_RNDZ);
- }
- return ret;
-}
-
-char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags)
-{
- return bf_ftoa_internal(plen, (const bf_t *)a, 10, prec, flags, TRUE);
-}
-
-int bfdec_atof(bfdec_t *r, const char *str, const char **pnext,
- limb_t prec, bf_flags_t flags)
-{
- slimb_t dummy_exp;
- return bf_atof_internal((bf_t *)r, &dummy_exp, str, pnext, 10, prec,
- flags, TRUE);
-}
-
-#endif /* USE_BF_DEC */
-
-#ifdef USE_FFT_MUL
-/***************************************************************/
-/* Integer multiplication with FFT */
-
-/* or LIMB_BITS at bit position 'pos' in tab */
-static inline void put_bits(limb_t *tab, limb_t len, slimb_t pos, limb_t val)
-{
- limb_t i;
- int p;
-
- i = pos >> LIMB_LOG2_BITS;
- p = pos & (LIMB_BITS - 1);
- if (i < len)
- tab[i] |= val << p;
- if (p != 0) {
- i++;
- if (i < len) {
- tab[i] |= val >> (LIMB_BITS - p);
- }
- }
-}
-
-#if defined(__AVX2__)
-
-typedef double NTTLimb;
-
-/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */
-#define NTT_MOD_LOG2_MIN 50
-#define NTT_MOD_LOG2_MAX 51
-#define NB_MODS 5
-#define NTT_PROOT_2EXP 39
-static const int ntt_int_bits[NB_MODS] = { 254, 203, 152, 101, 50, };
-
-static const limb_t ntt_mods[NB_MODS] = { 0x00073a8000000001, 0x0007858000000001, 0x0007a38000000001, 0x0007a68000000001, 0x0007fd8000000001,
-};
-
-static const limb_t ntt_proot[2][NB_MODS] = {
- { 0x00056198d44332c8, 0x0002eb5d640aad39, 0x00047e31eaa35fd0, 0x0005271ac118a150, 0x00075e0ce8442bd5, },
- { 0x000461169761bcc5, 0x0002dac3cb2da688, 0x0004abc97751e3bf, 0x000656778fc8c485, 0x0000dc6469c269fa, },
-};
-
-static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = {
- 0x00020e4da740da8e, 0x0004c3dc09c09c1d, 0x000063bd097b4271, 0x000799d8f18f18fd,
- 0x0005384222222264, 0x000572b07c1f07fe, 0x00035cd08888889a,
- 0x00066015555557e3, 0x000725960b60b623,
- 0x0002fc1fa1d6ce12,
-};
-
-#else
-
-typedef limb_t NTTLimb;
-
-#if LIMB_BITS == 64
-
-#define NTT_MOD_LOG2_MIN 61
-#define NTT_MOD_LOG2_MAX 62
-#define NB_MODS 5
-#define NTT_PROOT_2EXP 51
-static const int ntt_int_bits[NB_MODS] = { 307, 246, 185, 123, 61, };
-
-static const limb_t ntt_mods[NB_MODS] = { 0x28d8000000000001, 0x2a88000000000001, 0x2ed8000000000001, 0x3508000000000001, 0x3aa8000000000001,
-};
-
-static const limb_t ntt_proot[2][NB_MODS] = {
- { 0x1b8ea61034a2bea7, 0x21a9762de58206fb, 0x02ca782f0756a8ea, 0x278384537a3e50a1, 0x106e13fee74ce0ab, },
- { 0x233513af133e13b8, 0x1d13140d1c6f75f1, 0x12cde57f97e3eeda, 0x0d6149e23cbe654f, 0x36cd204f522a1379, },
-};
-
-static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = {
- 0x08a9ed097b425eea, 0x18a44aaaaaaaaab3, 0x2493f57f57f57f5d, 0x126b8d0649a7f8d4,
- 0x09d80ed7303b5ccc, 0x25b8bcf3cf3cf3d5, 0x2ce6ce63398ce638,
- 0x0e31fad40a57eb59, 0x02a3529fd4a7f52f,
- 0x3a5493e93e93e94a,
-};
-
-#elif LIMB_BITS == 32
-
-/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */
-#define NTT_MOD_LOG2_MIN 29
-#define NTT_MOD_LOG2_MAX 30
-#define NB_MODS 5
-#define NTT_PROOT_2EXP 20
-static const int ntt_int_bits[NB_MODS] = { 148, 119, 89, 59, 29, };
-
-static const limb_t ntt_mods[NB_MODS] = { 0x0000000032b00001, 0x0000000033700001, 0x0000000036d00001, 0x0000000037300001, 0x000000003e500001,
-};
-
-static const limb_t ntt_proot[2][NB_MODS] = {
- { 0x0000000032525f31, 0x0000000005eb3b37, 0x00000000246eda9f, 0x0000000035f25901, 0x00000000022f5768, },
- { 0x00000000051eba1a, 0x00000000107be10e, 0x000000001cd574e0, 0x00000000053806e6, 0x000000002cd6bf98, },
-};
-
-static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = {
- 0x000000000449559a, 0x000000001eba6ca9, 0x000000002ec18e46, 0x000000000860160b,
- 0x000000000d321307, 0x000000000bf51120, 0x000000000f662938,
- 0x000000000932ab3e, 0x000000002f40eef8,
- 0x000000002e760905,
-};
-
-#endif /* LIMB_BITS */
-
-#endif /* !AVX2 */
-
-#if defined(__AVX2__)
-#define NTT_TRIG_K_MAX 18
-#else
-#define NTT_TRIG_K_MAX 19
-#endif
-
-typedef struct BFNTTState {
- bf_context_t *ctx;
-
- /* used for mul_mod_fast() */
- limb_t ntt_mods_div[NB_MODS];
-
- limb_t ntt_proot_pow[NB_MODS][2][NTT_PROOT_2EXP + 1];
- limb_t ntt_proot_pow_inv[NB_MODS][2][NTT_PROOT_2EXP + 1];
- NTTLimb *ntt_trig[NB_MODS][2][NTT_TRIG_K_MAX + 1];
- /* 1/2^n mod m */
- limb_t ntt_len_inv[NB_MODS][NTT_PROOT_2EXP + 1][2];
-#if defined(__AVX2__)
- __m256d ntt_mods_cr_vec[NB_MODS * (NB_MODS - 1) / 2];
- __m256d ntt_mods_vec[NB_MODS];
- __m256d ntt_mods_inv_vec[NB_MODS];
-#else
- limb_t ntt_mods_cr_inv[NB_MODS * (NB_MODS - 1) / 2];
-#endif
-} BFNTTState;
-
-static NTTLimb *get_trig(BFNTTState *s, int k, int inverse, int m_idx);
-
-/* add modulo with up to (LIMB_BITS-1) bit modulo */
-static inline limb_t add_mod(limb_t a, limb_t b, limb_t m)
-{
- limb_t r;
- r = a + b;
- if (r >= m)
- r -= m;
- return r;
-}
-
-/* sub modulo with up to LIMB_BITS bit modulo */
-static inline limb_t sub_mod(limb_t a, limb_t b, limb_t m)
-{
- limb_t r;
- r = a - b;
- if (r > a)
- r += m;
- return r;
-}
-
-/* return (r0+r1*B) mod m
- precondition: 0 <= r0+r1*B < 2^(64+NTT_MOD_LOG2_MIN)
-*/
-static inline limb_t mod_fast(dlimb_t r,
- limb_t m, limb_t m_inv)
-{
- limb_t a1, q, t0, r1, r0;
-
- a1 = r >> NTT_MOD_LOG2_MIN;
-
- q = ((dlimb_t)a1 * m_inv) >> LIMB_BITS;
- r = r - (dlimb_t)q * m - m * 2;
- r1 = r >> LIMB_BITS;
- t0 = (slimb_t)r1 >> 1;
- r += m & t0;
- r0 = r;
- r1 = r >> LIMB_BITS;
- r0 += m & r1;
- return r0;
-}
-
-/* faster version using precomputed modulo inverse.
- precondition: 0 <= a * b < 2^(64+NTT_MOD_LOG2_MIN) */
-static inline limb_t mul_mod_fast(limb_t a, limb_t b,
- limb_t m, limb_t m_inv)
-{
- dlimb_t r;
- r = (dlimb_t)a * (dlimb_t)b;
- return mod_fast(r, m, m_inv);
-}
-
-static inline limb_t init_mul_mod_fast(limb_t m)
-{
- dlimb_t t;
- assert(m < (limb_t)1 << NTT_MOD_LOG2_MAX);
- assert(m >= (limb_t)1 << NTT_MOD_LOG2_MIN);
- t = (dlimb_t)1 << (LIMB_BITS + NTT_MOD_LOG2_MIN);
- return t / m;
-}
-
-/* Faster version used when the multiplier is constant. 0 <= a < 2^64,
- 0 <= b < m. */
-static inline limb_t mul_mod_fast2(limb_t a, limb_t b,
- limb_t m, limb_t b_inv)
-{
- limb_t r, q;
-
- q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS;
- r = a * b - q * m;
- if (r >= m)
- r -= m;
- return r;
-}
-
-/* Faster version used when the multiplier is constant. 0 <= a < 2^64,
- 0 <= b < m. Let r = a * b mod m. The return value is 'r' or 'r +
- m'. */
-static inline limb_t mul_mod_fast3(limb_t a, limb_t b,
- limb_t m, limb_t b_inv)
-{
- limb_t r, q;
-
- q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS;
- r = a * b - q * m;
- return r;
-}
-
-static inline limb_t init_mul_mod_fast2(limb_t b, limb_t m)
-{
- return ((dlimb_t)b << LIMB_BITS) / m;
-}
-
-#ifdef __AVX2__
-
-static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m)
-{
- slimb_t v;
- v = a;
- if (v < 0)
- v += m;
- if (v >= m)
- v -= m;
- return v;
-}
-
-static inline NTTLimb int_to_ntt_limb(limb_t a, limb_t m)
-{
- return (slimb_t)a;
-}
-
-static inline NTTLimb int_to_ntt_limb2(limb_t a, limb_t m)
-{
- if (a >= (m / 2))
- a -= m;
- return (slimb_t)a;
-}
-
-/* return r + m if r < 0 otherwise r. */
-static inline __m256d ntt_mod1(__m256d r, __m256d m)
-{
- return _mm256_blendv_pd(r, r + m, r);
-}
-
-/* input: abs(r) < 2 * m. Output: abs(r) < m */
-static inline __m256d ntt_mod(__m256d r, __m256d mf, __m256d m2f)
-{
- return _mm256_blendv_pd(r, r + m2f, r) - mf;
-}
-
-/* input: abs(a*b) < 2 * m^2, output: abs(r) < m */
-static inline __m256d ntt_mul_mod(__m256d a, __m256d b, __m256d mf,
- __m256d m_inv)
-{
- __m256d r, q, ab1, ab0, qm0, qm1;
- ab1 = a * b;
- q = _mm256_round_pd(ab1 * m_inv, 0); /* round to nearest */
- qm1 = q * mf;
- qm0 = _mm256_fmsub_pd(q, mf, qm1); /* low part */
- ab0 = _mm256_fmsub_pd(a, b, ab1); /* low part */
- r = (ab1 - qm1) + (ab0 - qm0);
- return r;
-}
-
-static void *bf_aligned_malloc(bf_context_t *s, size_t size, size_t align)
-{
- void *ptr;
- void **ptr1;
- ptr = bf_malloc(s, size + sizeof(void *) + align - 1);
- if (!ptr)
- return NULL;
- ptr1 = (void **)(((uintptr_t)ptr + sizeof(void *) + align - 1) &
- ~(align - 1));
- ptr1[-1] = ptr;
- return ptr1;
-}
-
-static void bf_aligned_free(bf_context_t *s, void *ptr)
-{
- if (!ptr)
- return;
- bf_free(s, ((void **)ptr)[-1]);
-}
-
-static void *ntt_malloc(BFNTTState *s, size_t size)
-{
- return bf_aligned_malloc(s->ctx, size, 64);
-}
-
-static void ntt_free(BFNTTState *s, void *ptr)
-{
- bf_aligned_free(s->ctx, ptr);
-}
-
-static no_inline int ntt_fft(BFNTTState *s,
- NTTLimb *out_buf, NTTLimb *in_buf,
- NTTLimb *tmp_buf, int fft_len_log2,
- int inverse, int m_idx)
-{
- limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j;
- NTTLimb *tab_in, *tab_out, *tmp, *trig;
- __m256d m_inv, mf, m2f, c, a0, a1, b0, b1;
- limb_t m;
- int l;
-
- m = ntt_mods[m_idx];
-
- m_inv = _mm256_set1_pd(1.0 / (double)m);
- mf = _mm256_set1_pd(m);
- m2f = _mm256_set1_pd(m * 2);
-
- n = (limb_t)1 << fft_len_log2;
- assert(n >= 8);
- stride_in = n / 2;
-
- tab_in = in_buf;
- tab_out = tmp_buf;
- trig = get_trig(s, fft_len_log2, inverse, m_idx);
- if (!trig)
- return -1;
- p = 0;
- for(k = 0; k < stride_in; k += 4) {
- a0 = _mm256_load_pd(&tab_in[k]);
- a1 = _mm256_load_pd(&tab_in[k + stride_in]);
- c = _mm256_load_pd(trig);
- trig += 4;
- b0 = ntt_mod(a0 + a1, mf, m2f);
- b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv);
- a0 = _mm256_permute2f128_pd(b0, b1, 0x20);
- a1 = _mm256_permute2f128_pd(b0, b1, 0x31);
- a0 = _mm256_permute4x64_pd(a0, 0xd8);
- a1 = _mm256_permute4x64_pd(a1, 0xd8);
- _mm256_store_pd(&tab_out[p], a0);
- _mm256_store_pd(&tab_out[p + 4], a1);
- p += 2 * 4;
- }
- tmp = tab_in;
- tab_in = tab_out;
- tab_out = tmp;
-
- trig = get_trig(s, fft_len_log2 - 1, inverse, m_idx);
- if (!trig)
- return -1;
- p = 0;
- for(k = 0; k < stride_in; k += 4) {
- a0 = _mm256_load_pd(&tab_in[k]);
- a1 = _mm256_load_pd(&tab_in[k + stride_in]);
- c = _mm256_setr_pd(trig[0], trig[0], trig[1], trig[1]);
- trig += 2;
- b0 = ntt_mod(a0 + a1, mf, m2f);
- b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv);
- a0 = _mm256_permute2f128_pd(b0, b1, 0x20);
- a1 = _mm256_permute2f128_pd(b0, b1, 0x31);
- _mm256_store_pd(&tab_out[p], a0);
- _mm256_store_pd(&tab_out[p + 4], a1);
- p += 2 * 4;
- }
- tmp = tab_in;
- tab_in = tab_out;
- tab_out = tmp;
-
- nb_blocks = n / 4;
- fft_per_block = 4;
-
- l = fft_len_log2 - 2;
- while (nb_blocks != 2) {
- nb_blocks >>= 1;
- p = 0;
- k = 0;
- trig = get_trig(s, l, inverse, m_idx);
- if (!trig)
- return -1;
- for(i = 0; i < nb_blocks; i++) {
- c = _mm256_set1_pd(trig[0]);
- trig++;
- for(j = 0; j < fft_per_block; j += 4) {
- a0 = _mm256_load_pd(&tab_in[k + j]);
- a1 = _mm256_load_pd(&tab_in[k + j + stride_in]);
- b0 = ntt_mod(a0 + a1, mf, m2f);
- b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv);
- _mm256_store_pd(&tab_out[p + j], b0);
- _mm256_store_pd(&tab_out[p + j + fft_per_block], b1);
- }
- k += fft_per_block;
- p += 2 * fft_per_block;
- }
- fft_per_block <<= 1;
- l--;
- tmp = tab_in;
- tab_in = tab_out;
- tab_out = tmp;
- }
-
- tab_out = out_buf;
- for(k = 0; k < stride_in; k += 4) {
- a0 = _mm256_load_pd(&tab_in[k]);
- a1 = _mm256_load_pd(&tab_in[k + stride_in]);
- b0 = ntt_mod(a0 + a1, mf, m2f);
- b1 = ntt_mod(a0 - a1, mf, m2f);
- _mm256_store_pd(&tab_out[k], b0);
- _mm256_store_pd(&tab_out[k + stride_in], b1);
- }
- return 0;
-}
-
-static void ntt_vec_mul(BFNTTState *s,
- NTTLimb *tab1, NTTLimb *tab2, limb_t fft_len_log2,
- int k_tot, int m_idx)
-{
- limb_t i, c_inv, n, m;
- __m256d m_inv, mf, a, b, c;
-
- m = ntt_mods[m_idx];
- c_inv = s->ntt_len_inv[m_idx][k_tot][0];
- m_inv = _mm256_set1_pd(1.0 / (double)m);
- mf = _mm256_set1_pd(m);
- c = _mm256_set1_pd(int_to_ntt_limb(c_inv, m));
- n = (limb_t)1 << fft_len_log2;
- for(i = 0; i < n; i += 4) {
- a = _mm256_load_pd(&tab1[i]);
- b = _mm256_load_pd(&tab2[i]);
- a = ntt_mul_mod(a, b, mf, m_inv);
- a = ntt_mul_mod(a, c, mf, m_inv);
- _mm256_store_pd(&tab1[i], a);
- }
-}
-
-static no_inline void mul_trig(NTTLimb *buf,
- limb_t n, limb_t c1, limb_t m, limb_t m_inv1)
-{
- limb_t i, c2, c3, c4;
- __m256d c, c_mul, a0, mf, m_inv;
- assert(n >= 2);
-
- mf = _mm256_set1_pd(m);
- m_inv = _mm256_set1_pd(1.0 / (double)m);
-
- c2 = mul_mod_fast(c1, c1, m, m_inv1);
- c3 = mul_mod_fast(c2, c1, m, m_inv1);
- c4 = mul_mod_fast(c2, c2, m, m_inv1);
- c = _mm256_setr_pd(1, int_to_ntt_limb(c1, m),
- int_to_ntt_limb(c2, m), int_to_ntt_limb(c3, m));
- c_mul = _mm256_set1_pd(int_to_ntt_limb(c4, m));
- for(i = 0; i < n; i += 4) {
- a0 = _mm256_load_pd(&buf[i]);
- a0 = ntt_mul_mod(a0, c, mf, m_inv);
- _mm256_store_pd(&buf[i], a0);
- c = ntt_mul_mod(c, c_mul, mf, m_inv);
- }
-}
-
-#else
-
-static void *ntt_malloc(BFNTTState *s, size_t size)
-{
- return bf_malloc(s->ctx, size);
-}
-
-static void ntt_free(BFNTTState *s, void *ptr)
-{
- bf_free(s->ctx, ptr);
-}
-
-static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m)
-{
- if (a >= m)
- a -= m;
- return a;
-}
-
-static inline NTTLimb int_to_ntt_limb(slimb_t a, limb_t m)
-{
- return a;
-}
-
-static no_inline int ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf,
- NTTLimb *tmp_buf, int fft_len_log2,
- int inverse, int m_idx)
-{
- limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j, m, m2;
- NTTLimb *tab_in, *tab_out, *tmp, a0, a1, b0, b1, c, *trig, c_inv;
- int l;
-
- m = ntt_mods[m_idx];
- m2 = 2 * m;
- n = (limb_t)1 << fft_len_log2;
- nb_blocks = n;
- fft_per_block = 1;
- stride_in = n / 2;
- tab_in = in_buf;
- tab_out = tmp_buf;
- l = fft_len_log2;
- while (nb_blocks != 2) {
- nb_blocks >>= 1;
- p = 0;
- k = 0;
- trig = get_trig(s, l, inverse, m_idx);
- if (!trig)
- return -1;
- for(i = 0; i < nb_blocks; i++) {
- c = trig[0];
- c_inv = trig[1];
- trig += 2;
- for(j = 0; j < fft_per_block; j++) {
- a0 = tab_in[k + j];
- a1 = tab_in[k + j + stride_in];
- b0 = add_mod(a0, a1, m2);
- b1 = a0 - a1 + m2;
- b1 = mul_mod_fast3(b1, c, m, c_inv);
- tab_out[p + j] = b0;
- tab_out[p + j + fft_per_block] = b1;
- }
- k += fft_per_block;
- p += 2 * fft_per_block;
- }
- fft_per_block <<= 1;
- l--;
- tmp = tab_in;
- tab_in = tab_out;
- tab_out = tmp;
- }
- /* no twiddle in last step */
- tab_out = out_buf;
- for(k = 0; k < stride_in; k++) {
- a0 = tab_in[k];
- a1 = tab_in[k + stride_in];
- b0 = add_mod(a0, a1, m2);
- b1 = sub_mod(a0, a1, m2);
- tab_out[k] = b0;
- tab_out[k + stride_in] = b1;
- }
- return 0;
-}
-
-static void ntt_vec_mul(BFNTTState *s,
- NTTLimb *tab1, NTTLimb *tab2, int fft_len_log2,
- int k_tot, int m_idx)
-{
- limb_t i, norm, norm_inv, a, n, m, m_inv;
-
- m = ntt_mods[m_idx];
- m_inv = s->ntt_mods_div[m_idx];
- norm = s->ntt_len_inv[m_idx][k_tot][0];
- norm_inv = s->ntt_len_inv[m_idx][k_tot][1];
- n = (limb_t)1 << fft_len_log2;
- for(i = 0; i < n; i++) {
- a = tab1[i];
- /* need to reduce the range so that the product is <
- 2^(LIMB_BITS+NTT_MOD_LOG2_MIN) */
- if (a >= m)
- a -= m;
- a = mul_mod_fast(a, tab2[i], m, m_inv);
- a = mul_mod_fast3(a, norm, m, norm_inv);
- tab1[i] = a;
- }
-}
-
-static no_inline void mul_trig(NTTLimb *buf,
- limb_t n, limb_t c_mul, limb_t m, limb_t m_inv)
-{
- limb_t i, c0, c_mul_inv;
-
- c0 = 1;
- c_mul_inv = init_mul_mod_fast2(c_mul, m);
- for(i = 0; i < n; i++) {
- buf[i] = mul_mod_fast(buf[i], c0, m, m_inv);
- c0 = mul_mod_fast2(c0, c_mul, m, c_mul_inv);
- }
-}
-
-#endif /* !AVX2 */
-
-static no_inline NTTLimb *get_trig(BFNTTState *s,
- int k, int inverse, int m_idx)
-{
- NTTLimb *tab;
- limb_t i, n2, c, c_mul, m, c_mul_inv;
-
- if (k > NTT_TRIG_K_MAX)
- return NULL;
-
- tab = s->ntt_trig[m_idx][inverse][k];
- if (tab)
- return tab;
- n2 = (limb_t)1 << (k - 1);
- m = ntt_mods[m_idx];
-#ifdef __AVX2__
- tab = ntt_malloc(s, sizeof(NTTLimb) * n2);
-#else
- tab = ntt_malloc(s, sizeof(NTTLimb) * n2 * 2);
-#endif
- if (!tab)
- return NULL;
- c = 1;
- c_mul = s->ntt_proot_pow[m_idx][inverse][k];
- c_mul_inv = s->ntt_proot_pow_inv[m_idx][inverse][k];
- for(i = 0; i < n2; i++) {
-#ifdef __AVX2__
- tab[i] = int_to_ntt_limb2(c, m);
-#else
- tab[2 * i] = int_to_ntt_limb(c, m);
- tab[2 * i + 1] = init_mul_mod_fast2(c, m);
-#endif
- c = mul_mod_fast2(c, c_mul, m, c_mul_inv);
- }
- s->ntt_trig[m_idx][inverse][k] = tab;
- return tab;
-}
-
-void fft_clear_cache(bf_context_t *s1)
-{
- int m_idx, inverse, k;
- BFNTTState *s = s1->ntt_state;
- if (s) {
- for(m_idx = 0; m_idx < NB_MODS; m_idx++) {
- for(inverse = 0; inverse < 2; inverse++) {
- for(k = 0; k < NTT_TRIG_K_MAX + 1; k++) {
- if (s->ntt_trig[m_idx][inverse][k]) {
- ntt_free(s, s->ntt_trig[m_idx][inverse][k]);
- s->ntt_trig[m_idx][inverse][k] = NULL;
- }
- }
- }
- }
-#if defined(__AVX2__)
- bf_aligned_free(s1, s);
-#else
- bf_free(s1, s);
-#endif
- s1->ntt_state = NULL;
- }
-}
-
-#define STRIP_LEN 16
-
-/* dst = buf1, src = buf2 */
-static int ntt_fft_partial(BFNTTState *s, NTTLimb *buf1,
- int k1, int k2, limb_t n1, limb_t n2, int inverse,
- limb_t m_idx)
-{
- limb_t i, j, c_mul, c0, m, m_inv, strip_len, l;
- NTTLimb *buf2, *buf3;
-
- buf2 = NULL;
- buf3 = ntt_malloc(s, sizeof(NTTLimb) * n1);
- if (!buf3)
- goto fail;
- if (k2 == 0) {
- if (ntt_fft(s, buf1, buf1, buf3, k1, inverse, m_idx))
- goto fail;
- } else {
- strip_len = STRIP_LEN;
- buf2 = ntt_malloc(s, sizeof(NTTLimb) * n1 * strip_len);
- if (!buf2)
- goto fail;
- m = ntt_mods[m_idx];
- m_inv = s->ntt_mods_div[m_idx];
- c0 = s->ntt_proot_pow[m_idx][inverse][k1 + k2];
- c_mul = 1;
- assert((n2 % strip_len) == 0);
- for(j = 0; j < n2; j += strip_len) {
- for(i = 0; i < n1; i++) {
- for(l = 0; l < strip_len; l++) {
- buf2[i + l * n1] = buf1[i * n2 + (j + l)];
- }
- }
- for(l = 0; l < strip_len; l++) {
- if (inverse)
- mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv);
- if (ntt_fft(s, buf2 + l * n1, buf2 + l * n1, buf3, k1, inverse, m_idx))
- goto fail;
- if (!inverse)
- mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv);
- c_mul = mul_mod_fast(c_mul, c0, m, m_inv);
- }
-
- for(i = 0; i < n1; i++) {
- for(l = 0; l < strip_len; l++) {
- buf1[i * n2 + (j + l)] = buf2[i + l *n1];
- }
- }
- }
- ntt_free(s, buf2);
- }
- ntt_free(s, buf3);
- return 0;
- fail:
- ntt_free(s, buf2);
- ntt_free(s, buf3);
- return -1;
-}
-
-
-/* dst = buf1, src = buf2, tmp = buf3 */
-static int ntt_conv(BFNTTState *s, NTTLimb *buf1, NTTLimb *buf2,
- int k, int k_tot, limb_t m_idx)
-{
- limb_t n1, n2, i;
- int k1, k2;
-
- if (k <= NTT_TRIG_K_MAX) {
- k1 = k;
- } else {
- /* recursive split of the FFT */
- k1 = bf_min(k / 2, NTT_TRIG_K_MAX);
- }
- k2 = k - k1;
- n1 = (limb_t)1 << k1;
- n2 = (limb_t)1 << k2;
-
- if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 0, m_idx))
- return -1;
- if (ntt_fft_partial(s, buf2, k1, k2, n1, n2, 0, m_idx))
- return -1;
- if (k2 == 0) {
- ntt_vec_mul(s, buf1, buf2, k, k_tot, m_idx);
- } else {
- for(i = 0; i < n1; i++) {
- ntt_conv(s, buf1 + i * n2, buf2 + i * n2, k2, k_tot, m_idx);
- }
- }
- if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 1, m_idx))
- return -1;
- return 0;
-}
-
-
-static no_inline void limb_to_ntt(BFNTTState *s,
- NTTLimb *tabr, limb_t fft_len,
- const limb_t *taba, limb_t a_len, int dpl,
- int first_m_idx, int nb_mods)
-{
- slimb_t i, n;
- dlimb_t a, b;
- int j, shift;
- limb_t base_mask1, a0, a1, a2, r, m, m_inv;
-
-#if 0
- for(i = 0; i < a_len; i++) {
- printf("%" PRId64 ": " FMT_LIMB "\n",
- (int64_t)i, taba[i]);
- }
-#endif
- memset(tabr, 0, sizeof(NTTLimb) * fft_len * nb_mods);
- shift = dpl & (LIMB_BITS - 1);
- if (shift == 0)
- base_mask1 = -1;
- else
- base_mask1 = ((limb_t)1 << shift) - 1;
- n = bf_min(fft_len, (a_len * LIMB_BITS + dpl - 1) / dpl);
- for(i = 0; i < n; i++) {
- a0 = get_bits(taba, a_len, i * dpl);
- if (dpl <= LIMB_BITS) {
- a0 &= base_mask1;
- a = a0;
- } else {
- a1 = get_bits(taba, a_len, i * dpl + LIMB_BITS);
- if (dpl <= (LIMB_BITS + NTT_MOD_LOG2_MIN)) {
- a = a0 | ((dlimb_t)(a1 & base_mask1) << LIMB_BITS);
- } else {
- if (dpl > 2 * LIMB_BITS) {
- a2 = get_bits(taba, a_len, i * dpl + LIMB_BITS * 2) &
- base_mask1;
- } else {
- a1 &= base_mask1;
- a2 = 0;
- }
- // printf("a=0x%016lx%016lx%016lx\n", a2, a1, a0);
- a = (a0 >> (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) |
- ((dlimb_t)a1 << (NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)) |
- ((dlimb_t)a2 << (LIMB_BITS + NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN));
- a0 &= ((limb_t)1 << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) - 1;
- }
- }
- for(j = 0; j < nb_mods; j++) {
- m = ntt_mods[first_m_idx + j];
- m_inv = s->ntt_mods_div[first_m_idx + j];
- r = mod_fast(a, m, m_inv);
- if (dpl > (LIMB_BITS + NTT_MOD_LOG2_MIN)) {
- b = ((dlimb_t)r << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | a0;
- r = mod_fast(b, m, m_inv);
- }
- tabr[i + j * fft_len] = int_to_ntt_limb(r, m);
- }
- }
-}
-
-#if defined(__AVX2__)
-
-#define VEC_LEN 4
-
-typedef union {
- __m256d v;
- double d[4];
-} VecUnion;
-
-static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len,
- const NTTLimb *buf, int fft_len_log2, int dpl,
- int nb_mods)
-{
- const limb_t *mods = ntt_mods + NB_MODS - nb_mods;
- const __m256d *mods_cr_vec, *mf, *m_inv;
- VecUnion y[NB_MODS];
- limb_t u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r;
- slimb_t i, len, pos;
- int j, k, l, shift, n_limb1, p;
- dlimb_t t;
-
- j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2;
- mods_cr_vec = s->ntt_mods_cr_vec + j;
- mf = s->ntt_mods_vec + NB_MODS - nb_mods;
- m_inv = s->ntt_mods_inv_vec + NB_MODS - nb_mods;
-
- shift = dpl & (LIMB_BITS - 1);
- if (shift == 0)
- base_mask1 = -1;
- else
- base_mask1 = ((limb_t)1 << shift) - 1;
- n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS;
- for(j = 0; j < NB_MODS; j++)
- carry[j] = 0;
- for(j = 0; j < NB_MODS; j++)
- u[j] = 0; /* avoid warnings */
- memset(tabr, 0, sizeof(limb_t) * r_len);
- fft_len = (limb_t)1 << fft_len_log2;
- len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl);
- len = (len + VEC_LEN - 1) & ~(VEC_LEN - 1);
- i = 0;
- while (i < len) {
- for(j = 0; j < nb_mods; j++)
- y[j].v = *(__m256d *)&buf[i + fft_len * j];
-
- /* Chinese remainder to get mixed radix representation */
- l = 0;
- for(j = 0; j < nb_mods - 1; j++) {
- y[j].v = ntt_mod1(y[j].v, mf[j]);
- for(k = j + 1; k < nb_mods; k++) {
- y[k].v = ntt_mul_mod(y[k].v - y[j].v,
- mods_cr_vec[l], mf[k], m_inv[k]);
- l++;
- }
- }
- y[j].v = ntt_mod1(y[j].v, mf[j]);
-
- for(p = 0; p < VEC_LEN; p++) {
- /* back to normal representation */
- u[0] = (int64_t)y[nb_mods - 1].d[p];
- l = 1;
- for(j = nb_mods - 2; j >= 1; j--) {
- r = (int64_t)y[j].d[p];
- for(k = 0; k < l; k++) {
- t = (dlimb_t)u[k] * mods[j] + r;
- r = t >> LIMB_BITS;
- u[k] = t;
- }
- u[l] = r;
- l++;
- }
- /* XXX: for nb_mods = 5, l should be 4 */
-
- /* last step adds the carry */
- r = (int64_t)y[0].d[p];
- for(k = 0; k < l; k++) {
- t = (dlimb_t)u[k] * mods[j] + r + carry[k];
- r = t >> LIMB_BITS;
- u[k] = t;
- }
- u[l] = r + carry[l];
-
-#if 0
- printf("%" PRId64 ": ", i);
- for(j = nb_mods - 1; j >= 0; j--) {
- printf(" %019" PRIu64, u[j]);
- }
- printf("\n");
-#endif
-
- /* write the digits */
- pos = i * dpl;
- for(j = 0; j < n_limb1; j++) {
- put_bits(tabr, r_len, pos, u[j]);
- pos += LIMB_BITS;
- }
- put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1);
- /* shift by dpl digits and set the carry */
- if (shift == 0) {
- for(j = n_limb1 + 1; j < nb_mods; j++)
- carry[j - (n_limb1 + 1)] = u[j];
- } else {
- for(j = n_limb1; j < nb_mods - 1; j++) {
- carry[j - n_limb1] = (u[j] >> shift) |
- (u[j + 1] << (LIMB_BITS - shift));
- }
- carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift;
- }
- i++;
- }
- }
-}
-#else
-static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len,
- const NTTLimb *buf, int fft_len_log2, int dpl,
- int nb_mods)
-{
- const limb_t *mods = ntt_mods + NB_MODS - nb_mods;
- const limb_t *mods_cr, *mods_cr_inv;
- limb_t y[NB_MODS], u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r;
- slimb_t i, len, pos;
- int j, k, l, shift, n_limb1;
- dlimb_t t;
-
- j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2;
- mods_cr = ntt_mods_cr + j;
- mods_cr_inv = s->ntt_mods_cr_inv + j;
-
- shift = dpl & (LIMB_BITS - 1);
- if (shift == 0)
- base_mask1 = -1;
- else
- base_mask1 = ((limb_t)1 << shift) - 1;
- n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS;
- for(j = 0; j < NB_MODS; j++)
- carry[j] = 0;
- for(j = 0; j < NB_MODS; j++)
- u[j] = 0; /* avoid warnings */
- memset(tabr, 0, sizeof(limb_t) * r_len);
- fft_len = (limb_t)1 << fft_len_log2;
- len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl);
- for(i = 0; i < len; i++) {
- for(j = 0; j < nb_mods; j++) {
- y[j] = ntt_limb_to_int(buf[i + fft_len * j], mods[j]);
- }
-
- /* Chinese remainder to get mixed radix representation */
- l = 0;
- for(j = 0; j < nb_mods - 1; j++) {
- for(k = j + 1; k < nb_mods; k++) {
- limb_t m;
- m = mods[k];
- /* Note: there is no overflow in the sub_mod() because
- the modulos are sorted by increasing order */
- y[k] = mul_mod_fast2(y[k] - y[j] + m,
- mods_cr[l], m, mods_cr_inv[l]);
- l++;
- }
- }
-
- /* back to normal representation */
- u[0] = y[nb_mods - 1];
- l = 1;
- for(j = nb_mods - 2; j >= 1; j--) {
- r = y[j];
- for(k = 0; k < l; k++) {
- t = (dlimb_t)u[k] * mods[j] + r;
- r = t >> LIMB_BITS;
- u[k] = t;
- }
- u[l] = r;
- l++;
- }
-
- /* last step adds the carry */
- r = y[0];
- for(k = 0; k < l; k++) {
- t = (dlimb_t)u[k] * mods[j] + r + carry[k];
- r = t >> LIMB_BITS;
- u[k] = t;
- }
- u[l] = r + carry[l];
-
-#if 0
- printf("%" PRId64 ": ", (int64_t)i);
- for(j = nb_mods - 1; j >= 0; j--) {
- printf(" " FMT_LIMB, u[j]);
- }
- printf("\n");
-#endif
-
- /* write the digits */
- pos = i * dpl;
- for(j = 0; j < n_limb1; j++) {
- put_bits(tabr, r_len, pos, u[j]);
- pos += LIMB_BITS;
- }
- put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1);
- /* shift by dpl digits and set the carry */
- if (shift == 0) {
- for(j = n_limb1 + 1; j < nb_mods; j++)
- carry[j - (n_limb1 + 1)] = u[j];
- } else {
- for(j = n_limb1; j < nb_mods - 1; j++) {
- carry[j - n_limb1] = (u[j] >> shift) |
- (u[j + 1] << (LIMB_BITS - shift));
- }
- carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift;
- }
- }
-}
-#endif
-
-static int ntt_static_init(bf_context_t *s1)
-{
- BFNTTState *s;
- int inverse, i, j, k, l;
- limb_t c, c_inv, c_inv2, m, m_inv;
-
- if (s1->ntt_state)
- return 0;
-#if defined(__AVX2__)
- s = bf_aligned_malloc(s1, sizeof(*s), 64);
-#else
- s = bf_malloc(s1, sizeof(*s));
-#endif
- if (!s)
- return -1;
- memset(s, 0, sizeof(*s));
- s1->ntt_state = s;
- s->ctx = s1;
-
- for(j = 0; j < NB_MODS; j++) {
- m = ntt_mods[j];
- m_inv = init_mul_mod_fast(m);
- s->ntt_mods_div[j] = m_inv;
-#if defined(__AVX2__)
- s->ntt_mods_vec[j] = _mm256_set1_pd(m);
- s->ntt_mods_inv_vec[j] = _mm256_set1_pd(1.0 / (double)m);
-#endif
- c_inv2 = (m + 1) / 2; /* 1/2 */
- c_inv = 1;
- for(i = 0; i <= NTT_PROOT_2EXP; i++) {
- s->ntt_len_inv[j][i][0] = c_inv;
- s->ntt_len_inv[j][i][1] = init_mul_mod_fast2(c_inv, m);
- c_inv = mul_mod_fast(c_inv, c_inv2, m, m_inv);
- }
-
- for(inverse = 0; inverse < 2; inverse++) {
- c = ntt_proot[inverse][j];
- for(i = 0; i < NTT_PROOT_2EXP; i++) {
- s->ntt_proot_pow[j][inverse][NTT_PROOT_2EXP - i] = c;
- s->ntt_proot_pow_inv[j][inverse][NTT_PROOT_2EXP - i] =
- init_mul_mod_fast2(c, m);
- c = mul_mod_fast(c, c, m, m_inv);
- }
- }
- }
-
- l = 0;
- for(j = 0; j < NB_MODS - 1; j++) {
- for(k = j + 1; k < NB_MODS; k++) {
-#if defined(__AVX2__)
- s->ntt_mods_cr_vec[l] = _mm256_set1_pd(int_to_ntt_limb2(ntt_mods_cr[l],
- ntt_mods[k]));
-#else
- s->ntt_mods_cr_inv[l] = init_mul_mod_fast2(ntt_mods_cr[l],
- ntt_mods[k]);
-#endif
- l++;
- }
- }
- return 0;
-}
-
-int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len)
-{
- int dpl, fft_len_log2, n_bits, nb_mods, dpl_found, fft_len_log2_found;
- int int_bits, nb_mods_found;
- limb_t cost, min_cost;
-
- min_cost = -1;
- dpl_found = 0;
- nb_mods_found = 4;
- fft_len_log2_found = 0;
- for(nb_mods = 3; nb_mods <= NB_MODS; nb_mods++) {
- int_bits = ntt_int_bits[NB_MODS - nb_mods];
- dpl = bf_min((int_bits - 4) / 2,
- 2 * LIMB_BITS + 2 * NTT_MOD_LOG2_MIN - NTT_MOD_LOG2_MAX);
- for(;;) {
- fft_len_log2 = ceil_log2((len * LIMB_BITS + dpl - 1) / dpl);
- if (fft_len_log2 > NTT_PROOT_2EXP)
- goto next;
- n_bits = fft_len_log2 + 2 * dpl;
- if (n_bits <= int_bits) {
- cost = ((limb_t)(fft_len_log2 + 1) << fft_len_log2) * nb_mods;
- // printf("n=%d dpl=%d: cost=%" PRId64 "\n", nb_mods, dpl, (int64_t)cost);
- if (cost < min_cost) {
- min_cost = cost;
- dpl_found = dpl;
- nb_mods_found = nb_mods;
- fft_len_log2_found = fft_len_log2;
- }
- break;
- }
- dpl--;
- if (dpl == 0)
- break;
- }
- next: ;
- }
- if (!dpl_found)
- abort();
- /* limit dpl if possible to reduce fixed cost of limb/NTT conversion */
- if (dpl_found > (LIMB_BITS + NTT_MOD_LOG2_MIN) &&
- ((limb_t)(LIMB_BITS + NTT_MOD_LOG2_MIN) << fft_len_log2_found) >=
- len * LIMB_BITS) {
- dpl_found = LIMB_BITS + NTT_MOD_LOG2_MIN;
- }
- *pnb_mods = nb_mods_found;
- *pdpl = dpl_found;
- return fft_len_log2_found;
-}
-
-/* return 0 if OK, -1 if memory error */
-static no_inline int fft_mul(bf_context_t *s1,
- bf_t *res, limb_t *a_tab, limb_t a_len,
- limb_t *b_tab, limb_t b_len, int mul_flags)
-{
- BFNTTState *s;
- int dpl, fft_len_log2, j, nb_mods, reduced_mem;
- slimb_t len, fft_len;
- NTTLimb *buf1, *buf2, *ptr;
-#if defined(USE_MUL_CHECK)
- limb_t ha, hb, hr, h_ref;
-#endif
-
- if (ntt_static_init(s1))
- return -1;
- s = s1->ntt_state;
-
- /* find the optimal number of digits per limb (dpl) */
- len = a_len + b_len;
- fft_len_log2 = bf_get_fft_size(&dpl, &nb_mods, len);
- fft_len = (uint64_t)1 << fft_len_log2;
- // printf("len=%" PRId64 " fft_len_log2=%d dpl=%d\n", len, fft_len_log2, dpl);
-#if defined(USE_MUL_CHECK)
- ha = mp_mod1(a_tab, a_len, BF_CHKSUM_MOD, 0);
- hb = mp_mod1(b_tab, b_len, BF_CHKSUM_MOD, 0);
-#endif
- if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == 0) {
- if (!(mul_flags & FFT_MUL_R_NORESIZE))
- bf_resize(res, 0);
- } else if (mul_flags & FFT_MUL_R_OVERLAP_B) {
- limb_t *tmp_tab, tmp_len;
- /* it is better to free 'b' first */
- tmp_tab = a_tab;
- a_tab = b_tab;
- b_tab = tmp_tab;
- tmp_len = a_len;
- a_len = b_len;
- b_len = tmp_len;
- }
- buf1 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods);
- if (!buf1)
- return -1;
- limb_to_ntt(s, buf1, fft_len, a_tab, a_len, dpl,
- NB_MODS - nb_mods, nb_mods);
- if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) ==
- FFT_MUL_R_OVERLAP_A) {
- if (!(mul_flags & FFT_MUL_R_NORESIZE))
- bf_resize(res, 0);
- }
- reduced_mem = (fft_len_log2 >= 14);
- if (!reduced_mem) {
- buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods);
- if (!buf2)
- goto fail;
- limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl,
- NB_MODS - nb_mods, nb_mods);
- if (!(mul_flags & FFT_MUL_R_NORESIZE))
- bf_resize(res, 0); /* in case res == b */
- } else {
- buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len);
- if (!buf2)
- goto fail;
- }
- for(j = 0; j < nb_mods; j++) {
- if (reduced_mem) {
- limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl,
- NB_MODS - nb_mods + j, 1);
- ptr = buf2;
- } else {
- ptr = buf2 + fft_len * j;
- }
- if (ntt_conv(s, buf1 + fft_len * j, ptr,
- fft_len_log2, fft_len_log2, j + NB_MODS - nb_mods))
- goto fail;
- }
- if (!(mul_flags & FFT_MUL_R_NORESIZE))
- bf_resize(res, 0); /* in case res == b and reduced mem */
- ntt_free(s, buf2);
- buf2 = NULL;
- if (!(mul_flags & FFT_MUL_R_NORESIZE)) {
- if (bf_resize(res, len))
- goto fail;
- }
- ntt_to_limb(s, res->tab, len, buf1, fft_len_log2, dpl, nb_mods);
- ntt_free(s, buf1);
-#if defined(USE_MUL_CHECK)
- hr = mp_mod1(res->tab, len, BF_CHKSUM_MOD, 0);
- h_ref = mul_mod(ha, hb, BF_CHKSUM_MOD);
- if (hr != h_ref) {
- printf("ntt_mul_error: len=%" PRId_LIMB " fft_len_log2=%d dpl=%d nb_mods=%d\n",
- len, fft_len_log2, dpl, nb_mods);
- // printf("ha=0x" FMT_LIMB" hb=0x" FMT_LIMB " hr=0x" FMT_LIMB " expected=0x" FMT_LIMB "\n", ha, hb, hr, h_ref);
- exit(1);
- }
-#endif
- return 0;
- fail:
- ntt_free(s, buf1);
- ntt_free(s, buf2);
- return -1;
-}
-
-#else /* USE_FFT_MUL */
-
-int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len)
-{
- return 0;
-}
-
-#endif /* !USE_FFT_MUL */
/*
* QuickJS Javascript Engine
*
- * Copyright (c) 2017-2021 Fabrice Bellard
- * Copyright (c) 2017-2021 Charlie Gordon
+ * Copyright (c) 2017-2025 Fabrice Bellard
+ * Copyright (c) 2017-2025 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
#include "quickjs.h"
#include "libregexp.h"
#include "libunicode.h"
-#include "libbf.h"
#define OPTIMIZE 1
#define SHORT_OPCODES 1
JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */
JS_CLASS_DATAVIEW, /* u.typed_array */
JS_CLASS_BIG_INT, /* u.object_data */
-#ifdef CONFIG_BIGNUM
- JS_CLASS_BIG_FLOAT, /* u.object_data */
- JS_CLASS_FLOAT_ENV, /* u.float_env */
- JS_CLASS_BIG_DECIMAL, /* u.object_data */
- JS_CLASS_OPERATOR_SET, /* u.operator_set */
-#endif
JS_CLASS_MAP, /* u.map_state */
JS_CLASS_SET, /* u.map_state */
JS_CLASS_WEAKMAP, /* u.map_state */
typedef enum OPCodeEnum OPCodeEnum;
-/* function pointers are used for numeric operations so that it is
- possible to remove some numeric types */
-typedef struct {
- JSValue (*to_string)(JSContext *ctx, JSValueConst val);
- JSValue (*from_string)(JSContext *ctx, const char *buf,
- int radix, int flags, slimb_t *pexponent);
- int (*unary_arith)(JSContext *ctx,
- JSValue *pres, OPCodeEnum op, JSValue op1);
- int (*binary_arith)(JSContext *ctx, OPCodeEnum op,
- JSValue *pres, JSValue op1, JSValue op2);
- int (*compare)(JSContext *ctx, OPCodeEnum op,
- JSValue op1, JSValue op2);
- /* only for bigfloat: */
- JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a,
- int64_t exponent);
- int (*mul_pow10)(JSContext *ctx, JSValue *sp);
-} JSNumericOperations;
-
struct JSRuntime {
JSMallocFunctions mf;
JSMallocState malloc_state;
int shape_hash_size;
int shape_hash_count; /* number of hashed shapes */
JSShape **shape_hash;
- bf_context_t bf_ctx;
- JSNumericOperations bigint_ops;
-#ifdef CONFIG_BIGNUM
- JSNumericOperations bigfloat_ops;
- JSNumericOperations bigdecimal_ops;
- uint32_t operator_count;
-#endif
void *user_opaque;
};
};
} JSVarRef;
-/* the same structure is used for big integers and big floats. Big
- integers are never infinite or NaNs */
-typedef struct JSBigFloat {
- JSRefCountHeader header; /* must come first, 32-bit */
- bf_t num;
-} JSBigFloat;
+/* bigint */
-#ifdef CONFIG_BIGNUM
-typedef struct JSFloatEnv {
- limb_t prec;
- bf_flags_t flags;
- unsigned int status;
-} JSFloatEnv;
+#if JS_LIMB_BITS == 32
+
+typedef int32_t js_slimb_t;
+typedef uint32_t js_limb_t;
+typedef int64_t js_sdlimb_t;
+typedef uint64_t js_dlimb_t;
+
+#define JS_LIMB_DIGITS 9
+
+#else
+
+typedef __int128 int128_t;
+typedef unsigned __int128 uint128_t;
+typedef int64_t js_slimb_t;
+typedef uint64_t js_limb_t;
+typedef int128_t js_sdlimb_t;
+typedef uint128_t js_dlimb_t;
+
+#define JS_LIMB_DIGITS 19
-typedef struct JSBigDecimal {
- JSRefCountHeader header; /* must come first, 32-bit */
- bfdec_t num;
-} JSBigDecimal;
#endif
+typedef struct JSBigInt {
+ JSRefCountHeader header; /* must come first, 32-bit */
+ uint32_t len; /* number of limbs, >= 1 */
+ js_limb_t tab[]; /* two's complement representation, always
+ normalized so that 'len' is the minimum
+ possible length >= 1 */
+} JSBigInt;
+
+/* this bigint structure can hold a 64 bit integer */
+typedef struct {
+ JSBigInt big_int;
+ /* must come just after */
+ js_limb_t tab[(64 + JS_LIMB_BITS - 1) / JS_LIMB_BITS];
+} JSBigIntBuf;
+
typedef enum {
JS_AUTOINIT_ID_PROTOTYPE,
JS_AUTOINIT_ID_MODULE_NS,
JSValue global_var_obj; /* contains the global let/const definitions */
uint64_t random_state;
- bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */
-#ifdef CONFIG_BIGNUM
- JSFloatEnv fp_env; /* global FP environment */
- BOOL bignum_ext : 8; /* enable math mode */
- BOOL allow_operator_overloading : 8;
-#endif
+
/* when the counter reaches zero, JSRutime.interrupt_handler is called */
int interrupt_counter;
struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */
struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */
struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */
-#ifdef CONFIG_BIGNUM
- struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */
- struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */
-#endif
struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */
struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */
struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */
static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);
static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
-#ifdef CONFIG_BIGNUM
-static void js_operator_set_finalizer(JSRuntime *rt, JSValue val);
-static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
-#endif
#define HINT_STRING 0
#define HINT_NUMBER 1
static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);
static JSProperty *add_property(JSContext *ctx,
JSObject *p, JSAtom prop, int prop_flags);
-static JSValue JS_NewBigInt(JSContext *ctx);
-static inline bf_t *JS_GetBigInt(JSValueConst val)
-{
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- return &p->num;
-}
-static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
- BOOL convert_to_safe_integer);
-static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val);
static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
-static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val);
-static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf);
-#ifdef CONFIG_BIGNUM
-static void js_float_env_finalizer(JSRuntime *rt, JSValue val);
-static JSValue JS_NewBigFloat(JSContext *ctx);
-static inline bf_t *JS_GetBigFloat(JSValueConst val)
-{
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- return &p->num;
-}
-static JSValue JS_NewBigDecimal(JSContext *ctx);
-static inline bfdec_t *JS_GetBigDecimal(JSValueConst val)
-{
- JSBigDecimal *p = JS_VALUE_GET_PTR(val);
- return &p->num;
-}
-static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val);
-static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
- BOOL allow_null_or_undefined);
-static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);
-#endif
JSValue JS_ThrowOutOfMemory(JSContext *ctx);
static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
return memset(ptr, 0, size);
}
-/* called by libbf */
-static void *js_bf_realloc(void *opaque, void *ptr, size_t size)
-{
- JSRuntime *rt = opaque;
- return js_realloc_rt(rt, ptr, size);
-}
-
/* Throw out of memory in case of error */
void *js_malloc(JSContext *ctx, size_t size)
{
{ JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */
{ JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */
{ JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */
-#ifdef CONFIG_BIGNUM
- { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_FLOAT */
- { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL }, /* JS_CLASS_FLOAT_ENV */
- { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_DECIMAL */
- { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark }, /* JS_CLASS_OPERATOR_SET */
-#endif
{ JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */
{ JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */
{ JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */
return 0;
}
-static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx)
-{
- return JS_ThrowTypeError(ctx, "unsupported operation");
-}
-
-static JSValue invalid_to_string(JSContext *ctx, JSValueConst val)
-{
- return JS_ThrowUnsupportedOperation(ctx);
-}
-
-static JSValue invalid_from_string(JSContext *ctx, const char *buf,
- int radix, int flags, slimb_t *pexponent)
-{
- return JS_NAN;
-}
-
-static int invalid_unary_arith(JSContext *ctx,
- JSValue *pres, OPCodeEnum op, JSValue op1)
-{
- JS_FreeValue(ctx, op1);
- JS_ThrowUnsupportedOperation(ctx);
- return -1;
-}
-
-static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op,
- JSValue *pres, JSValue op1, JSValue op2)
-{
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- JS_ThrowUnsupportedOperation(ctx);
- return -1;
-}
-
-static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
- int64_t exponent)
-{
- return JS_ThrowUnsupportedOperation(ctx);
-}
-
-static int invalid_mul_pow10(JSContext *ctx, JSValue *sp)
-{
- JS_ThrowUnsupportedOperation(ctx);
- return -1;
-}
-
-static void set_dummy_numeric_ops(JSNumericOperations *ops)
-{
- ops->to_string = invalid_to_string;
- ops->from_string = invalid_from_string;
- ops->unary_arith = invalid_unary_arith;
- ops->binary_arith = invalid_binary_arith;
- ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64;
- ops->mul_pow10 = invalid_mul_pow10;
-}
-
#if !defined(CONFIG_STACK_CHECK)
/* no stack limitation */
static inline uintptr_t js_get_stack_pointer(void)
rt->malloc_state = ms;
rt->malloc_gc_threshold = 256 * 1024;
- bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);
- set_dummy_numeric_ops(&rt->bigint_ops);
-#ifdef CONFIG_BIGNUM
- set_dummy_numeric_ops(&rt->bigfloat_ops);
- set_dummy_numeric_ops(&rt->bigdecimal_ops);
-#endif
-
init_list_head(&rt->context_list);
init_list_head(&rt->gc_obj_list);
init_list_head(&rt->gc_zero_ref_count_list);
}
js_free_rt(rt, rt->class_array);
- bf_context_end(&rt->bf_ctx);
-
#ifdef DUMP_LEAKS
/* only the atoms defined in JS_InitAtoms() should be left */
{
}
ctx->rt = rt;
list_add_tail(&ctx->link, &rt->context_list);
- ctx->bf_ctx = &rt->bf_ctx;
-#ifdef CONFIG_BIGNUM
- ctx->fp_env.prec = 113;
- ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL;
-#endif
for(i = 0; i < rt->class_count; i++)
ctx->class_proto[i] = JS_NULL;
ctx->array_ctor = JS_NULL;
return (sf && (sf->js_mode & JS_MODE_STRICT));
}
-#ifdef CONFIG_BIGNUM
-static inline BOOL is_math_mode(JSContext *ctx)
-{
- JSStackFrame *sf = ctx->rt->current_stack_frame;
- return (sf && (sf->js_mode & JS_MODE_MATH));
-}
-#else
-static inline BOOL is_math_mode(JSContext *ctx)
-{
- return FALSE;
-}
-#endif
-
/* JSAtom support */
#define JS_ATOM_TAG_INT (1U << 31)
case JS_CLASS_SYMBOL:
case JS_CLASS_DATE:
case JS_CLASS_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_CLASS_BIG_FLOAT:
- case JS_CLASS_BIG_DECIMAL:
-#endif
p->u.object_data = JS_UNDEFINED;
goto set_exotic;
case JS_CLASS_REGEXP:
case JS_CLASS_SYMBOL:
case JS_CLASS_DATE:
case JS_CLASS_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_CLASS_BIG_FLOAT:
- case JS_CLASS_BIG_DECIMAL:
-#endif
return JS_DupValue(ctx, p->u.object_data);
}
}
case JS_CLASS_SYMBOL:
case JS_CLASS_DATE:
case JS_CLASS_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_CLASS_BIG_FLOAT:
- case JS_CLASS_BIG_DECIMAL:
-#endif
JS_FreeValue(ctx, p->u.object_data);
p->u.object_data = val;
return 0;
abort(); /* never freed here */
break;
case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
-#endif
- {
- JSBigFloat *bf = JS_VALUE_GET_PTR(v);
- bf_delete(&bf->num);
- js_free_rt(rt, bf);
- }
- break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_DECIMAL:
{
- JSBigDecimal *bf = JS_VALUE_GET_PTR(v);
- bfdec_delete(&bf->num);
- js_free_rt(rt, bf);
+ JSBigInt *p = JS_VALUE_GET_PTR(v);
+ js_free_rt(rt, p);
}
break;
-#endif
case JS_TAG_SYMBOL:
{
JSAtomStruct *p = JS_VALUE_GET_PTR(v);
compute_jsstring_size(JS_VALUE_GET_STRING(val), hp);
break;
case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- case JS_TAG_BIG_DECIMAL:
-#endif
- /* should track JSBigFloat usage */
+ /* should track JSBigInt usage */
break;
}
}
case JS_CLASS_SYMBOL: /* u.object_data */
case JS_CLASS_DATE: /* u.object_data */
case JS_CLASS_BIG_INT: /* u.object_data */
-#ifdef CONFIG_BIGNUM
- case JS_CLASS_BIG_FLOAT: /* u.object_data */
- case JS_CLASS_BIG_DECIMAL: /* u.object_data */
-#endif
compute_value_size(p->u.object_data, hp);
break;
case JS_CLASS_C_FUNCTION: /* u.cfunc */
case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */
case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */
case JS_CLASS_DATAVIEW: /* u.typed_array */
-#ifdef CONFIG_BIGNUM
- case JS_CLASS_FLOAT_ENV: /* u.float_env */
-#endif
case JS_CLASS_MAP: /* u.map_state */
case JS_CLASS_SET: /* u.map_state */
case JS_CLASS_WEAKMAP: /* u.map_state */
void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
{
- fprintf(fp, "QuickJS memory usage -- "
-#ifdef CONFIG_BIGNUM
- "BigNum "
-#endif
- CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n",
+ fprintf(fp, "QuickJS memory usage -- " CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n",
(int)sizeof(void *) * 8, s->malloc_limit);
#if 1
if (rt) {
static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)
{
switch(JS_VALUE_GET_NORM_TAG(val)) {
+ case JS_TAG_SHORT_BIG_INT:
case JS_TAG_BIG_INT:
val = ctx->class_proto[JS_CLASS_BIG_INT];
break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- val = ctx->class_proto[JS_CLASS_BIG_FLOAT];
- break;
- case JS_TAG_BIG_DECIMAL:
- val = ctx->class_proto[JS_CLASS_BIG_DECIMAL];
- break;
-#endif
case JS_TAG_INT:
case JS_TAG_FLOAT64:
val = ctx->class_proto[JS_CLASS_NUMBER];
JS_FreeValue(ctx, val);
return ret;
}
+ case JS_TAG_SHORT_BIG_INT:
+ return JS_VALUE_GET_SHORT_BIG_INT(val) != 0;
case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
-#endif
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- BOOL ret;
- ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
- JS_FreeValue(ctx, val);
- return ret;
- }
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_DECIMAL:
{
- JSBigDecimal *p = JS_VALUE_GET_PTR(val);
+ JSBigInt *p = JS_VALUE_GET_PTR(val);
BOOL ret;
- ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
+ int i;
+
+ /* fail safe: we assume it is not necessarily
+ normalized. Beginning from the MSB ensures that the
+ test is fast. */
+ ret = FALSE;
+ for(i = p->len - 1; i >= 0; i--) {
+ if (p->tab[i] != 0) {
+ ret = TRUE;
+ break;
+ }
+ }
JS_FreeValue(ctx, val);
return ret;
}
-#endif
case JS_TAG_OBJECT:
{
JSObject *p = JS_VALUE_GET_OBJ(val);
return 36;
}
-/* XXX: remove */
-static double js_strtod(const char *str, int radix, BOOL is_float)
-{
- double d;
- int c;
+/* bigint support */
- if (!is_float || radix != 10) {
- const char *p = str;
- uint64_t n_max, n;
- int int_exp, is_neg;
+#define JS_BIGINT_MAX_SIZE ((1024 * 1024) / JS_LIMB_BITS) /* in limbs */
- is_neg = 0;
- if (*p == '-') {
- is_neg = 1;
- p++;
- }
+/* it is currently assumed that JS_SHORT_BIG_INT_BITS = JS_LIMB_BITS */
+#if JS_SHORT_BIG_INT_BITS == 32
+#define JS_SHORT_BIG_INT_MIN INT32_MIN
+#define JS_SHORT_BIG_INT_MAX INT32_MAX
+#elif JS_SHORT_BIG_INT_BITS == 64
+#define JS_SHORT_BIG_INT_MIN INT64_MIN
+#define JS_SHORT_BIG_INT_MAX INT64_MAX
+#else
+#error unsupported
+#endif
- /* skip leading zeros */
- while (*p == '0')
- p++;
- n = 0;
- if (radix == 10)
- n_max = ((uint64_t)-1 - 9) / 10; /* most common case */
- else
- n_max = ((uint64_t)-1 - (radix - 1)) / radix;
- /* XXX: could be more precise */
- int_exp = 0;
- while (*p != '\0') {
- c = to_digit((uint8_t)*p);
- if (c >= radix)
- break;
- if (n <= n_max) {
- n = n * radix + c;
- } else {
- if (radix == 10)
- goto strtod_case;
- int_exp++;
- }
- p++;
- }
- d = n;
- if (int_exp != 0) {
- d *= pow(radix, int_exp);
- }
- if (is_neg)
- d = -d;
- } else {
- strtod_case:
- d = strtod(str, NULL);
- }
- return d;
-}
+#define ADDC(res, carry_out, op1, op2, carry_in) \
+do { \
+ js_limb_t __v, __a, __k, __k1; \
+ __v = (op1); \
+ __a = __v + (op2); \
+ __k1 = __a < __v; \
+ __k = (carry_in); \
+ __a = __a + __k; \
+ carry_out = (__a < __k) | __k1; \
+ res = __a; \
+} while (0)
-#define ATOD_INT_ONLY (1 << 0)
-/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
-#define ATOD_ACCEPT_BIN_OCT (1 << 2)
-/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
-#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4)
-/* accept _ between digits as a digit separator */
-#define ATOD_ACCEPT_UNDERSCORES (1 << 5)
-/* allow a suffix to override the type */
-#define ATOD_ACCEPT_SUFFIX (1 << 6)
-/* default type */
-#define ATOD_TYPE_MASK (3 << 7)
-#define ATOD_TYPE_FLOAT64 (0 << 7)
-#define ATOD_TYPE_BIG_INT (1 << 7)
-#ifdef CONFIG_BIGNUM
-#define ATOD_TYPE_BIG_FLOAT (2 << 7)
-#define ATOD_TYPE_BIG_DECIMAL (3 << 7)
-/* assume bigint mode: floats are parsed as integers if no decimal
- point nor exponent */
-#define ATOD_MODE_BIGINT (1 << 9)
+#if JS_LIMB_BITS == 32
+/* a != 0 */
+static inline js_limb_t js_limb_clz(js_limb_t a)
+{
+ return clz32(a);
+}
+#else
+static inline js_limb_t js_limb_clz(js_limb_t a)
+{
+ return clz64(a);
+}
#endif
-/* accept -0x1 */
-#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)
-static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,
- int radix, int flags, slimb_t *pexponent)
+static js_limb_t mp_add(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2,
+ js_limb_t n, js_limb_t carry)
{
- bf_t a_s, *a = &a_s;
- int ret;
- JSValue val;
- val = JS_NewBigInt(ctx);
- if (JS_IsException(val))
- return val;
- a = JS_GetBigInt(val);
- ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ);
- if (ret & BF_ST_MEM_ERROR) {
- JS_FreeValue(ctx, val);
- return JS_ThrowOutOfMemory(ctx);
+ int i;
+ for(i = 0;i < n; i++) {
+ ADDC(res[i], carry, op1[i], op2[i], carry);
}
-#ifdef CONFIG_BIGNUM
- val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);
-#else
- val = JS_CompactBigInt1(ctx, val, FALSE);
-#endif
- return val;
+ return carry;
}
-#ifdef CONFIG_BIGNUM
-static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf,
- int radix, int flags, slimb_t *pexponent)
+static js_limb_t mp_sub(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2,
+ int n, js_limb_t carry)
{
- bf_t *a;
- int ret;
- JSValue val;
+ int i;
+ js_limb_t k, a, v, k1;
- val = JS_NewBigFloat(ctx);
- if (JS_IsException(val))
- return val;
- a = JS_GetBigFloat(val);
- if (flags & ATOD_ACCEPT_SUFFIX) {
- /* return the exponent to get infinite precision */
- ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF,
- BF_RNDZ | BF_ATOF_EXPONENT);
- } else {
- ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec,
- ctx->fp_env.flags);
- }
- if (ret & BF_ST_MEM_ERROR) {
- JS_FreeValue(ctx, val);
- return JS_ThrowOutOfMemory(ctx);
+ k = carry;
+ for(i=0;i<n;i++) {
+ v = op1[i];
+ a = v - op2[i];
+ k1 = a > v;
+ v = a - k;
+ k = (v > a) | k1;
+ res[i] = v;
}
- return val;
+ return k;
}
-static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf,
- int radix, int flags, slimb_t *pexponent)
+/* compute 0 - op2. carry = 0 or 1. */
+static js_limb_t mp_neg(js_limb_t *res, const js_limb_t *op2, int n)
{
- bfdec_t *a;
- int ret;
- JSValue val;
+ int i;
+ js_limb_t v, carry;
- val = JS_NewBigDecimal(ctx);
- if (JS_IsException(val))
- return val;
- a = JS_GetBigDecimal(val);
- ret = bfdec_atof(a, buf, NULL, BF_PREC_INF,
- BF_RNDZ | BF_ATOF_NO_NAN_INF);
- if (ret & BF_ST_MEM_ERROR) {
- JS_FreeValue(ctx, val);
- return JS_ThrowOutOfMemory(ctx);
+ carry = 1;
+ for(i=0;i<n;i++) {
+ v = ~op2[i] + carry;
+ carry = v < carry;
+ res[i] = v;
}
- return val;
+ return carry;
}
-#endif
-/* return an exception in case of memory error. Return JS_NAN if
- invalid syntax */
-#ifdef CONFIG_BIGNUM
-static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp,
- int radix, int flags, slimb_t *pexponent)
-#else
-static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
- int radix, int flags)
-#endif
+/* tabr[] = taba[] * b + l. Return the high carry */
+static js_limb_t mp_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
+ js_limb_t b, js_limb_t l)
{
- const char *p, *p_start;
- int sep, is_neg;
- BOOL is_float, has_legacy_octal;
- int atod_type = flags & ATOD_TYPE_MASK;
- char buf1[64], *buf;
- int i, j, len;
- BOOL buf_allocated = FALSE;
- JSValue val;
-
- /* optional separator between digits */
- sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
- has_legacy_octal = FALSE;
+ js_limb_t i;
+ js_dlimb_t t;
- p = str;
- p_start = p;
- is_neg = 0;
- if (p[0] == '+') {
- p++;
- p_start++;
- if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
- goto no_radix_prefix;
- } else if (p[0] == '-') {
- p++;
- p_start++;
- is_neg = 1;
- if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
- goto no_radix_prefix;
- }
- if (p[0] == '0') {
- if ((p[1] == 'x' || p[1] == 'X') &&
- (radix == 0 || radix == 16)) {
- p += 2;
- radix = 16;
- } else if ((p[1] == 'o' || p[1] == 'O') &&
- radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
- p += 2;
- radix = 8;
- } else if ((p[1] == 'b' || p[1] == 'B') &&
- radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
- p += 2;
- radix = 2;
- } else if ((p[1] >= '0' && p[1] <= '9') &&
- radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {
- int i;
- has_legacy_octal = TRUE;
- sep = 256;
- for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
- continue;
- if (p[i] == '8' || p[i] == '9')
- goto no_prefix;
- p += 1;
- radix = 8;
- } else {
- goto no_prefix;
- }
- /* there must be a digit after the prefix */
- if (to_digit((uint8_t)*p) >= radix)
- goto fail;
- no_prefix: ;
- } else {
- no_radix_prefix:
- if (!(flags & ATOD_INT_ONLY) &&
- (atod_type == ATOD_TYPE_FLOAT64
-#ifdef CONFIG_BIGNUM
- || atod_type == ATOD_TYPE_BIG_FLOAT
-#endif
- ) &&
- strstart(p, "Infinity", &p)) {
-#ifdef CONFIG_BIGNUM
- if (atod_type == ATOD_TYPE_BIG_FLOAT) {
- bf_t *a;
- val = JS_NewBigFloat(ctx);
- if (JS_IsException(val))
- goto done;
- a = JS_GetBigFloat(val);
- bf_set_inf(a, is_neg);
- } else
-#endif
- {
- double d = 1.0 / 0.0;
- if (is_neg)
- d = -d;
- val = JS_NewFloat64(ctx, d);
- }
- goto done;
- }
- }
- if (radix == 0)
- radix = 10;
- is_float = FALSE;
- p_start = p;
- while (to_digit((uint8_t)*p) < radix
- || (*p == sep && (radix != 10 ||
- p != p_start + 1 || p[-1] != '0') &&
- to_digit((uint8_t)p[1]) < radix)) {
- p++;
- }
- if (!(flags & ATOD_INT_ONLY)) {
- if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {
- is_float = TRUE;
- p++;
- if (*p == sep)
- goto fail;
- while (to_digit((uint8_t)*p) < radix ||
- (*p == sep && to_digit((uint8_t)p[1]) < radix))
- p++;
- }
- if (p > p_start &&
- (((*p == 'e' || *p == 'E') && radix == 10) ||
- ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
- const char *p1 = p + 1;
- is_float = TRUE;
- if (*p1 == '+') {
- p1++;
- } else if (*p1 == '-') {
- p1++;
- }
- if (is_digit((uint8_t)*p1)) {
- p = p1 + 1;
- while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))
- p++;
- }
- }
+ for(i = 0; i < n; i++) {
+ t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l;
+ tabr[i] = t;
+ l = t >> JS_LIMB_BITS;
}
- if (p == p_start)
- goto fail;
+ return l;
+}
- buf = buf1;
- buf_allocated = FALSE;
- len = p - p_start;
- if (unlikely((len + 2) > sizeof(buf1))) {
- buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */
- if (!buf)
- goto mem_error;
- buf_allocated = TRUE;
- }
- /* remove the separators and the radix prefixes */
- j = 0;
- if (is_neg)
- buf[j++] = '-';
- for (i = 0; i < len; i++) {
- if (p_start[i] != '_')
- buf[j++] = p_start[i];
+static js_limb_t mp_div1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
+ js_limb_t b, js_limb_t r)
+{
+ js_slimb_t i;
+ js_dlimb_t a1;
+ for(i = n - 1; i >= 0; i--) {
+ a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i];
+ tabr[i] = a1 / b;
+ r = a1 % b;
}
- buf[j] = '\0';
+ return r;
+}
- if (flags & ATOD_ACCEPT_SUFFIX) {
- if (*p == 'n') {
- p++;
- atod_type = ATOD_TYPE_BIG_INT;
- } else
-#ifdef CONFIG_BIGNUM
- if (*p == 'l') {
- p++;
- atod_type = ATOD_TYPE_BIG_FLOAT;
- } else if (*p == 'm') {
- p++;
- atod_type = ATOD_TYPE_BIG_DECIMAL;
- } else if (flags & ATOD_MODE_BIGINT) {
- if (!is_float)
- atod_type = ATOD_TYPE_BIG_INT;
- if (has_legacy_octal)
- goto fail;
- } else
-#endif
- {
- if (is_float && radix != 10)
- goto fail;
- }
- } else {
- if (atod_type == ATOD_TYPE_FLOAT64) {
-#ifdef CONFIG_BIGNUM
- if (flags & ATOD_MODE_BIGINT) {
- if (!is_float)
- atod_type = ATOD_TYPE_BIG_INT;
- if (has_legacy_octal)
- goto fail;
- } else
-#endif
- {
- if (is_float && radix != 10)
- goto fail;
- }
- }
- }
+/* tabr[] += taba[] * b, return the high word. */
+static js_limb_t mp_add_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
+ js_limb_t b)
+{
+ js_limb_t i, l;
+ js_dlimb_t t;
- switch(atod_type) {
- case ATOD_TYPE_FLOAT64:
- {
- double d;
- d = js_strtod(buf, radix, is_float);
- /* return int or float64 */
- val = JS_NewFloat64(ctx, d);
- }
- break;
- case ATOD_TYPE_BIG_INT:
- if (has_legacy_octal || is_float)
- goto fail;
- val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL);
- break;
-#ifdef CONFIG_BIGNUM
- case ATOD_TYPE_BIG_FLOAT:
- if (has_legacy_octal)
- goto fail;
- val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags,
- pexponent);
- break;
- case ATOD_TYPE_BIG_DECIMAL:
- if (radix != 10)
- goto fail;
- val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL);
- break;
-#endif
- default:
- abort();
+ l = 0;
+ for(i = 0; i < n; i++) {
+ t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l + tabr[i];
+ tabr[i] = t;
+ l = t >> JS_LIMB_BITS;
}
+ return l;
+}
-done:
- if (buf_allocated)
- js_free_rt(ctx->rt, buf);
- if (pp)
- *pp = p;
- return val;
- fail:
- val = JS_NAN;
- goto done;
- mem_error:
- val = JS_ThrowOutOfMemory(ctx);
- goto done;
+/* size of the result : op1_size + op2_size. */
+static void mp_mul_basecase(js_limb_t *result,
+ const js_limb_t *op1, js_limb_t op1_size,
+ const js_limb_t *op2, js_limb_t op2_size)
+{
+ int i;
+ js_limb_t r;
+
+ result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0);
+ for(i=1;i<op2_size;i++) {
+ r = mp_add_mul1(result + i, op1, op1_size, op2[i]);
+ result[i + op1_size] = r;
+ }
}
-#ifdef CONFIG_BIGNUM
-static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
- int radix, int flags)
+/* tabr[] -= taba[] * b. Return the value to substract to the high
+ word. */
+static js_limb_t mp_sub_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
+ js_limb_t b)
{
- return js_atof2(ctx, str, pp, radix, flags, NULL);
+ js_limb_t i, l;
+ js_dlimb_t t;
+
+ l = 0;
+ for(i = 0; i < n; i++) {
+ t = tabr[i] - (js_dlimb_t)taba[i] * (js_dlimb_t)b - l;
+ tabr[i] = t;
+ l = -(t >> JS_LIMB_BITS);
+ }
+ return l;
+}
+
+/* WARNING: d must be >= 2^(JS_LIMB_BITS-1) */
+static inline js_limb_t udiv1norm_init(js_limb_t d)
+{
+ js_limb_t a0, a1;
+ a1 = -d - 1;
+ a0 = -1;
+ return (((js_dlimb_t)a1 << JS_LIMB_BITS) | a0) / d;
+}
+
+/* return the quotient and the remainder in '*pr'of 'a1*2^JS_LIMB_BITS+a0
+ / d' with 0 <= a1 < d. */
+static inline js_limb_t udiv1norm(js_limb_t *pr, js_limb_t a1, js_limb_t a0,
+ js_limb_t d, js_limb_t d_inv)
+{
+ js_limb_t n1m, n_adj, q, r, ah;
+ js_dlimb_t a;
+ n1m = ((js_slimb_t)a0 >> (JS_LIMB_BITS - 1));
+ n_adj = a0 + (n1m & d);
+ a = (js_dlimb_t)d_inv * (a1 - n1m) + n_adj;
+ q = (a >> JS_LIMB_BITS) + a1;
+ /* compute a - q * r and update q so that the remainder is\
+ between 0 and d - 1 */
+ a = ((js_dlimb_t)a1 << JS_LIMB_BITS) | a0;
+ a = a - (js_dlimb_t)q * d - d;
+ ah = a >> JS_LIMB_BITS;
+ q += 1 + ah;
+ r = (js_limb_t)a + (ah & d);
+ *pr = r;
+ return q;
}
-#endif
-typedef enum JSToNumberHintEnum {
- TON_FLAG_NUMBER,
- TON_FLAG_NUMERIC,
-} JSToNumberHintEnum;
+#define UDIV1NORM_THRESHOLD 3
-static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
- JSToNumberHintEnum flag)
+/* b must be >= 1 << (JS_LIMB_BITS - 1) */
+static js_limb_t mp_div1norm(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
+ js_limb_t b, js_limb_t r)
{
- uint32_t tag;
- JSValue ret;
+ js_slimb_t i;
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_BIG_INT:
- if (flag != TON_FLAG_NUMERIC) {
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeError(ctx, "cannot convert bigint to number");
+ if (n >= UDIV1NORM_THRESHOLD) {
+ js_limb_t b_inv;
+ b_inv = udiv1norm_init(b);
+ for(i = n - 1; i >= 0; i--) {
+ tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv);
}
- ret = val;
- break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_DECIMAL:
- if (flag != TON_FLAG_NUMERIC) {
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number");
+ } else {
+ js_dlimb_t a1;
+ for(i = n - 1; i >= 0; i--) {
+ a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i];
+ tabr[i] = a1 / b;
+ r = a1 % b;
}
- ret = val;
- break;
- case JS_TAG_BIG_FLOAT:
- if (flag != TON_FLAG_NUMERIC) {
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number");
+ }
+ return r;
+}
+
+/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb
+ - 1] must be >= 1 << (JS_LIMB_BITS - 1). na - nb must be >= 0. 'taba'
+ is modified and contains the remainder (nb limbs). tabq[0..na-nb]
+ contains the quotient with tabq[na - nb] <= 1. */
+static void mp_divnorm(js_limb_t *tabq, js_limb_t *taba, js_limb_t na,
+ const js_limb_t *tabb, js_limb_t nb)
+{
+ js_limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r;
+ int i, j;
+
+ b1 = tabb[nb - 1];
+ if (nb == 1) {
+ taba[0] = mp_div1norm(tabq, taba, na, b1, 0);
+ return;
+ }
+ n = na - nb;
+
+ if (n >= UDIV1NORM_THRESHOLD)
+ b1_inv = udiv1norm_init(b1);
+ else
+ b1_inv = 0;
+
+ /* first iteration: the quotient is only 0 or 1 */
+ q = 1;
+ for(j = nb - 1; j >= 0; j--) {
+ if (taba[n + j] != tabb[j]) {
+ if (taba[n + j] < tabb[j])
+ q = 0;
+ break;
}
- ret = val;
- break;
-#endif
- case JS_TAG_FLOAT64:
- case JS_TAG_INT:
- case JS_TAG_EXCEPTION:
- ret = val;
- break;
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
- break;
- case JS_TAG_UNDEFINED:
- ret = JS_NAN;
- break;
- case JS_TAG_OBJECT:
- val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
- if (JS_IsException(val))
- return JS_EXCEPTION;
- goto redo;
- case JS_TAG_STRING:
- {
- const char *str;
- const char *p;
- size_t len;
+ }
+ tabq[n] = q;
+ if (q) {
+ mp_sub(taba + n, taba + n, tabb, nb, 0);
+ }
- str = JS_ToCStringLen(ctx, &len, val);
- JS_FreeValue(ctx, val);
- if (!str)
- return JS_EXCEPTION;
- p = str;
- p += skip_spaces(p);
- if ((p - str) == len) {
- ret = JS_NewInt32(ctx, 0);
- } else {
- int flags = ATOD_ACCEPT_BIN_OCT;
- ret = js_atof(ctx, p, &p, 0, flags);
- if (!JS_IsException(ret)) {
- p += skip_spaces(p);
- if ((p - str) != len) {
- JS_FreeValue(ctx, ret);
- ret = JS_NAN;
+ for(i = n - 1; i >= 0; i--) {
+ if (unlikely(taba[i + nb] >= b1)) {
+ q = -1;
+ } else if (b1_inv) {
+ q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv);
+ } else {
+ js_dlimb_t al;
+ al = ((js_dlimb_t)taba[i + nb] << JS_LIMB_BITS) | taba[i + nb - 1];
+ q = al / b1;
+ r = al % b1;
+ }
+ r = mp_sub_mul1(taba + i, tabb, nb, q);
+
+ v = taba[i + nb];
+ a = v - r;
+ c = (a > v);
+ taba[i + nb] = a;
+
+ if (c != 0) {
+ /* negative result */
+ for(;;) {
+ q--;
+ c = mp_add(taba + i, taba + i, tabb, nb, 0);
+ /* propagate carry and test if positive result */
+ if (c != 0) {
+ if (++taba[i + nb] == 0) {
+ break;
}
}
}
- JS_FreeCString(ctx, str);
}
- break;
- case JS_TAG_SYMBOL:
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeError(ctx, "cannot convert symbol to number");
- default:
- JS_FreeValue(ctx, val);
- ret = JS_NAN;
- break;
+ tabq[i] = q;
}
- return ret;
}
-static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
+/* 1 <= shift <= JS_LIMB_BITS - 1 */
+static js_limb_t mp_shl(js_limb_t *tabr, const js_limb_t *taba, int n,
+ int shift)
{
- return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);
+ int i;
+ js_limb_t l, v;
+ l = 0;
+ for(i = 0; i < n; i++) {
+ v = taba[i];
+ tabr[i] = (v << shift) | l;
+ l = v >> (JS_LIMB_BITS - shift);
+ }
+ return l;
}
-static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)
+/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift).
+ 1 <= shift <= LIMB_BITS - 1 */
+static js_limb_t mp_shr(js_limb_t *tab_r, const js_limb_t *tab, int n,
+ int shift, js_limb_t high)
{
- return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);
+ int i;
+ js_limb_t l, a;
+
+ l = high;
+ for(i = n - 1; i >= 0; i--) {
+ a = tab[i];
+ tab_r[i] = (a >> shift) | (l << (JS_LIMB_BITS - shift));
+ l = a;
+ }
+ return l & (((js_limb_t)1 << shift) - 1);
}
-static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
+static JSBigInt *js_bigint_new(JSContext *ctx, int len)
{
- return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
+ JSBigInt *r;
+ if (len > JS_BIGINT_MAX_SIZE) {
+ JS_ThrowRangeError(ctx, "BigInt is too large to allocate");
+ return NULL;
+ }
+ r = js_malloc(ctx, sizeof(JSBigInt) + len * sizeof(js_limb_t));
+ if (!r)
+ return NULL;
+ r->header.ref_count = 1;
+ r->len = len;
+ return r;
}
-static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,
- JSValue val)
+static JSBigInt *js_bigint_set_si(JSBigIntBuf *buf, js_slimb_t a)
{
- double d;
- uint32_t tag;
+ JSBigInt *r = &buf->big_int;
+ r->len = 1;
+ r->tab[0] = a;
+ return r;
+}
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val)) {
- *pres = JS_FLOAT64_NAN;
- return -1;
- }
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- d = JS_VALUE_GET_INT(val);
- break;
- case JS_TAG_FLOAT64:
- d = JS_VALUE_GET_FLOAT64(val);
- break;
- case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
+/* val must be a short big int */
+static JSBigInt *js_bigint_set_short(JSBigIntBuf *buf, JSValueConst val)
+{
+ return js_bigint_set_si(buf, JS_VALUE_GET_SHORT_BIG_INT(val));
+}
+
+static __maybe_unused void js_bigint_dump1(JSContext *ctx, const char *str,
+ const js_limb_t *tab, int len)
+{
+ int i;
+ printf("%s: ", str);
+ for(i = len - 1; i >= 0; i--) {
+#if JS_LIMB_BITS == 32
+ printf(" %08x", tab[i]);
+#else
+ printf(" %016" PRIx64, tab[i]);
#endif
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- /* XXX: there can be a double rounding issue with some
- primitives (such as JS_ToUint8ClampFree()), but it is
- not critical to fix it. */
- bf_get_float64(&p->num, &d, BF_RNDN);
- JS_FreeValue(ctx, val);
- }
- break;
- default:
- abort();
}
- *pres = d;
- return 0;
+ printf("\n");
}
-static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
+static __maybe_unused void js_bigint_dump(JSContext *ctx, const char *str,
+ const JSBigInt *p)
{
- uint32_t tag;
+ js_bigint_dump1(ctx, str, p->tab, p->len);
+}
- tag = JS_VALUE_GET_TAG(val);
- if (tag <= JS_TAG_NULL) {
- *pres = JS_VALUE_GET_INT(val);
- return 0;
- } else if (JS_TAG_IS_FLOAT64(tag)) {
- *pres = JS_VALUE_GET_FLOAT64(val);
- return 0;
+static JSBigInt *js_bigint_new_si(JSContext *ctx, js_slimb_t a)
+{
+ JSBigInt *r;
+ r = js_bigint_new(ctx, 1);
+ if (!r)
+ return NULL;
+ r->tab[0] = a;
+ return r;
+}
+
+static JSBigInt *js_bigint_new_si64(JSContext *ctx, int64_t a)
+{
+#if JS_LIMB_BITS == 64
+ return js_bigint_new_si(ctx, a);
+#else
+ if (a >= INT32_MIN && a <= INT32_MAX) {
+ return js_bigint_new_si(ctx, a);
} else {
- return __JS_ToFloat64Free(ctx, pres, val);
+ JSBigInt *r;
+ r = js_bigint_new(ctx, 2);
+ if (!r)
+ return NULL;
+ r->tab[0] = a;
+ r->tab[1] = a >> 32;
+ return r;
}
+#endif
}
-int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)
+static JSBigInt *js_bigint_new_ui64(JSContext *ctx, uint64_t a)
{
- return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));
+ if (a <= INT64_MAX) {
+ return js_bigint_new_si64(ctx, a);
+ } else {
+ JSBigInt *r;
+ r = js_bigint_new(ctx, (65 + JS_LIMB_BITS - 1) / JS_LIMB_BITS);
+ if (!r)
+ return NULL;
+#if JS_LIMB_BITS == 64
+ r->tab[0] = a;
+ r->tab[1] = 0;
+#else
+ r->tab[0] = a;
+ r->tab[1] = a >> 32;
+ r->tab[2] = 0;
+#endif
+ return r;
+ }
}
-static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
+static JSBigInt *js_bigint_new_di(JSContext *ctx, js_sdlimb_t a)
{
- return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
+ JSBigInt *r;
+ if (a == (js_slimb_t)a) {
+ r = js_bigint_new(ctx, 1);
+ if (!r)
+ return NULL;
+ r->tab[0] = a;
+ } else {
+ r = js_bigint_new(ctx, 2);
+ if (!r)
+ return NULL;
+ r->tab[0] = a;
+ r->tab[1] = a >> JS_LIMB_BITS;
+ }
+ return r;
}
-/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
-static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
+/* Remove redundant high order limbs. Warning: 'a' may be
+ reallocated. Can never fail.
+*/
+static JSBigInt *js_bigint_normalize1(JSContext *ctx, JSBigInt *a, int l)
{
- uint32_t tag;
- JSValue ret;
-
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
- break;
- case JS_TAG_FLOAT64:
- {
- double d = JS_VALUE_GET_FLOAT64(val);
- if (isnan(d)) {
- ret = JS_NewInt32(ctx, 0);
- } else {
- /* convert -0 to +0 */
- d = trunc(d) + 0.0;
- ret = JS_NewFloat64(ctx, d);
- }
- }
- break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- {
- bf_t a_s, *a, r_s, *r = &r_s;
- BOOL is_nan;
+ js_limb_t v;
- a = JS_ToBigFloat(ctx, &a_s, val);
- if (!a) {
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- if (!bf_is_finite(a)) {
- is_nan = bf_is_nan(a);
- if (is_nan)
- ret = JS_NewInt32(ctx, 0);
- else
- ret = JS_DupValue(ctx, val);
- } else {
- ret = JS_NewBigInt(ctx);
- if (!JS_IsException(ret)) {
- r = JS_GetBigInt(ret);
- bf_set(r, a);
- bf_rint(r, BF_RNDZ);
- ret = JS_CompactBigInt(ctx, ret);
- }
- }
- if (a == &a_s)
- bf_delete(a);
- JS_FreeValue(ctx, val);
+ assert(a->header.ref_count == 1);
+ while (l > 1) {
+ v = a->tab[l - 1];
+ if ((v != 0 && v != -1) ||
+ (v & 1) != (a->tab[l - 2] >> (JS_LIMB_BITS - 1))) {
+ break;
}
- break;
-#endif
- default:
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val))
- return val;
- goto redo;
+ l--;
}
- return ret;
+ if (l != a->len) {
+ JSBigInt *a1;
+ /* realloc to reduce the size */
+ a->len = l;
+ a1 = js_realloc(ctx, a, sizeof(JSBigInt) + l * sizeof(js_limb_t));
+ if (a1)
+ a = a1;
+ }
+ return a;
}
-/* Note: the integer value is satured to 32 bits */
-static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
+static JSBigInt *js_bigint_normalize(JSContext *ctx, JSBigInt *a)
{
- uint32_t tag;
- int ret;
-
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- ret = JS_VALUE_GET_INT(val);
- break;
- case JS_TAG_EXCEPTION:
- *pres = 0;
- return -1;
- case JS_TAG_FLOAT64:
- {
- double d = JS_VALUE_GET_FLOAT64(val);
- if (isnan(d)) {
- ret = 0;
- } else {
- if (d < INT32_MIN)
- ret = INT32_MIN;
- else if (d > INT32_MAX)
- ret = INT32_MAX;
- else
- ret = (int)d;
- }
- }
- break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- bf_get_int32(&ret, &p->num, 0);
- JS_FreeValue(ctx, val);
- }
- break;
-#endif
- default:
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val)) {
- *pres = 0;
- return -1;
- }
- goto redo;
- }
- *pres = ret;
- return 0;
+ return js_bigint_normalize1(ctx, a, a->len);
}
-int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)
+/* return 0 or 1 depending on the sign */
+static inline int js_bigint_sign(const JSBigInt *a)
{
- return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
+ return a->tab[a->len - 1] >> (JS_LIMB_BITS - 1);
}
-int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,
- int min, int max, int min_offset)
+static js_slimb_t js_bigint_get_si_sat(const JSBigInt *a)
{
- int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
- if (res == 0) {
- if (*pres < min) {
- *pres += min_offset;
- if (*pres < min)
- *pres = min;
- } else {
- if (*pres > max)
- *pres = max;
- }
+ if (a->len == 1) {
+ return a->tab[0];
+ } else {
+#if JS_LIMB_BITS == 32
+ if (js_bigint_sign(a))
+ return INT32_MIN;
+ else
+ return INT32_MAX;
+#else
+ if (js_bigint_sign(a))
+ return INT64_MIN;
+ else
+ return INT64_MAX;
+#endif
}
- return res;
}
-static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)
+/* add the op1 limb */
+static JSBigInt *js_bigint_extend(JSContext *ctx, JSBigInt *r,
+ js_limb_t op1)
{
- uint32_t tag;
-
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- *pres = JS_VALUE_GET_INT(val);
- return 0;
- case JS_TAG_EXCEPTION:
- *pres = 0;
- return -1;
- case JS_TAG_FLOAT64:
- {
- double d = JS_VALUE_GET_FLOAT64(val);
- if (isnan(d)) {
- *pres = 0;
- } else {
- if (d < INT64_MIN)
- *pres = INT64_MIN;
- else if (d >= 0x1p63) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */
- *pres = INT64_MAX;
- else
- *pres = (int64_t)d;
- }
+ int n2 = r->len;
+ if ((op1 != 0 && op1 != -1) ||
+ (op1 & 1) != r->tab[n2 - 1] >> (JS_LIMB_BITS - 1)) {
+ JSBigInt *r1;
+ r1 = js_realloc(ctx, r,
+ sizeof(JSBigInt) + (n2 + 1) * sizeof(js_limb_t));
+ if (!r1) {
+ js_free(ctx, r);
+ return NULL;
}
- return 0;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- bf_get_int64(pres, &p->num, 0);
- JS_FreeValue(ctx, val);
+ r = r1;
+ r->len = n2 + 1;
+ r->tab[n2] = op1;
+ } else {
+ /* otherwise still need to normalize the result */
+ r = js_bigint_normalize(ctx, r);
+ }
+ return r;
+}
+
+/* return NULL in case of error. Compute a + b (b_neg = 0) or a - b
+ (b_neg = 1) */
+/* XXX: optimize */
+static JSBigInt *js_bigint_add(JSContext *ctx, const JSBigInt *a,
+ const JSBigInt *b, int b_neg)
+{
+ JSBigInt *r;
+ int n1, n2, i;
+ js_limb_t carry, op1, op2, a_sign, b_sign;
+
+ n2 = max_int(a->len, b->len);
+ n1 = min_int(a->len, b->len);
+ r = js_bigint_new(ctx, n2);
+ if (!r)
+ return NULL;
+ /* XXX: optimize */
+ /* common part */
+ carry = b_neg;
+ for(i = 0; i < n1; i++) {
+ op1 = a->tab[i];
+ op2 = b->tab[i] ^ (-b_neg);
+ ADDC(r->tab[i], carry, op1, op2, carry);
+ }
+ a_sign = -js_bigint_sign(a);
+ b_sign = (-js_bigint_sign(b)) ^ (-b_neg);
+ /* part with sign extension of one operand */
+ if (a->len > b->len) {
+ for(i = n1; i < n2; i++) {
+ op1 = a->tab[i];
+ ADDC(r->tab[i], carry, op1, b_sign, carry);
}
- return 0;
-#endif
- default:
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val)) {
- *pres = 0;
- return -1;
+ } else if (a->len < b->len) {
+ for(i = n1; i < n2; i++) {
+ op2 = b->tab[i] ^ (-b_neg);
+ ADDC(r->tab[i], carry, a_sign, op2, carry);
}
- goto redo;
}
+
+ /* part with sign extension for both operands. Extend the result
+ if necessary */
+ return js_bigint_extend(ctx, r, a_sign + b_sign + carry);
}
-int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)
+/* XXX: optimize */
+static JSBigInt *js_bigint_neg(JSContext *ctx, const JSBigInt *a)
{
- return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
+ JSBigIntBuf buf;
+ JSBigInt *b;
+ b = js_bigint_set_si(&buf, 0);
+ return js_bigint_add(ctx, b, a, 1);
}
-int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,
- int64_t min, int64_t max, int64_t neg_offset)
+static JSBigInt *js_bigint_mul(JSContext *ctx, const JSBigInt *a,
+ const JSBigInt *b)
{
- int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
- if (res == 0) {
- if (*pres < 0)
- *pres += neg_offset;
- if (*pres < min)
- *pres = min;
- else if (*pres > max)
- *pres = max;
+ JSBigInt *r;
+
+ r = js_bigint_new(ctx, a->len + b->len);
+ if (!r)
+ return NULL;
+ mp_mul_basecase(r->tab, a->tab, a->len, b->tab, b->len);
+ /* correct the result if negative operands (no overflow is
+ possible) */
+ if (js_bigint_sign(a))
+ mp_sub(r->tab + a->len, r->tab + a->len, b->tab, b->len, 0);
+ if (js_bigint_sign(b))
+ mp_sub(r->tab + b->len, r->tab + b->len, a->tab, a->len, 0);
+ return js_bigint_normalize(ctx, r);
+}
+
+/* return the division or the remainder. 'b' must be != 0. return NULL
+ in case of exception (division by zero or memory error) */
+static JSBigInt *js_bigint_divrem(JSContext *ctx, const JSBigInt *a,
+ const JSBigInt *b, BOOL is_rem)
+{
+ JSBigInt *r, *q;
+ js_limb_t *tabb, h;
+ int na, nb, a_sign, b_sign, shift;
+
+ if (b->len == 1 && b->tab[0] == 0) {
+ JS_ThrowRangeError(ctx, "BigInt division by zero");
+ return NULL;
+ }
+
+ a_sign = js_bigint_sign(a);
+ b_sign = js_bigint_sign(b);
+ na = a->len;
+ nb = b->len;
+
+ r = js_bigint_new(ctx, na + 2);
+ if (!r)
+ return NULL;
+ if (a_sign) {
+ mp_neg(r->tab, a->tab, na);
+ } else {
+ memcpy(r->tab, a->tab, na * sizeof(a->tab[0]));
+ }
+ /* normalize */
+ while (na > 1 && r->tab[na - 1] == 0)
+ na--;
+
+ tabb = js_malloc(ctx, nb * sizeof(tabb[0]));
+ if (!tabb) {
+ js_free(ctx, r);
+ return NULL;
+ }
+ if (b_sign) {
+ mp_neg(tabb, b->tab, nb);
+ } else {
+ memcpy(tabb, b->tab, nb * sizeof(tabb[0]));
+ }
+ /* normalize */
+ while (nb > 1 && tabb[nb - 1] == 0)
+ nb--;
+
+ /* trivial case if 'a' is small */
+ if (na < nb) {
+ js_free(ctx, r);
+ js_free(ctx, tabb);
+ if (is_rem) {
+ /* r = a */
+ r = js_bigint_new(ctx, a->len);
+ if (!r)
+ return NULL;
+ memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0]));
+ return r;
+ } else {
+ /* q = 0 */
+ return js_bigint_new_si(ctx, 0);
+ }
+ }
+
+ /* normalize 'b' */
+ shift = js_limb_clz(tabb[nb - 1]);
+ if (shift != 0) {
+ mp_shl(tabb, tabb, nb, shift);
+ h = mp_shl(r->tab, r->tab, na, shift);
+ if (h != 0)
+ r->tab[na++] = h;
+ }
+
+ q = js_bigint_new(ctx, na - nb + 2); /* one more limb for the sign */
+ if (!q) {
+ js_free(ctx, r);
+ js_free(ctx, tabb);
+ return NULL;
+ }
+
+ // js_bigint_dump1(ctx, "a", r->tab, na);
+ // js_bigint_dump1(ctx, "b", tabb, nb);
+ mp_divnorm(q->tab, r->tab, na, tabb, nb);
+ js_free(ctx, tabb);
+
+ if (is_rem) {
+ js_free(ctx, q);
+ if (shift != 0)
+ mp_shr(r->tab, r->tab, nb, shift, 0);
+ r->tab[nb++] = 0;
+ if (a_sign)
+ mp_neg(r->tab, r->tab, nb);
+ r = js_bigint_normalize1(ctx, r, nb);
+ return r;
+ } else {
+ js_free(ctx, r);
+ q->tab[na - nb + 1] = 0;
+ if (a_sign ^ b_sign) {
+ mp_neg(q->tab, q->tab, q->len);
+ }
+ q = js_bigint_normalize(ctx, q);
+ return q;
}
- return res;
}
-/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)
- in case of exception */
-static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
+/* and, or, xor */
+static JSBigInt *js_bigint_logic(JSContext *ctx, const JSBigInt *a,
+ const JSBigInt *b, OPCodeEnum op)
{
- uint32_t tag;
- int64_t ret;
+ JSBigInt *r;
+ js_limb_t b_sign;
+ int a_len, b_len, i;
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- ret = JS_VALUE_GET_INT(val);
+ if (a->len < b->len) {
+ const JSBigInt *tmp;
+ tmp = a;
+ a = b;
+ b = tmp;
+ }
+ /* a_len >= b_len */
+ a_len = a->len;
+ b_len = b->len;
+ b_sign = -js_bigint_sign(b);
+
+ r = js_bigint_new(ctx, a_len);
+ if (!r)
+ return NULL;
+ switch(op) {
+ case OP_or:
+ for(i = 0; i < b_len; i++) {
+ r->tab[i] = a->tab[i] | b->tab[i];
+ }
+ for(i = b_len; i < a_len; i++) {
+ r->tab[i] = a->tab[i] | b_sign;
+ }
break;
- case JS_TAG_FLOAT64:
- {
- JSFloat64Union u;
- double d;
- int e;
- d = JS_VALUE_GET_FLOAT64(val);
- u.d = d;
- /* we avoid doing fmod(x, 2^64) */
- e = (u.u64 >> 52) & 0x7ff;
- if (likely(e <= (1023 + 62))) {
- /* fast case */
- ret = (int64_t)d;
- } else if (e <= (1023 + 62 + 53)) {
- uint64_t v;
- /* remainder modulo 2^64 */
- v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
- ret = v << ((e - 1023) - 52);
- /* take the sign into account */
- if (u.u64 >> 63)
- ret = -ret;
- } else {
- ret = 0; /* also handles NaN and +inf */
- }
+ case OP_and:
+ for(i = 0; i < b_len; i++) {
+ r->tab[i] = a->tab[i] & b->tab[i];
+ }
+ for(i = b_len; i < a_len; i++) {
+ r->tab[i] = a->tab[i] & b_sign;
}
break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- bf_get_int64(&ret, &p->num, BF_GET_INT_MOD);
- JS_FreeValue(ctx, val);
+ case OP_xor:
+ for(i = 0; i < b_len; i++) {
+ r->tab[i] = a->tab[i] ^ b->tab[i];
+ }
+ for(i = b_len; i < a_len; i++) {
+ r->tab[i] = a->tab[i] ^ b_sign;
}
break;
-#endif
default:
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val)) {
- *pres = 0;
- return -1;
- }
- goto redo;
+ abort();
}
- *pres = ret;
- return 0;
+ return js_bigint_normalize(ctx, r);
}
-int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
+static JSBigInt *js_bigint_not(JSContext *ctx, const JSBigInt *a)
{
- return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));
+ JSBigInt *r;
+ int i;
+
+ r = js_bigint_new(ctx, a->len);
+ if (!r)
+ return NULL;
+ for(i = 0; i < a->len; i++) {
+ r->tab[i] = ~a->tab[i];
+ }
+ /* no normalization is needed */
+ return r;
}
-int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)
+static JSBigInt *js_bigint_shl(JSContext *ctx, const JSBigInt *a,
+ unsigned int shift1)
{
- if (JS_IsBigInt(ctx, val))
- return JS_ToBigInt64(ctx, pres, val);
- else
- return JS_ToInt64(ctx, pres, val);
+ int d, i, shift;
+ JSBigInt *r;
+ js_limb_t l;
+
+ if (a->len == 1 && a->tab[0] == 0)
+ return js_bigint_new_si(ctx, 0); /* zero case */
+ d = shift1 / JS_LIMB_BITS;
+ shift = shift1 % JS_LIMB_BITS;
+ r = js_bigint_new(ctx, a->len + d);
+ if (!r)
+ return NULL;
+ for(i = 0; i < d; i++)
+ r->tab[i] = 0;
+ if (shift == 0) {
+ for(i = 0; i < a->len; i++) {
+ r->tab[i + d] = a->tab[i];
+ }
+ } else {
+ l = mp_shl(r->tab + d, a->tab, a->len, shift);
+ if (js_bigint_sign(a))
+ l |= (js_limb_t)(-1) << shift;
+ r = js_bigint_extend(ctx, r, l);
+ }
+ return r;
}
-/* return (<0, 0) in case of exception */
-static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
+static JSBigInt *js_bigint_shr(JSContext *ctx, const JSBigInt *a,
+ unsigned int shift1)
{
- uint32_t tag;
- int32_t ret;
+ int d, i, shift, a_sign, n1;
+ JSBigInt *r;
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- ret = JS_VALUE_GET_INT(val);
- break;
- case JS_TAG_FLOAT64:
- {
- JSFloat64Union u;
- double d;
- int e;
- d = JS_VALUE_GET_FLOAT64(val);
- u.d = d;
- /* we avoid doing fmod(x, 2^32) */
- e = (u.u64 >> 52) & 0x7ff;
- if (likely(e <= (1023 + 30))) {
- /* fast case */
- ret = (int32_t)d;
- } else if (e <= (1023 + 30 + 53)) {
- uint64_t v;
- /* remainder modulo 2^32 */
- v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
- v = v << ((e - 1023) - 52 + 32);
- ret = v >> 32;
- /* take the sign into account */
- if (u.u64 >> 63)
- ret = -ret;
- } else {
- ret = 0; /* also handles NaN and +inf */
- }
- }
- break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- bf_get_int32(&ret, &p->num, BF_GET_INT_MOD);
- JS_FreeValue(ctx, val);
- }
- break;
-#endif
- default:
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val)) {
- *pres = 0;
- return -1;
+ d = shift1 / JS_LIMB_BITS;
+ shift = shift1 % JS_LIMB_BITS;
+ a_sign = js_bigint_sign(a);
+ if (d >= a->len)
+ return js_bigint_new_si(ctx, -a_sign);
+ n1 = a->len - d;
+ r = js_bigint_new(ctx, n1);
+ if (!r)
+ return NULL;
+ if (shift == 0) {
+ for(i = 0; i < n1; i++) {
+ r->tab[i] = a->tab[i + d];
}
- goto redo;
+ /* no normalization is needed */
+ } else {
+ mp_shr(r->tab, a->tab + d, n1, shift, -a_sign);
+ r = js_bigint_normalize(ctx, r);
}
- *pres = ret;
- return 0;
+ return r;
}
-int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)
+static JSBigInt *js_bigint_pow(JSContext *ctx, const JSBigInt *a, JSBigInt *b)
{
- return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));
-}
+ uint32_t e;
+ int n_bits, i;
+ JSBigInt *r, *r1;
+
+ /* b must be >= 0 */
+ if (js_bigint_sign(b)) {
+ JS_ThrowRangeError(ctx, "BigInt negative exponent");
+ return NULL;
+ }
+ if (b->len == 1 && b->tab[0] == 0) {
+ /* a^0 = 1 */
+ return js_bigint_new_si(ctx, 1);
+ } else if (a->len == 1) {
+ js_limb_t v;
+ BOOL is_neg;
+
+ v = a->tab[0];
+ if (v <= 1)
+ return js_bigint_new_si(ctx, v);
+ else if (v == -1)
+ return js_bigint_new_si(ctx, 1 - 2 * (b->tab[0] & 1));
+ is_neg = (js_slimb_t)v < 0;
+ if (is_neg)
+ v = -v;
+ if ((v & (v - 1)) == 0) {
+ uint64_t e1;
+ int n;
+ /* v = 2^n */
+ n = JS_LIMB_BITS - 1 - js_limb_clz(v);
+ if (b->len > 1)
+ goto overflow;
+ if (b->tab[0] > INT32_MAX)
+ goto overflow;
+ e = b->tab[0];
+ e1 = (uint64_t)e * n;
+ if (e1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS)
+ goto overflow;
+ e = e1;
+ if (is_neg)
+ is_neg = b->tab[0] & 1;
+ r = js_bigint_new(ctx,
+ (e + JS_LIMB_BITS + 1 - is_neg) / JS_LIMB_BITS);
+ if (!r)
+ return NULL;
+ memset(r->tab, 0, sizeof(r->tab[0]) * r->len);
+ r->tab[e / JS_LIMB_BITS] =
+ (js_limb_t)(1 - 2 * is_neg) << (e % JS_LIMB_BITS);
+ return r;
+ }
+ }
+ if (b->len > 1)
+ goto overflow;
+ if (b->tab[0] > INT32_MAX)
+ goto overflow;
+ e = b->tab[0];
+ n_bits = 32 - clz32(e);
-static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)
-{
- return JS_ToInt32Free(ctx, (int32_t *)pres, val);
+ r = js_bigint_new(ctx, a->len);
+ if (!r)
+ return NULL;
+ memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0]));
+ for(i = n_bits - 2; i >= 0; i--) {
+ r1 = js_bigint_mul(ctx, r, r);
+ if (!r1)
+ return NULL;
+ js_free(ctx, r);
+ r = r1;
+ if ((e >> i) & 1) {
+ r1 = js_bigint_mul(ctx, r, a);
+ if (!r1)
+ return NULL;
+ js_free(ctx, r);
+ r = r1;
+ }
+ }
+ return r;
+ overflow:
+ JS_ThrowRangeError(ctx, "BigInt is too large");
+ return NULL;
}
-static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
-{
- uint32_t tag;
- int res;
-
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- res = JS_VALUE_GET_INT(val);
-#ifdef CONFIG_BIGNUM
- int_clamp:
-#endif
- res = max_int(0, min_int(255, res));
- break;
- case JS_TAG_FLOAT64:
- {
- double d = JS_VALUE_GET_FLOAT64(val);
- if (isnan(d)) {
- res = 0;
- } else {
- if (d < 0)
- res = 0;
- else if (d > 255)
- res = 255;
- else
- res = lrint(d);
- }
+/* return (mant, exp) so that abs(a) ~ mant*2^(exp - (limb_bits -
+ 1). a must be != 0. */
+static uint64_t js_bigint_get_mant_exp(JSContext *ctx,
+ int *pexp, const JSBigInt *a)
+{
+ js_limb_t t[4 - JS_LIMB_BITS / 32], carry, v, low_bits;
+ int n1, n2, sgn, shift, i, j, e;
+ uint64_t a1, a0;
+
+ n2 = 4 - JS_LIMB_BITS / 32;
+ n1 = a->len - n2;
+ sgn = js_bigint_sign(a);
+
+ /* low_bits != 0 if there are a non zero low bit in abs(a) */
+ low_bits = 0;
+ carry = sgn;
+ for(i = 0; i < n1; i++) {
+ v = (a->tab[i] ^ (-sgn)) + carry;
+ carry = v < carry;
+ low_bits |= v;
+ }
+ /* get the n2 high limbs of abs(a) */
+ for(j = 0; j < n2; j++) {
+ i = j + n1;
+ if (i < 0) {
+ v = 0;
+ } else {
+ v = (a->tab[i] ^ (-sgn)) + carry;
+ carry = v < carry;
}
- break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- bf_t r_s, *r = &r_s;
- bf_init(ctx->bf_ctx, r);
- bf_set(r, &p->num);
- bf_rint(r, BF_RNDN);
- bf_get_int32(&res, r, 0);
- bf_delete(r);
- JS_FreeValue(ctx, val);
+ t[j] = v;
+ }
+
+#if JS_LIMB_BITS == 32
+ a1 = ((uint64_t)t[2] << 32) | t[1];
+ a0 = (uint64_t)t[0] << 32;
+#else
+ a1 = t[1];
+ a0 = t[0];
+#endif
+ a0 |= (low_bits != 0);
+ /* normalize */
+ if (a1 == 0) {
+ /* JS_LIMB_BITS = 64 bit only */
+ shift = 64;
+ a1 = a0;
+ a0 = 0;
+ } else {
+ shift = clz64(a1);
+ if (shift != 0) {
+ a1 = (a1 << shift) | (a0 >> (64 - shift));
+ a0 <<= shift;
+ }
+ }
+ a1 |= (a0 != 0); /* keep the bits for the final rounding */
+ /* compute the exponent */
+ e = a->len * JS_LIMB_BITS - shift - 1;
+ *pexp = e;
+ return a1;
+}
+
+/* shift left with round to nearest, ties to even. n >= 1 */
+static uint64_t shr_rndn(uint64_t a, int n)
+{
+ uint64_t addend = ((a >> n) & 1) + ((1 << (n - 1)) - 1);
+ return (a + addend) >> n;
+}
+
+/* convert to float64 with round to nearest, ties to even. Return
+ +/-infinity if too large. */
+static double js_bigint_to_float64(JSContext *ctx, const JSBigInt *a)
+{
+ int sgn, e;
+ uint64_t mant;
+
+ if (a->len == 1) {
+ /* fast case, including zero */
+ return (double)(js_slimb_t)a->tab[0];
+ }
+
+ sgn = js_bigint_sign(a);
+ mant = js_bigint_get_mant_exp(ctx, &e, a);
+ if (e > 1023) {
+ /* overflow: return infinity */
+ mant = 0;
+ e = 1024;
+ } else {
+ mant = (mant >> 1) | (mant & 1); /* avoid overflow in rounding */
+ mant = shr_rndn(mant, 10);
+ /* rounding can cause an overflow */
+ if (mant >= ((uint64_t)1 << 53)) {
+ mant >>= 1;
+ e++;
+ }
+ mant &= (((uint64_t)1 << 52) - 1);
+ }
+ return uint64_as_float64(((uint64_t)sgn << 63) |
+ ((uint64_t)(e + 1023) << 52) |
+ mant);
+}
+
+/* return (1, NULL) if not an integer, (2, NULL) if NaN or Infinity,
+ (0, n) if an integer, (0, NULL) in case of memory error */
+static JSBigInt *js_bigint_from_float64(JSContext *ctx, int *pres, double a1)
+{
+ uint64_t a = float64_as_uint64(a1);
+ int sgn, e, shift;
+ uint64_t mant;
+ JSBigIntBuf buf;
+ JSBigInt *r, *r1;
+
+ sgn = a >> 63;
+ e = (a >> 52) & ((1 << 11) - 1);
+ mant = a & (((uint64_t)1 << 52) - 1);
+ if (e == 2047) {
+ /* NaN, Infinity */
+ *pres = 2;
+ return NULL;
+ }
+ if (e == 0 && mant == 0) {
+ /* zero */
+ *pres = 0;
+ return js_bigint_new_si(ctx, 0);
+ }
+ e -= 1023;
+ /* 0 < a < 1 : not an integer */
+ if (e < 0)
+ goto not_an_integer;
+ mant |= (uint64_t)1 << 52;
+ if (e < 52) {
+ shift = 52 - e;
+ /* check that there is no fractional part */
+ if (mant & (((uint64_t)1 << shift) - 1)) {
+ not_an_integer:
+ *pres = 1;
+ return NULL;
}
- goto int_clamp;
+ mant >>= shift;
+ e = 0;
+ } else {
+ e -= 52;
+ }
+
+ /* the integer is mant*2^e */
+ r = &buf.big_int;
+#if JS_LIMB_BITS == 64
+ r->len = 1;
+ r->tab[0] = mant;
+#else
+ if (mant <= INT32_MAX) {
+ r->len = 1;
+ r->tab[0] = mant;
+ } else {
+ r->len = 2;
+ r->tab[0] = mant;
+ r->tab[1] = mant >> 32;
+ }
#endif
- default:
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val)) {
- *pres = 0;
- return -1;
+ /* XXX: optimize */
+ if (sgn) {
+ r = js_bigint_neg(ctx, r);
+ if (!r)
+ goto fail;
+ r1 = js_bigint_shl(ctx, r, e);
+ js_free(ctx, r);
+ if (!r1)
+ goto fail;
+ r = r1;
+ } else {
+ r = js_bigint_shl(ctx, r, e);
+ }
+ *pres = 0;
+ return r;
+ fail:
+ *pres = 0;
+ return NULL;
+}
+
+/* return -1, 0, 1 or (2) (unordered) */
+static int js_bigint_float64_cmp(JSContext *ctx, const JSBigInt *a,
+ double b)
+{
+ int b_sign, a_sign, e, f;
+ uint64_t mant, b1, a_mant;
+
+ b1 = float64_as_uint64(b);
+ b_sign = b1 >> 63;
+ e = (b1 >> 52) & ((1 << 11) - 1);
+ mant = b1 & (((uint64_t)1 << 52) - 1);
+ a_sign = js_bigint_sign(a);
+ if (e == 2047) {
+ if (mant != 0) {
+ /* NaN */
+ return 2;
+ } else {
+ /* +/- infinity */
+ return 2 * b_sign - 1;
+ }
+ } else if (e == 0 && mant == 0) {
+ /* b = +/-0 */
+ if (a->len == 1 && a->tab[0] == 0)
+ return 0;
+ else
+ return 1 - 2 * a_sign;
+ } else if (a->len == 1 && a->tab[0] == 0) {
+ /* a = 0, b != 0 */
+ return 2 * b_sign - 1;
+ } else if (a_sign != b_sign) {
+ return 1 - 2 * a_sign;
+ } else {
+ e -= 1023;
+ /* Note: handling denormals is not necessary because we
+ compare to integers hence f >= 0 */
+ /* compute f so that 2^f <= abs(a) < 2^(f+1) */
+ a_mant = js_bigint_get_mant_exp(ctx, &f, a);
+ if (f != e) {
+ if (f < e)
+ return -1;
+ else
+ return 1;
+ } else {
+ mant = (mant | ((uint64_t)1 << 52)) << 11; /* align to a_mant */
+ if (a_mant < mant)
+ return 2 * a_sign - 1;
+ else if (a_mant > mant)
+ return 1 - 2 * a_sign;
+ else
+ return 0;
}
- goto redo;
}
- *pres = res;
- return 0;
}
-static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
- JSValue val, BOOL is_array_ctor)
+/* return -1, 0 or 1 */
+static int js_bigint_cmp(JSContext *ctx, const JSBigInt *a,
+ const JSBigInt *b)
{
- uint32_t tag, len;
-
- tag = JS_VALUE_GET_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- {
- int v;
- v = JS_VALUE_GET_INT(val);
- if (v < 0)
- goto fail;
- len = v;
- }
- break;
- case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
-#endif
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- bf_t a;
- BOOL res;
- bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD);
- bf_init(ctx->bf_ctx, &a);
- bf_set_ui(&a, len);
- res = bf_cmp_eq(&a, &p->num);
- bf_delete(&a);
- JS_FreeValue(ctx, val);
- if (!res)
- goto fail;
- }
- break;
- default:
- if (JS_TAG_IS_FLOAT64(tag)) {
- double d;
- d = JS_VALUE_GET_FLOAT64(val);
- if (!(d >= 0 && d <= UINT32_MAX))
- goto fail;
- len = (uint32_t)d;
- if (len != d)
- goto fail;
+ int a_sign, b_sign, res, i;
+ a_sign = js_bigint_sign(a);
+ b_sign = js_bigint_sign(b);
+ if (a_sign != b_sign) {
+ res = 1 - 2 * a_sign;
+ } else {
+ /* we assume the numbers are normalized */
+ if (a->len != b->len) {
+ if (a->len < b->len)
+ res = 2 * a_sign - 1;
+ else
+ res = 1 - 2 * a_sign;
} else {
- uint32_t len1;
-
- if (is_array_ctor) {
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val))
- return -1;
- /* cannot recurse because val is a number */
- if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))
- return -1;
- } else {
- /* legacy behavior: must do the conversion twice and compare */
- if (JS_ToUint32(ctx, &len, val)) {
- JS_FreeValue(ctx, val);
- return -1;
- }
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val))
- return -1;
- /* cannot recurse because val is a number */
- if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))
- return -1;
- if (len1 != len) {
- fail:
- JS_ThrowRangeError(ctx, "invalid array length");
- return -1;
+ res = 0;
+ for(i = a->len -1; i >= 0; i--) {
+ if (a->tab[i] != b->tab[i]) {
+ if (a->tab[i] < b->tab[i])
+ res = -1;
+ else
+ res = 1;
+ break;
}
}
}
- break;
- }
- *plen = len;
- return 0;
-}
-
-#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
-
-static BOOL is_safe_integer(double d)
-{
- return isfinite(d) && floor(d) == d &&
- fabs(d) <= (double)MAX_SAFE_INTEGER;
-}
-
-int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
-{
- int64_t v;
- if (JS_ToInt64Sat(ctx, &v, val))
- return -1;
- if (v < 0 || v > MAX_SAFE_INTEGER) {
- JS_ThrowRangeError(ctx, "invalid array index");
- *plen = 0;
- return -1;
}
- *plen = v;
- return 0;
-}
-
-/* convert a value to a length between 0 and MAX_SAFE_INTEGER.
- return -1 for exception */
-static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
- JSValue val)
-{
- int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);
- JS_FreeValue(ctx, val);
return res;
}
-/* Note: can return an exception */
-static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)
-{
- double d;
- if (!JS_IsNumber(val))
- return FALSE;
- if (unlikely(JS_ToFloat64(ctx, &d, val)))
- return -1;
- return isfinite(d) && floor(d) == d;
-}
-
-static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)
-{
- uint32_t tag;
-
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- {
- int v;
- v = JS_VALUE_GET_INT(val);
- return (v < 0);
- }
- case JS_TAG_FLOAT64:
- {
- JSFloat64Union u;
- u.d = JS_VALUE_GET_FLOAT64(val);
- return (u.u64 >> 63);
- }
- case JS_TAG_BIG_INT:
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- /* Note: integer zeros are not necessarily positive */
- return p->num.sign && !bf_is_zero(&p->num);
- }
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- return p->num.sign;
- }
- break;
- case JS_TAG_BIG_DECIMAL:
- {
- JSBigDecimal *p = JS_VALUE_GET_PTR(val);
- return p->num.sign;
- }
- break;
+/* contains 10^i */
+static const js_limb_t js_pow_dec[JS_LIMB_DIGITS + 1] = {
+ 1U,
+ 10U,
+ 100U,
+ 1000U,
+ 10000U,
+ 100000U,
+ 1000000U,
+ 10000000U,
+ 100000000U,
+ 1000000000U,
+#if JS_LIMB_BITS == 64
+ 10000000000U,
+ 100000000000U,
+ 1000000000000U,
+ 10000000000000U,
+ 100000000000000U,
+ 1000000000000000U,
+ 10000000000000000U,
+ 100000000000000000U,
+ 1000000000000000000U,
+ 10000000000000000000U,
#endif
- default:
- return FALSE;
- }
-}
-
-static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)
-{
- JSValue ret;
- bf_t a_s, *a;
- char *str;
- int saved_sign;
-
- a = JS_ToBigInt(ctx, &a_s, val);
- if (!a)
- return JS_EXCEPTION;
- saved_sign = a->sign;
- if (a->expn == BF_EXP_ZERO)
- a->sign = 0;
- str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC |
- BF_FTOA_JS_QUIRKS);
- a->sign = saved_sign;
- JS_FreeBigInt(ctx, a, &a_s);
- if (!str)
- return JS_ThrowOutOfMemory(ctx);
- ret = JS_NewString(ctx, str);
- bf_free(ctx->bf_ctx, str);
- return ret;
-}
-
-static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)
-{
- return js_bigint_to_string1(ctx, val, 10);
-}
-
-#ifdef CONFIG_BIGNUM
+};
-static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix,
- limb_t prec, bf_flags_t flags)
+/* syntax: [-]digits in base radix. Return NULL if memory error. radix
+ = 10, 2, 8 or 16. */
+static JSBigInt *js_bigint_from_string(JSContext *ctx,
+ const char *str, int radix)
{
- JSValue val, ret;
- bf_t a_s, *a;
- char *str;
- int saved_sign;
-
- val = JS_ToNumeric(ctx, val1);
- if (JS_IsException(val))
- return val;
- a = JS_ToBigFloat(ctx, &a_s, val);
- if (!a) {
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
+ const char *p = str;
+ int is_neg, n_digits, n_limbs, len, log2_radix, n_bits, i;
+ JSBigInt *r;
+ js_limb_t v, c, h;
+
+ is_neg = 0;
+ if (*p == '-') {
+ is_neg = 1;
+ p++;
}
- saved_sign = a->sign;
- if (a->expn == BF_EXP_ZERO)
- a->sign = 0;
- flags |= BF_FTOA_JS_QUIRKS;
- if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) {
- /* Note: for floating point numbers with a radix which is not
- a power of two, the current precision is used to compute
- the number of digits. */
- if ((radix & (radix - 1)) != 0) {
- bf_t r_s, *r = &r_s;
- int prec, flags1;
- /* must round first */
- if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
- prec = ctx->fp_env.prec;
- flags1 = ctx->fp_env.flags &
- (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT));
+ while (*p == '0')
+ p++;
+ n_digits = strlen(p);
+ log2_radix = 32 - clz32(radix - 1); /* ceil(log2(radix)) */
+ /* compute the maximum number of limbs */
+ /* XXX: overflow */
+ if (radix == 10) {
+ n_bits = (n_digits * 27 + 7) / 8; /* >= ceil(n_digits * log2(10)) */
+ } else {
+ n_bits = n_digits * log2_radix;
+ }
+ /* we add one extra bit for the sign */
+ n_limbs = max_int(1, n_bits / JS_LIMB_BITS + 1);
+ r = js_bigint_new(ctx, n_limbs);
+ if (!r)
+ return NULL;
+ if (radix == 10) {
+ int digits_per_limb = JS_LIMB_DIGITS;
+ len = 1;
+ r->tab[0] = 0;
+ for(;;) {
+ /* XXX: slow */
+ v = 0;
+ for(i = 0; i < digits_per_limb; i++) {
+ c = to_digit(*p);
+ if (c >= radix)
+ break;
+ p++;
+ v = v * 10 + c;
+ }
+ if (i == 0)
+ break;
+ if (len == 1 && r->tab[0] == 0) {
+ r->tab[0] = v;
} else {
- prec = 53;
- flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL;
- }
- bf_init(ctx->bf_ctx, r);
- bf_set(r, a);
- bf_round(r, prec, flags1 | BF_RNDN);
- str = bf_ftoa(NULL, r, radix, prec, flags1 | flags);
- bf_delete(r);
- } else {
- str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags);
- }
- } else {
- str = bf_ftoa(NULL, a, radix, prec, flags);
+ h = mp_mul1(r->tab, r->tab, len, js_pow_dec[i], v);
+ if (h != 0) {
+ r->tab[len++] = h;
+ }
+ }
+ }
+ /* add one extra limb to have the correct sign*/
+ if ((r->tab[len - 1] >> (JS_LIMB_BITS - 1)) != 0)
+ r->tab[len++] = 0;
+ r->len = len;
+ } else {
+ unsigned int bit_pos, shift, pos;
+
+ /* power of two base: no multiplication is needed */
+ r->len = n_limbs;
+ memset(r->tab, 0, sizeof(r->tab[0]) * n_limbs);
+ for(i = 0; i < n_digits; i++) {
+ c = to_digit(p[n_digits - 1 - i]);
+ assert(c < radix);
+ bit_pos = i * log2_radix;
+ shift = bit_pos & (JS_LIMB_BITS - 1);
+ pos = bit_pos / JS_LIMB_BITS;
+ r->tab[pos] |= c << shift;
+ /* if log2_radix does not divide JS_LIMB_BITS, needed an
+ additional op */
+ if (shift + log2_radix > JS_LIMB_BITS) {
+ r->tab[pos + 1] |= c >> (JS_LIMB_BITS - shift);
+ }
+ }
+ }
+ r = js_bigint_normalize(ctx, r);
+ /* XXX: could do it in place */
+ if (is_neg) {
+ JSBigInt *r1;
+ r1 = js_bigint_neg(ctx, r);
+ js_free(ctx, r);
+ r = r1;
}
- a->sign = saved_sign;
- if (a == &a_s)
- bf_delete(a);
- JS_FreeValue(ctx, val);
- if (!str)
- return JS_ThrowOutOfMemory(ctx);
- ret = JS_NewString(ctx, str);
- bf_free(ctx->bf_ctx, str);
- return ret;
-}
-
-static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val)
-{
- return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
-}
-
-static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val,
- limb_t prec, int flags)
-{
- JSValue ret;
- bfdec_t *a;
- char *str;
- int saved_sign;
-
- a = JS_ToBigDecimal(ctx, val);
- if (!a)
- return JS_EXCEPTION;
- saved_sign = a->sign;
- if (a->expn == BF_EXP_ZERO)
- a->sign = 0;
- str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS);
- a->sign = saved_sign;
- if (!str)
- return JS_ThrowOutOfMemory(ctx);
- ret = JS_NewString(ctx, str);
- bf_free(ctx->bf_ctx, str);
- return ret;
-}
-
-static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)
-{
- return js_bigdecimal_to_string1(ctx, val, 0,
- BF_RNDZ | BF_FTOA_FORMAT_FREE);
+ return r;
}
-#endif /* CONFIG_BIGNUM */
-
/* 2 <= base <= 36 */
static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
-static char *i64toa(char *buf_end, int64_t n, unsigned int base)
+static char *u64toa(char *q, int64_t n, unsigned int base)
{
- char *q = buf_end;
- int digit, is_neg;
-
- is_neg = 0;
- if (n < 0) {
- is_neg = 1;
- n = -n;
- }
- *--q = '\0';
+ int digit;
if (base == 10) {
/* division by known base uses multiplication */
do {
*--q = digits[digit];
} while (n != 0);
}
- if (is_neg)
- *--q = '-';
return q;
}
-/* buf1 contains the printf result */
-static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,
- int rounding_mode, char *buf1, int buf1_size)
+static char *i64toa(char *buf_end, int64_t n, unsigned int base)
{
- if (rounding_mode != FE_TONEAREST)
- fesetround(rounding_mode);
- snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);
- if (rounding_mode != FE_TONEAREST)
- fesetround(FE_TONEAREST);
- *sign = (buf1[0] == '-');
- /* mantissa */
- buf[0] = buf1[1];
- if (n_digits > 1)
- memcpy(buf + 1, buf1 + 3, n_digits - 1);
- buf[n_digits] = '\0';
- /* exponent */
- *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;
-}
+ char *q = buf_end;
+ int is_neg;
-/* maximum buffer size for js_dtoa */
-#define JS_DTOA_BUF_SIZE 128
+ is_neg = 0;
+ if (n < 0) {
+ is_neg = 1;
+ n = -n;
+ }
+ *--q = '\0';
+ q = u64toa(q, n, base);
+ if (is_neg)
+ *--q = '-';
+ return q;
+}
-/* needed because ecvt usually limits the number of digits to
- 17. Return the number of digits. */
-static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,
- BOOL is_fixed)
+/* len >= 1. 2 <= radix <= 36 */
+static char *limb_to_a(char *q, js_limb_t n, unsigned int radix, int len)
{
- int rounding_mode;
- char buf_tmp[JS_DTOA_BUF_SIZE];
+ int digit, i;
- if (!is_fixed) {
- unsigned int n_digits_min, n_digits_max;
- /* find the minimum amount of digits (XXX: inefficient but simple) */
- n_digits_min = 1;
- n_digits_max = 17;
- while (n_digits_min < n_digits_max) {
- n_digits = (n_digits_min + n_digits_max) / 2;
- js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,
- buf_tmp, sizeof(buf_tmp));
- if (strtod(buf_tmp, NULL) == d) {
- /* no need to keep the trailing zeros */
- while (n_digits >= 2 && buf[n_digits - 1] == '0')
- n_digits--;
- n_digits_max = n_digits;
- } else {
- n_digits_min = n_digits + 1;
- }
+ if (radix == 10) {
+ /* specific case with constant divisor */
+ /* XXX: optimize */
+ for(i = 0; i < len; i++) {
+ digit = (js_limb_t)n % 10;
+ n = (js_limb_t)n / 10;
+ *--q = digit + '0';
}
- n_digits = n_digits_max;
- rounding_mode = FE_TONEAREST;
} else {
- rounding_mode = FE_TONEAREST;
-#ifdef CONFIG_PRINTF_RNDN
- {
- char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];
- int decpt1, sign1, decpt2, sign2;
- /* The JS rounding is specified as round to nearest ties away
- from zero (RNDNA), but in printf the "ties" case is not
- specified (for example it is RNDN for glibc, RNDNA for
- Windows), so we must round manually. */
- js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,
- buf_tmp, sizeof(buf_tmp));
- /* XXX: could use 2 digits to reduce the average running time */
- if (buf1[n_digits] == '5') {
- js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,
- buf_tmp, sizeof(buf_tmp));
- js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,
- buf_tmp, sizeof(buf_tmp));
- if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {
- /* exact result: round away from zero */
- if (sign1)
- rounding_mode = FE_DOWNWARD;
- else
- rounding_mode = FE_UPWARD;
- }
- }
+ for(i = 0; i < len; i++) {
+ digit = (js_limb_t)n % radix;
+ n = (js_limb_t)n / radix;
+ *--q = digits[digit];
}
-#endif /* CONFIG_PRINTF_RNDN */
}
- js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,
- buf_tmp, sizeof(buf_tmp));
- return n_digits;
-}
-
-static int js_fcvt1(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits,
- int rounding_mode)
-{
- int n;
- if (rounding_mode != FE_TONEAREST)
- fesetround(rounding_mode);
- n = snprintf(*buf, sizeof(*buf), "%.*f", n_digits, d);
- if (rounding_mode != FE_TONEAREST)
- fesetround(FE_TONEAREST);
- assert(n < sizeof(*buf));
- return n;
+ return q;
}
-static void js_fcvt(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits)
-{
- int rounding_mode;
- rounding_mode = FE_TONEAREST;
-#ifdef CONFIG_PRINTF_RNDN
- {
- int n1, n2;
- char buf1[JS_DTOA_BUF_SIZE];
- char buf2[JS_DTOA_BUF_SIZE];
+#define JS_RADIX_MAX 36
- /* The JS rounding is specified as round to nearest ties away from
- zero (RNDNA), but in printf the "ties" case is not specified
- (for example it is RNDN for glibc, RNDNA for Windows), so we
- must round manually. */
- n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_TONEAREST);
- rounding_mode = FE_TONEAREST;
- /* XXX: could use 2 digits to reduce the average running time */
- if (buf1[n1 - 1] == '5') {
- n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_DOWNWARD);
- n2 = js_fcvt1(&buf2, d, n_digits + 1, FE_UPWARD);
- if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {
- /* exact result: round away from zero */
- if (buf1[0] == '-')
- rounding_mode = FE_DOWNWARD;
- else
- rounding_mode = FE_UPWARD;
- }
- }
- }
-#endif /* CONFIG_PRINTF_RNDN */
- js_fcvt1(buf, d, n_digits, rounding_mode);
-}
+static const uint8_t digits_per_limb_table[JS_RADIX_MAX - 1] = {
+#if JS_LIMB_BITS == 32
+32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+#else
+64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12,
+#endif
+};
-/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */
-/* use as many digits as necessary */
-#define JS_DTOA_VAR_FORMAT (0 << 0)
-/* use n_digits significant digits (1 <= n_digits <= 101) */
-#define JS_DTOA_FIXED_FORMAT (1 << 0)
-/* force fractional format: [-]dd.dd with n_digits fractional digits */
-#define JS_DTOA_FRAC_FORMAT (2 << 0)
-/* force exponential notation either in fixed or variable format */
-#define JS_DTOA_FORCE_EXP (1 << 2)
+static const js_limb_t radix_base_table[JS_RADIX_MAX - 1] = {
+#if JS_LIMB_BITS == 32
+ 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395,
+ 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91,
+ 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021,
+ 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571,
+ 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d,
+ 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51,
+ 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899,
+ 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1,
+ 0x5c13d840, 0x6d91b519, 0x81bf1000,
+#else
+ 0x0000000000000000, 0xa8b8b452291fe821, 0x0000000000000000, 0x6765c793fa10079d,
+ 0x41c21cb8e1000000, 0x3642798750226111, 0x8000000000000000, 0xa8b8b452291fe821,
+ 0x8ac7230489e80000, 0x4d28cb56c33fa539, 0x1eca170c00000000, 0x780c7372621bd74d,
+ 0x1e39a5057d810000, 0x5b27ac993df97701, 0x0000000000000000, 0x27b95e997e21d9f1,
+ 0x5da0e1e53c5c8000, 0xd2ae3299c1c4aedb, 0x16bcc41e90000000, 0x2d04b7fdd9c0ef49,
+ 0x5658597bcaa24000, 0xa0e2073737609371, 0x0c29e98000000000, 0x14adf4b7320334b9,
+ 0x226ed36478bfa000, 0x383d9170b85ff80b, 0x5a3c23e39c000000, 0x8e65137388122bcd,
+ 0xdd41bb36d259e000, 0x0aee5720ee830681, 0x1000000000000000, 0x172588ad4f5f0981,
+ 0x211e44f7d02c1000, 0x2ee56725f06e5c71, 0x41c21cb8e1000000,
+#endif
+};
-/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.
- XXX: radix != 10 is only supported for small integers
-*/
-static void js_dtoa1(char (*buf)[JS_DTOA_BUF_SIZE], double d,
- int radix, int n_digits, int flags)
+static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)
{
- char *q;
-
- if (!isfinite(d)) {
- if (isnan(d)) {
- pstrcpy(*buf, sizeof(*buf), "NaN");
- } else if (d < 0) {
- pstrcpy(*buf, sizeof(*buf), "-Infinity");
- } else {
- pstrcpy(*buf, sizeof(*buf), "Infinity");
- }
- } else if (flags == JS_DTOA_VAR_FORMAT) {
- int64_t i64;
- char buf1[70], *ptr;
- if (d > (double)MAX_SAFE_INTEGER || d < (double)-MAX_SAFE_INTEGER)
- goto generic_conv;
- i64 = (int64_t)d;
- if (d != i64)
- goto generic_conv;
- /* fast path for integers */
- ptr = i64toa(buf1 + sizeof(buf1), i64, radix);
- pstrcpy(*buf, sizeof(*buf), ptr);
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) {
+ char buf[66], *q;
+
+ q = i64toa(buf + sizeof(buf), JS_VALUE_GET_SHORT_BIG_INT(val), radix);
+ return JS_NewString(ctx, q);
} else {
- if (d == 0.0)
- d = 0.0; /* convert -0 to 0 */
- if (flags == JS_DTOA_FRAC_FORMAT) {
- js_fcvt(buf, d, n_digits);
- } else {
- char buf1[JS_DTOA_BUF_SIZE];
- int sign, decpt, k, n, i, p, n_max;
- BOOL is_fixed;
- generic_conv:
- is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);
- if (is_fixed) {
- n_max = n_digits;
- } else {
- n_max = 21;
- }
- /* the number has k digits (k >= 1) */
- k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);
- n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */
- q = *buf;
- if (sign)
- *q++ = '-';
- if (flags & JS_DTOA_FORCE_EXP)
- goto force_exp;
- if (n >= 1 && n <= n_max) {
- if (k <= n) {
- memcpy(q, buf1, k);
- q += k;
- for(i = 0; i < (n - k); i++)
- *q++ = '0';
- *q = '\0';
+ JSBigInt *r, *tmp = NULL;
+ char *buf, *q;
+ int is_neg, n_bits, log2_radix, n_digits;
+ BOOL is_binary_radix;
+ JSValue res;
+
+ assert(JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT);
+ r = JS_VALUE_GET_PTR(val);
+ if (r->len == 1 && r->tab[0] == 0) {
+ /* '0' case */
+ return JS_NewString(ctx, "0");
+ }
+ is_binary_radix = ((radix & (radix - 1)) == 0);
+ is_neg = js_bigint_sign(r);
+ if (is_neg) {
+ tmp = js_bigint_neg(ctx, r);
+ if (!tmp)
+ return JS_EXCEPTION;
+ r = tmp;
+ } else if (!is_binary_radix) {
+ /* need to modify 'r' */
+ tmp = js_bigint_new(ctx, r->len);
+ if (!tmp)
+ return JS_EXCEPTION;
+ memcpy(tmp->tab, r->tab, r->len * sizeof(r->tab[0]));
+ r = tmp;
+ }
+ log2_radix = 31 - clz32(radix); /* floor(log2(radix)) */
+ n_bits = r->len * JS_LIMB_BITS - js_limb_clz(r->tab[r->len - 1]);
+ /* n_digits is exact only if radix is a power of
+ two. Otherwise it is >= the exact number of digits */
+ n_digits = (n_bits + log2_radix - 1) / log2_radix;
+ /* XXX: could directly build the JSString */
+ buf = js_malloc(ctx, n_digits + is_neg + 1);
+ if (!buf) {
+ js_free(ctx, tmp);
+ return JS_EXCEPTION;
+ }
+ q = buf + n_digits + is_neg + 1;
+ *--q = '\0';
+ if (!is_binary_radix) {
+ int len;
+ js_limb_t radix_base, v;
+ radix_base = radix_base_table[radix - 2];
+ len = r->len;
+ for(;;) {
+ /* remove leading zero limbs */
+ while (len > 1 && r->tab[len - 1] == 0)
+ len--;
+ if (len == 1 && r->tab[0] < radix_base) {
+ v = r->tab[0];
+ if (v != 0) {
+ q = u64toa(q, v, radix);
+ }
+ break;
} else {
- /* k > n */
- memcpy(q, buf1, n);
- q += n;
- *q++ = '.';
- for(i = 0; i < (k - n); i++)
- *q++ = buf1[n + i];
- *q = '\0';
+ v = mp_div1(r->tab, r->tab, len, radix_base, 0);
+ q = limb_to_a(q, v, radix, digits_per_limb_table[radix - 2]);
}
- } else if (n >= -5 && n <= 0) {
- *q++ = '0';
- *q++ = '.';
- for(i = 0; i < -n; i++)
- *q++ = '0';
- memcpy(q, buf1, k);
- q += k;
- *q = '\0';
- } else {
- force_exp:
- /* exponential notation */
- *q++ = buf1[0];
- if (k > 1) {
- *q++ = '.';
- for(i = 1; i < k; i++)
- *q++ = buf1[i];
+ }
+ } else {
+ int i, shift;
+ unsigned int bit_pos, pos, c;
+
+ /* radix is a power of two */
+ for(i = 0; i < n_digits; i++) {
+ bit_pos = i * log2_radix;
+ pos = bit_pos / JS_LIMB_BITS;
+ shift = bit_pos % JS_LIMB_BITS;
+ if (likely((shift + log2_radix) <= JS_LIMB_BITS)) {
+ c = r->tab[pos] >> shift;
+ } else {
+ c = (r->tab[pos] >> shift) |
+ (r->tab[pos + 1] << (JS_LIMB_BITS - shift));
}
- *q++ = 'e';
- p = n - 1;
- if (p >= 0)
- *q++ = '+';
- snprintf(q, *buf + sizeof(*buf) - q, "%d", p);
+ c &= (radix - 1);
+ *--q = digits[c];
}
}
+ if (is_neg)
+ *--q = '-';
+ js_free(ctx, tmp);
+ res = JS_NewString(ctx, q);
+ js_free(ctx, buf);
+ return res;
}
}
-static JSValue js_dtoa(JSContext *ctx,
- double d, int radix, int n_digits, int flags)
+/* if possible transform a BigInt to short big and free it, otherwise
+ return a normal bigint */
+static JSValue JS_CompactBigInt(JSContext *ctx, JSBigInt *p)
{
- char buf[JS_DTOA_BUF_SIZE];
- js_dtoa1(&buf, d, radix, n_digits, flags);
- return JS_NewString(ctx, buf);
+ JSValue res;
+ if (p->len == 1) {
+ res = __JS_NewShortBigInt(ctx, (js_slimb_t)p->tab[0]);
+ js_free(ctx, p);
+ return res;
+ } else {
+ return JS_MKPTR(JS_TAG_BIG_INT, p);
+ }
}
-static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix)
+/* XXX: remove */
+static double js_strtod(const char *str, int radix, BOOL is_float)
{
- char buf[2200], *ptr, *ptr2;
- /* d is finite */
- int sign = d < 0;
- int digit;
- double frac, d0;
- int64_t n0 = 0;
- d = fabs(d);
- d0 = trunc(d);
- frac = d - d0;
- ptr = buf + 1100;
- *ptr = '\0';
- if (d0 <= MAX_SAFE_INTEGER) {
- int64_t n = n0 = (int64_t)d0;
- while (n >= radix) {
- digit = n % radix;
- n = n / radix;
- *--ptr = digits[digit];
- }
- *--ptr = digits[(int)n];
- } else {
- /* no decimals */
- while (d0 >= radix) {
- digit = fmod(d0, radix);
- d0 = trunc(d0 / radix);
- if (d0 >= MAX_SAFE_INTEGER)
- digit = 0;
- *--ptr = digits[digit];
+ double d;
+ int c;
+
+ if (!is_float || radix != 10) {
+ const char *p = str;
+ uint64_t n_max, n;
+ int int_exp, is_neg;
+
+ is_neg = 0;
+ if (*p == '-') {
+ is_neg = 1;
+ p++;
}
- *--ptr = digits[(int)d0];
- goto done;
- }
- if (frac != 0) {
- double log2_radix = log2(radix);
- double prec = 1023 + 51; // handle subnormals
- ptr2 = buf + 1100;
- *ptr2++ = '.';
- while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) {
- frac *= radix;
- digit = trunc(frac);
- frac -= digit;
- *ptr2++ = digits[digit];
- n0 = n0 * radix + digit;
- prec -= log2_radix;
+
+ /* skip leading zeros */
+ while (*p == '0')
+ p++;
+ n = 0;
+ if (radix == 10)
+ n_max = ((uint64_t)-1 - 9) / 10; /* most common case */
+ else
+ n_max = ((uint64_t)-1 - (radix - 1)) / radix;
+ /* XXX: could be more precise */
+ int_exp = 0;
+ while (*p != '\0') {
+ c = to_digit((uint8_t)*p);
+ if (c >= radix)
+ break;
+ if (n <= n_max) {
+ n = n * radix + c;
+ } else {
+ if (radix == 10)
+ goto strtod_case;
+ int_exp++;
+ }
+ p++;
}
- *ptr2 = '\0';
- if (frac * radix >= radix / 2) {
- char nine = digits[radix - 1];
- // round to closest
- while (ptr2[-1] == nine)
- *--ptr2 = '\0';
- if (ptr2[-1] == '.') {
- *--ptr2 = '\0';
- while (ptr2[-1] == nine)
- *--ptr2 = '0';
+ d = n;
+ if (int_exp != 0) {
+ d *= pow(radix, int_exp);
+ }
+ if (is_neg)
+ d = -d;
+ } else {
+ strtod_case:
+ d = strtod(str, NULL);
+ }
+ return d;
+}
+
+#define ATOD_INT_ONLY (1 << 0)
+/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
+#define ATOD_ACCEPT_BIN_OCT (1 << 2)
+/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
+#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4)
+/* accept _ between digits as a digit separator */
+#define ATOD_ACCEPT_UNDERSCORES (1 << 5)
+/* allow a suffix to override the type */
+#define ATOD_ACCEPT_SUFFIX (1 << 6)
+/* default type */
+#define ATOD_TYPE_MASK (3 << 7)
+#define ATOD_TYPE_FLOAT64 (0 << 7)
+#define ATOD_TYPE_BIG_INT (1 << 7)
+/* accept -0x1 */
+#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)
+
+/* return an exception in case of memory error. Return JS_NAN if
+ invalid syntax */
+static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
+ int radix, int flags)
+{
+ const char *p, *p_start;
+ int sep, is_neg;
+ BOOL is_float, has_legacy_octal;
+ int atod_type = flags & ATOD_TYPE_MASK;
+ char buf1[64], *buf;
+ int i, j, len;
+ BOOL buf_allocated = FALSE;
+ JSValue val;
+
+ /* optional separator between digits */
+ sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
+ has_legacy_octal = FALSE;
+
+ p = str;
+ p_start = p;
+ is_neg = 0;
+ if (p[0] == '+') {
+ p++;
+ p_start++;
+ if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
+ goto no_radix_prefix;
+ } else if (p[0] == '-') {
+ p++;
+ p_start++;
+ is_neg = 1;
+ if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
+ goto no_radix_prefix;
+ }
+ if (p[0] == '0') {
+ if ((p[1] == 'x' || p[1] == 'X') &&
+ (radix == 0 || radix == 16)) {
+ p += 2;
+ radix = 16;
+ } else if ((p[1] == 'o' || p[1] == 'O') &&
+ radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
+ p += 2;
+ radix = 8;
+ } else if ((p[1] == 'b' || p[1] == 'B') &&
+ radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
+ p += 2;
+ radix = 2;
+ } else if ((p[1] >= '0' && p[1] <= '9') &&
+ radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {
+ int i;
+ has_legacy_octal = TRUE;
+ sep = 256;
+ for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
+ continue;
+ if (p[i] == '8' || p[i] == '9')
+ goto no_prefix;
+ p += 1;
+ radix = 8;
+ } else {
+ goto no_prefix;
+ }
+ /* there must be a digit after the prefix */
+ if (to_digit((uint8_t)*p) >= radix)
+ goto fail;
+ no_prefix: ;
+ } else {
+ no_radix_prefix:
+ if (!(flags & ATOD_INT_ONLY) &&
+ (atod_type == ATOD_TYPE_FLOAT64) &&
+ strstart(p, "Infinity", &p)) {
+ double d = 1.0 / 0.0;
+ if (is_neg)
+ d = -d;
+ val = JS_NewFloat64(ctx, d);
+ goto done;
+ }
+ }
+ if (radix == 0)
+ radix = 10;
+ is_float = FALSE;
+ p_start = p;
+ while (to_digit((uint8_t)*p) < radix
+ || (*p == sep && (radix != 10 ||
+ p != p_start + 1 || p[-1] != '0') &&
+ to_digit((uint8_t)p[1]) < radix)) {
+ p++;
+ }
+ if (!(flags & ATOD_INT_ONLY)) {
+ if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {
+ is_float = TRUE;
+ p++;
+ if (*p == sep)
+ goto fail;
+ while (to_digit((uint8_t)*p) < radix ||
+ (*p == sep && to_digit((uint8_t)p[1]) < radix))
+ p++;
+ }
+ if (p > p_start &&
+ (((*p == 'e' || *p == 'E') && radix == 10) ||
+ ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
+ const char *p1 = p + 1;
+ is_float = TRUE;
+ if (*p1 == '+') {
+ p1++;
+ } else if (*p1 == '-') {
+ p1++;
}
- if (ptr2 - 1 == ptr)
- *--ptr = '1';
- else
- ptr2[-1] += 1;
+ if (is_digit((uint8_t)*p1)) {
+ p = p1 + 1;
+ while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))
+ p++;
+ }
+ }
+ }
+ if (p == p_start)
+ goto fail;
+
+ buf = buf1;
+ buf_allocated = FALSE;
+ len = p - p_start;
+ if (unlikely((len + 2) > sizeof(buf1))) {
+ buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */
+ if (!buf)
+ goto mem_error;
+ buf_allocated = TRUE;
+ }
+ /* remove the separators and the radix prefixes */
+ j = 0;
+ if (is_neg)
+ buf[j++] = '-';
+ for (i = 0; i < len; i++) {
+ if (p_start[i] != '_')
+ buf[j++] = p_start[i];
+ }
+ buf[j] = '\0';
+
+ if (flags & ATOD_ACCEPT_SUFFIX) {
+ if (*p == 'n') {
+ p++;
+ atod_type = ATOD_TYPE_BIG_INT;
} else {
- while (ptr2[-1] == '0')
- *--ptr2 = '\0';
- if (ptr2[-1] == '.')
- *--ptr2 = '\0';
+ if (is_float && radix != 10)
+ goto fail;
+ }
+ } else {
+ if (atod_type == ATOD_TYPE_FLOAT64) {
+ if (is_float && radix != 10)
+ goto fail;
+ }
+ }
+
+ switch(atod_type) {
+ case ATOD_TYPE_FLOAT64:
+ {
+ double d;
+ d = js_strtod(buf, radix, is_float);
+ /* return int or float64 */
+ val = JS_NewFloat64(ctx, d);
+ }
+ break;
+ case ATOD_TYPE_BIG_INT:
+ {
+ JSBigInt *r;
+ if (has_legacy_octal || is_float)
+ goto fail;
+ r = js_bigint_from_string(ctx, buf, radix);
+ if (!r)
+ goto mem_error;
+ val = JS_CompactBigInt(ctx, r);
}
+ break;
+ default:
+ abort();
}
+
done:
- ptr[-1] = '-';
- ptr -= sign;
- return JS_NewString(ctx, ptr);
+ if (buf_allocated)
+ js_free_rt(ctx->rt, buf);
+ if (pp)
+ *pp = p;
+ return val;
+ fail:
+ val = JS_NAN;
+ goto done;
+ mem_error:
+ val = JS_ThrowOutOfMemory(ctx);
+ goto done;
}
-JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)
+typedef enum JSToNumberHintEnum {
+ TON_FLAG_NUMBER,
+ TON_FLAG_NUMERIC,
+} JSToNumberHintEnum;
+
+static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
+ JSToNumberHintEnum flag)
{
uint32_t tag;
- const char *str;
- char buf[32];
+ JSValue ret;
+ redo:
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
- case JS_TAG_STRING:
- return JS_DupValue(ctx, val);
+ case JS_TAG_BIG_INT:
+ case JS_TAG_SHORT_BIG_INT:
+ if (flag != TON_FLAG_NUMERIC) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert bigint to number");
+ }
+ ret = val;
+ break;
+ case JS_TAG_FLOAT64:
case JS_TAG_INT:
- snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));
- str = buf;
- goto new_string;
+ case JS_TAG_EXCEPTION:
+ ret = val;
+ break;
case JS_TAG_BOOL:
- return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
- JS_ATOM_true : JS_ATOM_false);
case JS_TAG_NULL:
- return JS_AtomToString(ctx, JS_ATOM_null);
+ ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
+ break;
case JS_TAG_UNDEFINED:
- return JS_AtomToString(ctx, JS_ATOM_undefined);
- case JS_TAG_EXCEPTION:
- return JS_EXCEPTION;
+ ret = JS_NAN;
+ break;
case JS_TAG_OBJECT:
+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+ if (JS_IsException(val))
+ return JS_EXCEPTION;
+ goto redo;
+ case JS_TAG_STRING:
{
- JSValue val1, ret;
- val1 = JS_ToPrimitive(ctx, val, HINT_STRING);
- if (JS_IsException(val1))
- return val1;
- ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);
- JS_FreeValue(ctx, val1);
- return ret;
+ const char *str;
+ const char *p;
+ size_t len;
+
+ str = JS_ToCStringLen(ctx, &len, val);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return JS_EXCEPTION;
+ p = str;
+ p += skip_spaces(p);
+ if ((p - str) == len) {
+ ret = JS_NewInt32(ctx, 0);
+ } else {
+ int flags = ATOD_ACCEPT_BIN_OCT;
+ ret = js_atof(ctx, p, &p, 0, flags);
+ if (!JS_IsException(ret)) {
+ p += skip_spaces(p);
+ if ((p - str) != len) {
+ JS_FreeValue(ctx, ret);
+ ret = JS_NAN;
+ }
+ }
+ }
+ JS_FreeCString(ctx, str);
}
break;
- case JS_TAG_FUNCTION_BYTECODE:
- str = "[function bytecode]";
- goto new_string;
case JS_TAG_SYMBOL:
- if (is_ToPropertyKey) {
- return JS_DupValue(ctx, val);
- } else {
- return JS_ThrowTypeError(ctx, "cannot convert symbol to string");
- }
- case JS_TAG_FLOAT64:
- return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
- JS_DTOA_VAR_FORMAT);
- case JS_TAG_BIG_INT:
- return ctx->rt->bigint_ops.to_string(ctx, val);
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- return ctx->rt->bigfloat_ops.to_string(ctx, val);
- case JS_TAG_BIG_DECIMAL:
- return ctx->rt->bigdecimal_ops.to_string(ctx, val);
-#endif
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert symbol to number");
default:
- str = "[unsupported type]";
- new_string:
- return JS_NewString(ctx, str);
+ JS_FreeValue(ctx, val);
+ ret = JS_NAN;
+ break;
}
+ return ret;
}
-JSValue JS_ToString(JSContext *ctx, JSValueConst val)
+static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
{
- return JS_ToStringInternal(ctx, val, FALSE);
+ return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);
}
-static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)
+static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)
{
- JSValue ret;
- ret = JS_ToString(ctx, val);
- JS_FreeValue(ctx, val);
- return ret;
+ return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);
}
-static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)
+static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
{
- if (JS_IsUndefined(val) || JS_IsNull(val))
- return JS_ToStringFree(ctx, val);
- return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);
+ return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
}
-JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
+static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,
+ JSValue val)
{
- return JS_ToStringInternal(ctx, val, TRUE);
+ double d;
+ uint32_t tag;
+
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ goto fail;
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ d = JS_VALUE_GET_INT(val);
+ break;
+ case JS_TAG_FLOAT64:
+ d = JS_VALUE_GET_FLOAT64(val);
+ break;
+ default:
+ abort();
+ }
+ *pres = d;
+ return 0;
+ fail:
+ *pres = JS_FLOAT64_NAN;
+ return -1;
}
-static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)
+static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
{
- uint32_t tag = JS_VALUE_GET_TAG(val);
- if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
- return JS_ThrowTypeError(ctx, "null or undefined are forbidden");
- return JS_ToString(ctx, val);
+ uint32_t tag;
+
+ tag = JS_VALUE_GET_TAG(val);
+ if (tag <= JS_TAG_NULL) {
+ *pres = JS_VALUE_GET_INT(val);
+ return 0;
+ } else if (JS_TAG_IS_FLOAT64(tag)) {
+ *pres = JS_VALUE_GET_FLOAT64(val);
+ return 0;
+ } else {
+ return __JS_ToFloat64Free(ctx, pres, val);
+ }
}
-static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
+int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)
{
- JSValue val;
- JSString *p;
- int i;
- uint32_t c;
- StringBuffer b_s, *b = &b_s;
- char buf[16];
+ return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));
+}
- val = JS_ToStringCheckObject(ctx, val1);
- if (JS_IsException(val))
- return val;
- p = JS_VALUE_GET_STRING(val);
+static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
+{
+ return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
+}
- if (string_buffer_init(ctx, b, p->len + 2))
- goto fail;
+/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
+static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
+{
+ uint32_t tag;
+ JSValue ret;
- if (string_buffer_putc8(b, '\"'))
- goto fail;
- for(i = 0; i < p->len; ) {
- c = string_getc(p, &i);
- switch(c) {
- case '\t':
- c = 't';
- goto quote;
- case '\r':
- c = 'r';
- goto quote;
- case '\n':
- c = 'n';
- goto quote;
- case '\b':
- c = 'b';
- goto quote;
- case '\f':
- c = 'f';
- goto quote;
- case '\"':
- case '\\':
- quote:
- if (string_buffer_putc8(b, '\\'))
- goto fail;
- if (string_buffer_putc8(b, c))
- goto fail;
- break;
- default:
- if (c < 32 || is_surrogate(c)) {
- snprintf(buf, sizeof(buf), "\\u%04x", c);
- if (string_buffer_puts8(b, buf))
- goto fail;
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (isnan(d)) {
+ ret = JS_NewInt32(ctx, 0);
} else {
- if (string_buffer_putc(b, c))
- goto fail;
+ /* convert -0 to +0 */
+ d = trunc(d) + 0.0;
+ ret = JS_NewFloat64(ctx, d);
}
- break;
}
+ break;
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ return val;
+ goto redo;
}
- if (string_buffer_putc8(b, '\"'))
- goto fail;
- JS_FreeValue(ctx, val);
- return string_buffer_end(b);
- fail:
- JS_FreeValue(ctx, val);
- string_buffer_free(b);
- return JS_EXCEPTION;
+ return ret;
}
-static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
+/* Note: the integer value is satured to 32 bits */
+static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
{
- printf("%14s %4s %4s %14s %10s %s\n",
- "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
-}
+ uint32_t tag;
+ int ret;
-/* for debug only: dump an object without side effect */
-static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
-{
- uint32_t i;
- char atom_buf[ATOM_GET_STR_BUF_SIZE];
- JSShape *sh;
- JSShapeProperty *prs;
- JSProperty *pr;
- BOOL is_first = TRUE;
-
- /* XXX: should encode atoms with special characters */
- sh = p->shape; /* the shape can be NULL while freeing an object */
- printf("%14p %4d ",
- (void *)p,
- p->header.ref_count);
- if (sh) {
- printf("%3d%c %14p ",
- sh->header.ref_count,
- " *"[sh->is_hashed],
- (void *)sh->proto);
- } else {
- printf("%3s %14s ", "-", "-");
- }
- printf("%10s ",
- JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));
- if (p->is_exotic && p->fast_array) {
- printf("[ ");
- for(i = 0; i < p->u.array.count; i++) {
- if (i != 0)
- printf(", ");
- switch (p->class_id) {
- case JS_CLASS_ARRAY:
- case JS_CLASS_ARGUMENTS:
- JS_DumpValueShort(rt, p->u.array.u.values[i]);
- break;
- case JS_CLASS_UINT8C_ARRAY:
- case JS_CLASS_INT8_ARRAY:
- case JS_CLASS_UINT8_ARRAY:
- case JS_CLASS_INT16_ARRAY:
- case JS_CLASS_UINT16_ARRAY:
- case JS_CLASS_INT32_ARRAY:
- case JS_CLASS_UINT32_ARRAY:
- case JS_CLASS_BIG_INT64_ARRAY:
- case JS_CLASS_BIG_UINT64_ARRAY:
- case JS_CLASS_FLOAT32_ARRAY:
- case JS_CLASS_FLOAT64_ARRAY:
- {
- int size = 1 << typed_array_size_log2(p->class_id);
- const uint8_t *b = p->u.array.u.uint8_ptr + i * size;
- while (size-- > 0)
- printf("%02X", *b++);
- }
- break;
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ ret = JS_VALUE_GET_INT(val);
+ break;
+ case JS_TAG_EXCEPTION:
+ *pres = 0;
+ return -1;
+ case JS_TAG_FLOAT64:
+ {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (isnan(d)) {
+ ret = 0;
+ } else {
+ if (d < INT32_MIN)
+ ret = INT32_MIN;
+ else if (d > INT32_MAX)
+ ret = INT32_MAX;
+ else
+ ret = (int)d;
}
}
- printf(" ] ");
- }
-
- if (sh) {
- printf("{ ");
- for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
- if (prs->atom != JS_ATOM_NULL) {
- pr = &p->prop[i];
- if (!is_first)
- printf(", ");
- printf("%s: ",
- JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));
- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
- printf("[getset %p %p]", (void *)pr->u.getset.getter,
- (void *)pr->u.getset.setter);
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- printf("[varref %p]", (void *)pr->u.var_ref);
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
- printf("[autoinit %p %d %p]",
- (void *)js_autoinit_get_realm(pr),
- js_autoinit_get_id(pr),
- (void *)pr->u.init.opaque);
- } else {
- JS_DumpValueShort(rt, pr->u.value);
- }
- is_first = FALSE;
- }
+ break;
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
}
- printf(" }");
+ goto redo;
}
+ *pres = ret;
+ return 0;
+}
- if (js_class_has_bytecode(p->class_id)) {
- JSFunctionBytecode *b = p->u.func.function_bytecode;
- JSVarRef **var_refs;
- if (b->closure_var_count) {
- var_refs = p->u.func.var_refs;
- printf(" Closure:");
- for(i = 0; i < b->closure_var_count; i++) {
- printf(" ");
- JS_DumpValueShort(rt, var_refs[i]->value);
- }
- if (p->u.func.home_object) {
- printf(" HomeObject: ");
- JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
- }
- }
- }
- printf("\n");
+int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)
+{
+ return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
}
-static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
+int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,
+ int min, int max, int min_offset)
{
- if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
- JS_DumpObject(rt, (JSObject *)p);
- } else {
- printf("%14p %4d ",
- (void *)p,
- p->ref_count);
- switch(p->gc_obj_type) {
- case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
- printf("[function bytecode]");
- break;
- case JS_GC_OBJ_TYPE_SHAPE:
- printf("[shape]");
- break;
- case JS_GC_OBJ_TYPE_VAR_REF:
- printf("[var_ref]");
- break;
- case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
- printf("[async_function]");
- break;
- case JS_GC_OBJ_TYPE_JS_CONTEXT:
- printf("[js_context]");
- break;
- default:
- printf("[unknown %d]", p->gc_obj_type);
- break;
+ int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
+ if (res == 0) {
+ if (*pres < min) {
+ *pres += min_offset;
+ if (*pres < min)
+ *pres = min;
+ } else {
+ if (*pres > max)
+ *pres = max;
}
- printf("\n");
}
+ return res;
}
-static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
- JSValueConst val)
+static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)
{
- uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
- const char *str;
+ uint32_t tag;
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_INT:
- printf("%d", JS_VALUE_GET_INT(val));
- break;
case JS_TAG_BOOL:
- if (JS_VALUE_GET_BOOL(val))
- str = "true";
- else
- str = "false";
- goto print_str;
case JS_TAG_NULL:
- str = "null";
- goto print_str;
- case JS_TAG_EXCEPTION:
- str = "exception";
- goto print_str;
- case JS_TAG_UNINITIALIZED:
- str = "uninitialized";
- goto print_str;
case JS_TAG_UNDEFINED:
- str = "undefined";
- print_str:
- printf("%s", str);
- break;
+ *pres = JS_VALUE_GET_INT(val);
+ return 0;
+ case JS_TAG_EXCEPTION:
+ *pres = 0;
+ return -1;
case JS_TAG_FLOAT64:
- printf("%.14g", JS_VALUE_GET_FLOAT64(val));
- break;
- case JS_TAG_BIG_INT:
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- char *str;
- str = bf_ftoa(NULL, &p->num, 10, 0,
- BF_RNDZ | BF_FTOA_FORMAT_FRAC);
- printf("%sn", str);
- bf_realloc(&rt->bf_ctx, str, 0);
- }
- break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- char *str;
- str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF,
- BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX);
- printf("%sl", str);
- bf_free(&rt->bf_ctx, str);
- }
- break;
- case JS_TAG_BIG_DECIMAL:
- {
- JSBigDecimal *p = JS_VALUE_GET_PTR(val);
- char *str;
- str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF,
- BF_RNDZ | BF_FTOA_FORMAT_FREE);
- printf("%sm", str);
- bf_free(&rt->bf_ctx, str);
- }
- break;
-#endif
- case JS_TAG_STRING:
- {
- JSString *p;
- p = JS_VALUE_GET_STRING(val);
- JS_DumpString(rt, p);
- }
- break;
- case JS_TAG_FUNCTION_BYTECODE:
- {
- JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);
- char buf[ATOM_GET_STR_BUF_SIZE];
- printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
- }
- break;
- case JS_TAG_OBJECT:
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSAtom atom = rt->class_array[p->class_id].class_name;
- char atom_buf[ATOM_GET_STR_BUF_SIZE];
- printf("[%s %p]",
- JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);
- }
- break;
- case JS_TAG_SYMBOL:
{
- JSAtomStruct *p = JS_VALUE_GET_PTR(val);
- char atom_buf[ATOM_GET_STR_BUF_SIZE];
- printf("Symbol(%s)",
- JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (isnan(d)) {
+ *pres = 0;
+ } else {
+ if (d < INT64_MIN)
+ *pres = INT64_MIN;
+ else if (d >= 0x1p63) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */
+ *pres = INT64_MAX;
+ else
+ *pres = (int64_t)d;
+ }
}
- break;
- case JS_TAG_MODULE:
- printf("[module]");
- break;
+ return 0;
default:
- printf("[unknown tag %d]", tag);
- break;
- }
-}
-
-static __maybe_unused void JS_DumpValue(JSContext *ctx,
- JSValueConst val)
-{
- JS_DumpValueShort(ctx->rt, val);
-}
-
-static __maybe_unused void JS_PrintValue(JSContext *ctx,
- const char *str,
- JSValueConst val)
-{
- printf("%s=", str);
- JS_DumpValueShort(ctx->rt, val);
- printf("\n");
-}
-
-/* return -1 if exception (proxy case) or TRUE/FALSE */
-// TODO: should take flags to make proxy resolution and exceptions optional
-int JS_IsArray(JSContext *ctx, JSValueConst val)
-{
- if (js_resolve_proxy(ctx, &val, TRUE))
- return -1;
- if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- return p->class_id == JS_CLASS_ARRAY;
- } else {
- return FALSE;
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
+ }
+ goto redo;
}
}
-static double js_pow(double a, double b)
+int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)
{
- if (unlikely(!isfinite(b)) && fabs(a) == 1) {
- /* not compatible with IEEE 754 */
- return JS_FLOAT64_NAN;
- } else {
- return pow(a, b);
- }
+ return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
}
-JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v)
+int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,
+ int64_t min, int64_t max, int64_t neg_offset)
{
- JSValue val;
- bf_t *a;
- val = JS_NewBigInt(ctx);
- if (JS_IsException(val))
- return val;
- a = JS_GetBigInt(val);
- if (bf_set_si(a, v)) {
- JS_FreeValue(ctx, val);
- return JS_ThrowOutOfMemory(ctx);
+ int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
+ if (res == 0) {
+ if (*pres < 0)
+ *pres += neg_offset;
+ if (*pres < min)
+ *pres = min;
+ else if (*pres > max)
+ *pres = max;
}
- return val;
+ return res;
}
-JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
+/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)
+ in case of exception */
+static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
{
- if (is_math_mode(ctx) &&
- v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
- return JS_NewInt64(ctx, v);
- } else {
- return JS_NewBigInt64_1(ctx, v);
- }
-}
+ uint32_t tag;
+ int64_t ret;
-JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
-{
- JSValue val;
- if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) {
- val = JS_NewInt64(ctx, v);
- } else {
- bf_t *a;
- val = JS_NewBigInt(ctx);
- if (JS_IsException(val))
- return val;
- a = JS_GetBigInt(val);
- if (bf_set_ui(a, v)) {
- JS_FreeValue(ctx, val);
- return JS_ThrowOutOfMemory(ctx);
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ ret = JS_VALUE_GET_INT(val);
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ JSFloat64Union u;
+ double d;
+ int e;
+ d = JS_VALUE_GET_FLOAT64(val);
+ u.d = d;
+ /* we avoid doing fmod(x, 2^64) */
+ e = (u.u64 >> 52) & 0x7ff;
+ if (likely(e <= (1023 + 62))) {
+ /* fast case */
+ ret = (int64_t)d;
+ } else if (e <= (1023 + 62 + 53)) {
+ uint64_t v;
+ /* remainder modulo 2^64 */
+ v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
+ ret = v << ((e - 1023) - 52);
+ /* take the sign into account */
+ if (u.u64 >> 63)
+ ret = -ret;
+ } else {
+ ret = 0; /* also handles NaN and +inf */
+ }
+ }
+ break;
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
}
+ goto redo;
}
- return val;
+ *pres = ret;
+ return 0;
}
-/* return NaN if bad bigint literal */
-static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
+int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
{
- const char *str, *p;
- size_t len;
- int flags;
-
- str = JS_ToCStringLen(ctx, &len, val);
- JS_FreeValue(ctx, val);
- if (!str)
- return JS_EXCEPTION;
- p = str;
- p += skip_spaces(p);
- if ((p - str) == len) {
- val = JS_NewBigInt64(ctx, 0);
- } else {
- flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
-#ifdef CONFIG_BIGNUM
- if (is_math_mode(ctx))
- flags |= ATOD_MODE_BIGINT;
-#endif
- val = js_atof(ctx, p, &p, 0, flags);
- p += skip_spaces(p);
- if (!JS_IsException(val)) {
- if ((p - str) != len) {
- JS_FreeValue(ctx, val);
- val = JS_NAN;
- }
- }
- }
- JS_FreeCString(ctx, str);
- return val;
+ return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));
}
-static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)
+int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)
{
- val = JS_StringToBigInt(ctx, val);
- if (JS_VALUE_IS_NAN(val))
- return JS_ThrowSyntaxError(ctx, "invalid bigint literal");
- return val;
+ if (JS_IsBigInt(ctx, val))
+ return JS_ToBigInt64(ctx, pres, val);
+ else
+ return JS_ToInt64(ctx, pres, val);
}
-/* if the returned bigfloat is allocated it is equal to
- 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */
-static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
+/* return (<0, 0) in case of exception */
+static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
{
uint32_t tag;
- bf_t *r;
- JSBigFloat *p;
+ int32_t ret;
redo:
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_INT:
+ case JS_TAG_BOOL:
case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
- if (!is_math_mode(ctx))
- goto fail;
- /* fall tru */
- case JS_TAG_BOOL:
- r = buf;
- bf_init(ctx->bf_ctx, r);
- bf_set_si(r, JS_VALUE_GET_INT(val));
+ ret = JS_VALUE_GET_INT(val);
break;
case JS_TAG_FLOAT64:
{
- double d = JS_VALUE_GET_FLOAT64(val);
- if (!is_math_mode(ctx))
- goto fail;
- if (!isfinite(d))
- goto fail;
- r = buf;
- bf_init(ctx->bf_ctx, r);
- d = trunc(d);
- bf_set_float64(r, d);
+ JSFloat64Union u;
+ double d;
+ int e;
+ d = JS_VALUE_GET_FLOAT64(val);
+ u.d = d;
+ /* we avoid doing fmod(x, 2^32) */
+ e = (u.u64 >> 52) & 0x7ff;
+ if (likely(e <= (1023 + 30))) {
+ /* fast case */
+ ret = (int32_t)d;
+ } else if (e <= (1023 + 30 + 53)) {
+ uint64_t v;
+ /* remainder modulo 2^32 */
+ v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
+ v = v << ((e - 1023) - 52 + 32);
+ ret = v >> 32;
+ /* take the sign into account */
+ if (u.u64 >> 63)
+ ret = -ret;
+ } else {
+ ret = 0; /* also handles NaN and +inf */
+ }
}
break;
- case JS_TAG_BIG_INT:
- p = JS_VALUE_GET_PTR(val);
- r = &p->num;
- break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- if (!is_math_mode(ctx))
- goto fail;
- p = JS_VALUE_GET_PTR(val);
- if (!bf_is_finite(&p->num))
- goto fail;
- r = buf;
- bf_init(ctx->bf_ctx, r);
- bf_set(r, &p->num);
- bf_rint(r, BF_RNDZ);
- JS_FreeValue(ctx, val);
- break;
-#endif
- case JS_TAG_STRING:
- val = JS_StringToBigIntErr(ctx, val);
- if (JS_IsException(val))
- return NULL;
- goto redo;
- case JS_TAG_OBJECT:
- val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
- if (JS_IsException(val))
- return NULL;
- goto redo;
default:
- fail:
- JS_FreeValue(ctx, val);
- JS_ThrowTypeError(ctx, "cannot convert to bigint");
- return NULL;
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
+ }
+ goto redo;
}
- return r;
+ *pres = ret;
+ return 0;
}
-static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val)
+int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)
{
- return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val));
+ return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));
}
-static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val)
+static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)
{
- if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) {
- return val;
- } else {
- bf_t a_s, *a, *r;
- int ret;
- JSValue res;
-
- res = JS_NewBigInt(ctx);
- if (JS_IsException(res))
- return JS_EXCEPTION;
- a = JS_ToBigIntFree(ctx, &a_s, val);
- if (!a) {
- JS_FreeValue(ctx, res);
- return JS_EXCEPTION;
- }
- r = JS_GetBigInt(res);
- ret = bf_set(r, a);
- JS_FreeBigInt(ctx, a, &a_s);
- if (ret) {
- JS_FreeValue(ctx, res);
- return JS_ThrowOutOfMemory(ctx);
- }
- return JS_CompactBigInt(ctx, res);
- }
+ return JS_ToInt32Free(ctx, (int32_t *)pres, val);
}
-/* free the bf_t allocated by JS_ToBigInt */
-static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf)
+static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
{
- if (a == buf) {
- bf_delete(a);
- } else {
- JSBigFloat *p = (JSBigFloat *)((uint8_t *)a -
- offsetof(JSBigFloat, num));
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_INT, p));
- }
-}
-
-/* XXX: merge with JS_ToInt64Free with a specific flag */
-static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
-{
- bf_t a_s, *a;
+ uint32_t tag;
+ int res;
- a = JS_ToBigIntFree(ctx, &a_s, val);
- if (!a) {
- *pres = 0;
- return -1;
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ res = JS_VALUE_GET_INT(val);
+ res = max_int(0, min_int(255, res));
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (isnan(d)) {
+ res = 0;
+ } else {
+ if (d < 0)
+ res = 0;
+ else if (d > 255)
+ res = 255;
+ else
+ res = lrint(d);
+ }
+ }
+ break;
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
+ }
+ goto redo;
}
- bf_get_int64(pres, a, BF_GET_INT_MOD);
- JS_FreeBigInt(ctx, a, &a_s);
+ *pres = res;
return 0;
}
-int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
+static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
+ JSValue val, BOOL is_array_ctor)
{
- return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));
-}
+ uint32_t tag, len;
-static JSBigFloat *js_new_bf(JSContext *ctx)
-{
- JSBigFloat *p;
- p = js_malloc(ctx, sizeof(*p));
- if (!p)
- return NULL;
- p->header.ref_count = 1;
- bf_init(ctx->bf_ctx, &p->num);
- return p;
+ tag = JS_VALUE_GET_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ {
+ int v;
+ v = JS_VALUE_GET_INT(val);
+ if (v < 0)
+ goto fail;
+ len = v;
+ }
+ break;
+ default:
+ if (JS_TAG_IS_FLOAT64(tag)) {
+ double d;
+ d = JS_VALUE_GET_FLOAT64(val);
+ if (!(d >= 0 && d <= UINT32_MAX))
+ goto fail;
+ len = (uint32_t)d;
+ if (len != d)
+ goto fail;
+ } else {
+ uint32_t len1;
+
+ if (is_array_ctor) {
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ return -1;
+ /* cannot recurse because val is a number */
+ if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))
+ return -1;
+ } else {
+ /* legacy behavior: must do the conversion twice and compare */
+ if (JS_ToUint32(ctx, &len, val)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ return -1;
+ /* cannot recurse because val is a number */
+ if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))
+ return -1;
+ if (len1 != len) {
+ fail:
+ JS_ThrowRangeError(ctx, "invalid array length");
+ return -1;
+ }
+ }
+ }
+ break;
+ }
+ *plen = len;
+ return 0;
}
-static JSValue JS_NewBigInt(JSContext *ctx)
+#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
+
+static BOOL is_safe_integer(double d)
{
- JSBigFloat *p;
- p = js_malloc(ctx, sizeof(*p));
- if (!p)
- return JS_EXCEPTION;
- p->header.ref_count = 1;
- bf_init(ctx->bf_ctx, &p->num);
- return JS_MKPTR(JS_TAG_BIG_INT, p);
+ return isfinite(d) && floor(d) == d &&
+ fabs(d) <= (double)MAX_SAFE_INTEGER;
}
-static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
- BOOL convert_to_safe_integer)
+int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
{
int64_t v;
- bf_t *a;
-
- if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT)
- return val; /* fail safe */
- a = JS_GetBigInt(val);
- if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 &&
- v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
- JS_FreeValue(ctx, val);
- return JS_NewInt64(ctx, v);
- } else if (a->expn == BF_EXP_ZERO && a->sign) {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- assert(p->header.ref_count == 1);
- a->sign = 0;
+ if (JS_ToInt64Sat(ctx, &v, val))
+ return -1;
+ if (v < 0 || v > MAX_SAFE_INTEGER) {
+ JS_ThrowRangeError(ctx, "invalid array index");
+ *plen = 0;
+ return -1;
}
- return val;
+ *plen = v;
+ return 0;
}
-/* Convert the big int to a safe integer if in math mode. normalize
- the zero representation. Could also be used to convert the bigint
- to a short bigint value. The reference count of the value must be
- 1. Cannot fail */
-static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val)
+/* convert a value to a length between 0 and MAX_SAFE_INTEGER.
+ return -1 for exception */
+static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
+ JSValue val)
{
- return JS_CompactBigInt1(ctx, val, is_math_mode(ctx));
+ int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);
+ JS_FreeValue(ctx, val);
+ return res;
}
-static JSValue throw_bf_exception(JSContext *ctx, int status)
+/* Note: can return an exception */
+static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)
{
- const char *str;
- if (status & BF_ST_MEM_ERROR)
- return JS_ThrowOutOfMemory(ctx);
- if (status & BF_ST_DIVIDE_ZERO) {
- str = "division by zero";
- } else if (status & BF_ST_INVALID_OP) {
- str = "invalid operation";
- } else {
- str = "integer overflow";
- }
- return JS_ThrowRangeError(ctx, "%s", str);
+ double d;
+ if (!JS_IsNumber(val))
+ return FALSE;
+ if (unlikely(JS_ToFloat64(ctx, &d, val)))
+ return -1;
+ return isfinite(d) && floor(d) == d;
}
-/* if the returned bigfloat is allocated it is equal to
- 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return
- NULL in case of error. */
-static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val)
+static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)
{
uint32_t tag;
- bf_t *r;
- JSBigFloat *p;
tag = JS_VALUE_GET_NORM_TAG(val);
switch(tag) {
case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- r = buf;
- bf_init(ctx->bf_ctx, r);
- if (bf_set_si(r, JS_VALUE_GET_INT(val)))
- goto fail;
- break;
+ {
+ int v;
+ v = JS_VALUE_GET_INT(val);
+ return (v < 0);
+ }
case JS_TAG_FLOAT64:
- r = buf;
- bf_init(ctx->bf_ctx, r);
- if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) {
- fail:
- bf_delete(r);
- return NULL;
+ {
+ JSFloat64Union u;
+ u.d = JS_VALUE_GET_FLOAT64(val);
+ return (u.u64 >> 63);
}
- break;
+ case JS_TAG_SHORT_BIG_INT:
+ return (JS_VALUE_GET_SHORT_BIG_INT(val) < 0);
case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
-#endif
- p = JS_VALUE_GET_PTR(val);
- r = &p->num;
- break;
- case JS_TAG_UNDEFINED:
+ {
+ JSBigInt *p = JS_VALUE_GET_PTR(val);
+ return js_bigint_sign(p);
+ }
default:
- r = buf;
- bf_init(ctx->bf_ctx, r);
- bf_set_nan(r);
- break;
+ return FALSE;
}
- return r;
}
-#ifdef CONFIG_BIGNUM
-/* return NULL if invalid type */
-static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val)
+static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)
{
- uint32_t tag;
- JSBigDecimal *p;
- bfdec_t *r;
-
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_BIG_DECIMAL:
- p = JS_VALUE_GET_PTR(val);
- r = &p->num;
- break;
- default:
- JS_ThrowTypeError(ctx, "bigdecimal expected");
- r = NULL;
- break;
- }
- return r;
+ return js_bigint_to_string1(ctx, val, 10);
}
-static JSValue JS_NewBigFloat(JSContext *ctx)
+/* buf1 contains the printf result */
+static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,
+ int rounding_mode, char *buf1, int buf1_size)
{
- JSBigFloat *p;
- p = js_malloc(ctx, sizeof(*p));
- if (!p)
- return JS_EXCEPTION;
- p->header.ref_count = 1;
- bf_init(ctx->bf_ctx, &p->num);
- return JS_MKPTR(JS_TAG_BIG_FLOAT, p);
+ if (rounding_mode != FE_TONEAREST)
+ fesetround(rounding_mode);
+ snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);
+ if (rounding_mode != FE_TONEAREST)
+ fesetround(FE_TONEAREST);
+ *sign = (buf1[0] == '-');
+ /* mantissa */
+ buf[0] = buf1[1];
+ if (n_digits > 1)
+ memcpy(buf + 1, buf1 + 3, n_digits - 1);
+ buf[n_digits] = '\0';
+ /* exponent */
+ *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;
}
-static JSValue JS_NewBigDecimal(JSContext *ctx)
-{
- JSBigDecimal *p;
- p = js_malloc(ctx, sizeof(*p));
- if (!p)
- return JS_EXCEPTION;
- p->header.ref_count = 1;
- bfdec_init(ctx->bf_ctx, &p->num);
- return JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
-}
-
-/* must be kept in sync with JSOverloadableOperatorEnum */
-/* XXX: use atoms ? */
-static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = {
- "+",
- "-",
- "*",
- "/",
- "%",
- "**",
- "|",
- "&",
- "^",
- "<<",
- ">>",
- ">>>",
- "==",
- "<",
- "pos",
- "neg",
- "++",
- "--",
- "~",
-};
+/* maximum buffer size for js_dtoa */
+#define JS_DTOA_BUF_SIZE 128
-static int get_ovop_from_opcode(OPCodeEnum op)
+/* needed because ecvt usually limits the number of digits to
+ 17. Return the number of digits. */
+static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,
+ BOOL is_fixed)
{
- switch(op) {
- case OP_add:
- return JS_OVOP_ADD;
- case OP_sub:
- return JS_OVOP_SUB;
- case OP_mul:
- return JS_OVOP_MUL;
- case OP_div:
- return JS_OVOP_DIV;
- case OP_mod:
- case OP_math_mod:
- return JS_OVOP_MOD;
- case OP_pow:
- return JS_OVOP_POW;
- case OP_or:
- return JS_OVOP_OR;
- case OP_and:
- return JS_OVOP_AND;
- case OP_xor:
- return JS_OVOP_XOR;
- case OP_shl:
- return JS_OVOP_SHL;
- case OP_sar:
- return JS_OVOP_SAR;
- case OP_shr:
- return JS_OVOP_SHR;
- case OP_eq:
- case OP_neq:
- return JS_OVOP_EQ;
- case OP_lt:
- case OP_lte:
- case OP_gt:
- case OP_gte:
- return JS_OVOP_LESS;
- case OP_plus:
- return JS_OVOP_POS;
- case OP_neg:
- return JS_OVOP_NEG;
- case OP_inc:
- return JS_OVOP_INC;
- case OP_dec:
- return JS_OVOP_DEC;
- default:
- abort();
+ int rounding_mode;
+ char buf_tmp[JS_DTOA_BUF_SIZE];
+
+ if (!is_fixed) {
+ unsigned int n_digits_min, n_digits_max;
+ /* find the minimum amount of digits (XXX: inefficient but simple) */
+ n_digits_min = 1;
+ n_digits_max = 17;
+ while (n_digits_min < n_digits_max) {
+ n_digits = (n_digits_min + n_digits_max) / 2;
+ js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,
+ buf_tmp, sizeof(buf_tmp));
+ if (strtod(buf_tmp, NULL) == d) {
+ /* no need to keep the trailing zeros */
+ while (n_digits >= 2 && buf[n_digits - 1] == '0')
+ n_digits--;
+ n_digits_max = n_digits;
+ } else {
+ n_digits_min = n_digits + 1;
+ }
+ }
+ n_digits = n_digits_max;
+ rounding_mode = FE_TONEAREST;
+ } else {
+ rounding_mode = FE_TONEAREST;
+#ifdef CONFIG_PRINTF_RNDN
+ {
+ char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];
+ int decpt1, sign1, decpt2, sign2;
+ /* The JS rounding is specified as round to nearest ties away
+ from zero (RNDNA), but in printf the "ties" case is not
+ specified (for example it is RNDN for glibc, RNDNA for
+ Windows), so we must round manually. */
+ js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,
+ buf_tmp, sizeof(buf_tmp));
+ /* XXX: could use 2 digits to reduce the average running time */
+ if (buf1[n_digits] == '5') {
+ js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,
+ buf_tmp, sizeof(buf_tmp));
+ js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,
+ buf_tmp, sizeof(buf_tmp));
+ if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {
+ /* exact result: round away from zero */
+ if (sign1)
+ rounding_mode = FE_DOWNWARD;
+ else
+ rounding_mode = FE_UPWARD;
+ }
+ }
+ }
+#endif /* CONFIG_PRINTF_RNDN */
}
+ js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,
+ buf_tmp, sizeof(buf_tmp));
+ return n_digits;
}
-/* return NULL if not present */
-static JSObject *find_binary_op(JSBinaryOperatorDef *def,
- uint32_t operator_index,
- JSOverloadableOperatorEnum op)
+static int js_fcvt1(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits,
+ int rounding_mode)
{
- JSBinaryOperatorDefEntry *ent;
- int i;
- for(i = 0; i < def->count; i++) {
- ent = &def->tab[i];
- if (ent->operator_index == operator_index)
- return ent->ops[op];
- }
- return NULL;
+ int n;
+ if (rounding_mode != FE_TONEAREST)
+ fesetround(rounding_mode);
+ n = snprintf(*buf, sizeof(*buf), "%.*f", n_digits, d);
+ if (rounding_mode != FE_TONEAREST)
+ fesetround(FE_TONEAREST);
+ assert(n < sizeof(*buf));
+ return n;
}
-/* return -1 if exception, 0 if no operator overloading, 1 if
- overloaded operator called */
-static __exception int js_call_binary_op_fallback(JSContext *ctx,
- JSValue *pret,
- JSValueConst op1,
- JSValueConst op2,
- OPCodeEnum op,
- BOOL is_numeric,
- int hint)
+static void js_fcvt(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits)
{
- JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2;
- JSOperatorSetData *opset1, *opset2;
- JSOverloadableOperatorEnum ovop;
- JSObject *p;
- JSValueConst args[2];
-
- if (!ctx->allow_operator_overloading)
- return 0;
-
- opset2_obj = JS_UNDEFINED;
- opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
- if (JS_IsException(opset1_obj))
- goto exception;
- if (JS_IsUndefined(opset1_obj))
- return 0;
- opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
- if (!opset1)
- goto exception;
-
- opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet);
- if (JS_IsException(opset2_obj))
- goto exception;
- if (JS_IsUndefined(opset2_obj)) {
- JS_FreeValue(ctx, opset1_obj);
- return 0;
- }
- opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET);
- if (!opset2)
- goto exception;
+ int rounding_mode;
+ rounding_mode = FE_TONEAREST;
+#ifdef CONFIG_PRINTF_RNDN
+ {
+ int n1, n2;
+ char buf1[JS_DTOA_BUF_SIZE];
+ char buf2[JS_DTOA_BUF_SIZE];
- if (opset1->is_primitive && opset2->is_primitive) {
- JS_FreeValue(ctx, opset1_obj);
- JS_FreeValue(ctx, opset2_obj);
- return 0;
+ /* The JS rounding is specified as round to nearest ties away from
+ zero (RNDNA), but in printf the "ties" case is not specified
+ (for example it is RNDN for glibc, RNDNA for Windows), so we
+ must round manually. */
+ n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_TONEAREST);
+ rounding_mode = FE_TONEAREST;
+ /* XXX: could use 2 digits to reduce the average running time */
+ if (buf1[n1 - 1] == '5') {
+ n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_DOWNWARD);
+ n2 = js_fcvt1(&buf2, d, n_digits + 1, FE_UPWARD);
+ if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {
+ /* exact result: round away from zero */
+ if (buf1[0] == '-')
+ rounding_mode = FE_DOWNWARD;
+ else
+ rounding_mode = FE_UPWARD;
+ }
+ }
}
+#endif /* CONFIG_PRINTF_RNDN */
+ js_fcvt1(buf, d, n_digits, rounding_mode);
+}
- ovop = get_ovop_from_opcode(op);
+/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */
+/* use as many digits as necessary */
+#define JS_DTOA_VAR_FORMAT (0 << 0)
+/* use n_digits significant digits (1 <= n_digits <= 101) */
+#define JS_DTOA_FIXED_FORMAT (1 << 0)
+/* force fractional format: [-]dd.dd with n_digits fractional digits */
+#define JS_DTOA_FRAC_FORMAT (2 << 0)
+/* force exponential notation either in fixed or variable format */
+#define JS_DTOA_FORCE_EXP (1 << 2)
- if (opset1->operator_counter == opset2->operator_counter) {
- p = opset1->self_ops[ovop];
- } else if (opset1->operator_counter > opset2->operator_counter) {
- p = find_binary_op(&opset1->left, opset2->operator_counter, ovop);
- } else {
- p = find_binary_op(&opset2->right, opset1->operator_counter, ovop);
- }
- if (!p) {
- JS_ThrowTypeError(ctx, "operator %s: no function defined",
- js_overloadable_operator_names[ovop]);
- goto exception;
- }
+/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.
+ XXX: radix != 10 is only supported for small integers
+*/
+static void js_dtoa1(char (*buf)[JS_DTOA_BUF_SIZE], double d,
+ int radix, int n_digits, int flags)
+{
+ char *q;
- if (opset1->is_primitive) {
- if (is_numeric) {
- new_op1 = JS_ToNumeric(ctx, op1);
+ if (!isfinite(d)) {
+ if (isnan(d)) {
+ pstrcpy(*buf, sizeof(*buf), "NaN");
+ } else if (d < 0) {
+ pstrcpy(*buf, sizeof(*buf), "-Infinity");
} else {
- new_op1 = JS_ToPrimitive(ctx, op1, hint);
+ pstrcpy(*buf, sizeof(*buf), "Infinity");
}
- if (JS_IsException(new_op1))
- goto exception;
+ } else if (flags == JS_DTOA_VAR_FORMAT) {
+ int64_t i64;
+ char buf1[70], *ptr;
+ if (d > (double)MAX_SAFE_INTEGER || d < (double)-MAX_SAFE_INTEGER)
+ goto generic_conv;
+ i64 = (int64_t)d;
+ if (d != i64)
+ goto generic_conv;
+ /* fast path for integers */
+ ptr = i64toa(buf1 + sizeof(buf1), i64, radix);
+ pstrcpy(*buf, sizeof(*buf), ptr);
} else {
- new_op1 = JS_DupValue(ctx, op1);
- }
-
- if (opset2->is_primitive) {
- if (is_numeric) {
- new_op2 = JS_ToNumeric(ctx, op2);
+ if (d == 0.0)
+ d = 0.0; /* convert -0 to 0 */
+ if (flags == JS_DTOA_FRAC_FORMAT) {
+ js_fcvt(buf, d, n_digits);
} else {
- new_op2 = JS_ToPrimitive(ctx, op2, hint);
- }
- if (JS_IsException(new_op2)) {
- JS_FreeValue(ctx, new_op1);
- goto exception;
+ char buf1[JS_DTOA_BUF_SIZE];
+ int sign, decpt, k, n, i, p, n_max;
+ BOOL is_fixed;
+ generic_conv:
+ is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);
+ if (is_fixed) {
+ n_max = n_digits;
+ } else {
+ n_max = 21;
+ }
+ /* the number has k digits (k >= 1) */
+ k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);
+ n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */
+ q = *buf;
+ if (sign)
+ *q++ = '-';
+ if (flags & JS_DTOA_FORCE_EXP)
+ goto force_exp;
+ if (n >= 1 && n <= n_max) {
+ if (k <= n) {
+ memcpy(q, buf1, k);
+ q += k;
+ for(i = 0; i < (n - k); i++)
+ *q++ = '0';
+ *q = '\0';
+ } else {
+ /* k > n */
+ memcpy(q, buf1, n);
+ q += n;
+ *q++ = '.';
+ for(i = 0; i < (k - n); i++)
+ *q++ = buf1[n + i];
+ *q = '\0';
+ }
+ } else if (n >= -5 && n <= 0) {
+ *q++ = '0';
+ *q++ = '.';
+ for(i = 0; i < -n; i++)
+ *q++ = '0';
+ memcpy(q, buf1, k);
+ q += k;
+ *q = '\0';
+ } else {
+ force_exp:
+ /* exponential notation */
+ *q++ = buf1[0];
+ if (k > 1) {
+ *q++ = '.';
+ for(i = 1; i < k; i++)
+ *q++ = buf1[i];
+ }
+ *q++ = 'e';
+ p = n - 1;
+ if (p >= 0)
+ *q++ = '+';
+ snprintf(q, *buf + sizeof(*buf) - q, "%d", p);
+ }
}
- } else {
- new_op2 = JS_DupValue(ctx, op2);
}
+}
- /* XXX: could apply JS_ToPrimitive() if primitive type so that the
- operator function does not get a value object */
+static JSValue js_dtoa(JSContext *ctx,
+ double d, int radix, int n_digits, int flags)
+{
+ char buf[JS_DTOA_BUF_SIZE];
+ js_dtoa1(&buf, d, radix, n_digits, flags);
+ return JS_NewString(ctx, buf);
+}
- method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
- if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) {
- args[0] = new_op2;
- args[1] = new_op1;
+static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix)
+{
+ char buf[2200], *ptr, *ptr2;
+ /* d is finite */
+ int sign = d < 0;
+ int digit;
+ double frac, d0;
+ int64_t n0 = 0;
+ d = fabs(d);
+ d0 = trunc(d);
+ frac = d - d0;
+ ptr = buf + 1100;
+ *ptr = '\0';
+ if (d0 <= MAX_SAFE_INTEGER) {
+ int64_t n = n0 = (int64_t)d0;
+ while (n >= radix) {
+ digit = n % radix;
+ n = n / radix;
+ *--ptr = digits[digit];
+ }
+ *--ptr = digits[(int)n];
} else {
- args[0] = new_op1;
- args[1] = new_op2;
+ /* no decimals */
+ while (d0 >= radix) {
+ digit = fmod(d0, radix);
+ d0 = trunc(d0 / radix);
+ if (d0 >= MAX_SAFE_INTEGER)
+ digit = 0;
+ *--ptr = digits[digit];
+ }
+ *--ptr = digits[(int)d0];
+ goto done;
}
- ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
- JS_FreeValue(ctx, new_op1);
- JS_FreeValue(ctx, new_op2);
- if (JS_IsException(ret))
- goto exception;
- if (ovop == JS_OVOP_EQ) {
- BOOL res = JS_ToBoolFree(ctx, ret);
- if (op == OP_neq)
- res ^= 1;
- ret = JS_NewBool(ctx, res);
- } else if (ovop == JS_OVOP_LESS) {
- if (JS_IsUndefined(ret)) {
- ret = JS_FALSE;
+ if (frac != 0) {
+ double log2_radix = log2(radix);
+ double prec = 1023 + 51; // handle subnormals
+ ptr2 = buf + 1100;
+ *ptr2++ = '.';
+ while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) {
+ frac *= radix;
+ digit = trunc(frac);
+ frac -= digit;
+ *ptr2++ = digits[digit];
+ n0 = n0 * radix + digit;
+ prec -= log2_radix;
+ }
+ *ptr2 = '\0';
+ if (frac * radix >= radix / 2) {
+ char nine = digits[radix - 1];
+ // round to closest
+ while (ptr2[-1] == nine)
+ *--ptr2 = '\0';
+ if (ptr2[-1] == '.') {
+ *--ptr2 = '\0';
+ while (ptr2[-1] == nine)
+ *--ptr2 = '0';
+ }
+ if (ptr2 - 1 == ptr)
+ *--ptr = '1';
+ else
+ ptr2[-1] += 1;
} else {
- BOOL res = JS_ToBoolFree(ctx, ret);
- if (op == OP_lte || op == OP_gte)
- res ^= 1;
- ret = JS_NewBool(ctx, res);
+ while (ptr2[-1] == '0')
+ *--ptr2 = '\0';
+ if (ptr2[-1] == '.')
+ *--ptr2 = '\0';
}
}
- JS_FreeValue(ctx, opset1_obj);
- JS_FreeValue(ctx, opset2_obj);
- *pret = ret;
- return 1;
- exception:
- JS_FreeValue(ctx, opset1_obj);
- JS_FreeValue(ctx, opset2_obj);
- *pret = JS_UNDEFINED;
- return -1;
+done:
+ ptr[-1] = '-';
+ ptr -= sign;
+ return JS_NewString(ctx, ptr);
}
-/* try to call the operation on the operatorSet field of 'obj'. Only
- used for "/" and "**" on the BigInt prototype in math mode */
-static __exception int js_call_binary_op_simple(JSContext *ctx,
- JSValue *pret,
- JSValueConst obj,
- JSValueConst op1,
- JSValueConst op2,
- OPCodeEnum op)
+JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)
{
- JSValue opset1_obj, method, ret, new_op1, new_op2;
- JSOperatorSetData *opset1;
- JSOverloadableOperatorEnum ovop;
- JSObject *p;
- JSValueConst args[2];
-
- opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
- if (JS_IsException(opset1_obj))
- goto exception;
- if (JS_IsUndefined(opset1_obj))
- return 0;
- opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
- if (!opset1)
- goto exception;
- ovop = get_ovop_from_opcode(op);
-
- p = opset1->self_ops[ovop];
- if (!p) {
- JS_FreeValue(ctx, opset1_obj);
- return 0;
- }
+ uint32_t tag;
+ const char *str;
+ char buf[32];
- new_op1 = JS_ToNumeric(ctx, op1);
- if (JS_IsException(new_op1))
- goto exception;
- new_op2 = JS_ToNumeric(ctx, op2);
- if (JS_IsException(new_op2)) {
- JS_FreeValue(ctx, new_op1);
- goto exception;
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_STRING:
+ return JS_DupValue(ctx, val);
+ case JS_TAG_INT:
+ snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));
+ str = buf;
+ goto new_string;
+ case JS_TAG_BOOL:
+ return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
+ JS_ATOM_true : JS_ATOM_false);
+ case JS_TAG_NULL:
+ return JS_AtomToString(ctx, JS_ATOM_null);
+ case JS_TAG_UNDEFINED:
+ return JS_AtomToString(ctx, JS_ATOM_undefined);
+ case JS_TAG_EXCEPTION:
+ return JS_EXCEPTION;
+ case JS_TAG_OBJECT:
+ {
+ JSValue val1, ret;
+ val1 = JS_ToPrimitive(ctx, val, HINT_STRING);
+ if (JS_IsException(val1))
+ return val1;
+ ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);
+ JS_FreeValue(ctx, val1);
+ return ret;
+ }
+ break;
+ case JS_TAG_FUNCTION_BYTECODE:
+ str = "[function bytecode]";
+ goto new_string;
+ case JS_TAG_SYMBOL:
+ if (is_ToPropertyKey) {
+ return JS_DupValue(ctx, val);
+ } else {
+ return JS_ThrowTypeError(ctx, "cannot convert symbol to string");
+ }
+ case JS_TAG_FLOAT64:
+ return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
+ JS_DTOA_VAR_FORMAT);
+ case JS_TAG_SHORT_BIG_INT:
+ case JS_TAG_BIG_INT:
+ return js_bigint_to_string(ctx, val);
+ default:
+ str = "[unsupported type]";
+ new_string:
+ return JS_NewString(ctx, str);
}
-
- method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
- args[0] = new_op1;
- args[1] = new_op2;
- ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
- JS_FreeValue(ctx, new_op1);
- JS_FreeValue(ctx, new_op2);
- if (JS_IsException(ret))
- goto exception;
- JS_FreeValue(ctx, opset1_obj);
- *pret = ret;
- return 1;
- exception:
- JS_FreeValue(ctx, opset1_obj);
- *pret = JS_UNDEFINED;
- return -1;
}
-/* return -1 if exception, 0 if no operator overloading, 1 if
- overloaded operator called */
-static __exception int js_call_unary_op_fallback(JSContext *ctx,
- JSValue *pret,
- JSValueConst op1,
- OPCodeEnum op)
+JSValue JS_ToString(JSContext *ctx, JSValueConst val)
{
- JSValue opset1_obj, method, ret;
- JSOperatorSetData *opset1;
- JSOverloadableOperatorEnum ovop;
- JSObject *p;
-
- if (!ctx->allow_operator_overloading)
- return 0;
-
- opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
- if (JS_IsException(opset1_obj))
- goto exception;
- if (JS_IsUndefined(opset1_obj))
- return 0;
- opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
- if (!opset1)
- goto exception;
- if (opset1->is_primitive) {
- JS_FreeValue(ctx, opset1_obj);
- return 0;
- }
-
- ovop = get_ovop_from_opcode(op);
+ return JS_ToStringInternal(ctx, val, FALSE);
+}
- p = opset1->self_ops[ovop];
- if (!p) {
- JS_ThrowTypeError(ctx, "no overloaded operator %s",
- js_overloadable_operator_names[ovop]);
- goto exception;
- }
- method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
- ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1);
- if (JS_IsException(ret))
- goto exception;
- JS_FreeValue(ctx, opset1_obj);
- *pret = ret;
- return 1;
- exception:
- JS_FreeValue(ctx, opset1_obj);
- *pret = JS_UNDEFINED;
- return -1;
+static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)
+{
+ JSValue ret;
+ ret = JS_ToString(ctx, val);
+ JS_FreeValue(ctx, val);
+ return ret;
}
-static int js_unary_arith_bigfloat(JSContext *ctx,
- JSValue *pres, OPCodeEnum op, JSValue op1)
+static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)
{
- bf_t a_s, *r, *a;
- int ret, v;
- JSValue res;
+ if (JS_IsUndefined(val) || JS_IsNull(val))
+ return JS_ToStringFree(ctx, val);
+ return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);
+}
- if (op == OP_plus && !is_math_mode(ctx)) {
- JS_ThrowTypeError(ctx, "bigfloat argument with unary +");
- JS_FreeValue(ctx, op1);
- return -1;
- }
+JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
+{
+ return JS_ToStringInternal(ctx, val, TRUE);
+}
- res = JS_NewBigFloat(ctx);
- if (JS_IsException(res)) {
- JS_FreeValue(ctx, op1);
- return -1;
- }
- r = JS_GetBigFloat(res);
- a = JS_ToBigFloat(ctx, &a_s, op1);
- if (!a) {
- JS_FreeValue(ctx, res);
- JS_FreeValue(ctx, op1);
- return -1;
- }
- ret = 0;
- switch(op) {
- case OP_inc:
- case OP_dec:
- v = 2 * (op - OP_dec) - 1;
- ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags);
- break;
- case OP_plus:
- ret = bf_set(r, a);
- break;
- case OP_neg:
- ret = bf_set(r, a);
- bf_neg(r);
- break;
- default:
- abort();
- }
- if (a == &a_s)
- bf_delete(a);
- JS_FreeValue(ctx, op1);
- if (unlikely(ret & BF_ST_MEM_ERROR)) {
- JS_FreeValue(ctx, res);
- throw_bf_exception(ctx, ret);
- return -1;
- }
- *pres = res;
- return 0;
+static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)
+{
+ uint32_t tag = JS_VALUE_GET_TAG(val);
+ if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
+ return JS_ThrowTypeError(ctx, "null or undefined are forbidden");
+ return JS_ToString(ctx, val);
}
-static int js_unary_arith_bigdecimal(JSContext *ctx,
- JSValue *pres, OPCodeEnum op, JSValue op1)
+static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
{
- bfdec_t *r, *a;
- int ret, v;
- JSValue res;
+ JSValue val;
+ JSString *p;
+ int i;
+ uint32_t c;
+ StringBuffer b_s, *b = &b_s;
+ char buf[16];
- if (op == OP_plus && !is_math_mode(ctx)) {
- JS_ThrowTypeError(ctx, "bigdecimal argument with unary +");
- JS_FreeValue(ctx, op1);
- return -1;
- }
+ val = JS_ToStringCheckObject(ctx, val1);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_STRING(val);
- res = JS_NewBigDecimal(ctx);
- if (JS_IsException(res)) {
- JS_FreeValue(ctx, op1);
- return -1;
- }
- r = JS_GetBigDecimal(res);
- a = JS_ToBigDecimal(ctx, op1);
- if (!a) {
- JS_FreeValue(ctx, res);
- JS_FreeValue(ctx, op1);
- return -1;
- }
- ret = 0;
- switch(op) {
- case OP_inc:
- case OP_dec:
- v = 2 * (op - OP_dec) - 1;
- ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
- break;
- case OP_plus:
- ret = bfdec_set(r, a);
- break;
- case OP_neg:
- ret = bfdec_set(r, a);
- bfdec_neg(r);
- break;
- default:
- abort();
- }
- JS_FreeValue(ctx, op1);
- if (unlikely(ret)) {
- JS_FreeValue(ctx, res);
- throw_bf_exception(ctx, ret);
- return -1;
+ if (string_buffer_init(ctx, b, p->len + 2))
+ goto fail;
+
+ if (string_buffer_putc8(b, '\"'))
+ goto fail;
+ for(i = 0; i < p->len; ) {
+ c = string_getc(p, &i);
+ switch(c) {
+ case '\t':
+ c = 't';
+ goto quote;
+ case '\r':
+ c = 'r';
+ goto quote;
+ case '\n':
+ c = 'n';
+ goto quote;
+ case '\b':
+ c = 'b';
+ goto quote;
+ case '\f':
+ c = 'f';
+ goto quote;
+ case '\"':
+ case '\\':
+ quote:
+ if (string_buffer_putc8(b, '\\'))
+ goto fail;
+ if (string_buffer_putc8(b, c))
+ goto fail;
+ break;
+ default:
+ if (c < 32 || is_surrogate(c)) {
+ snprintf(buf, sizeof(buf), "\\u%04x", c);
+ if (string_buffer_puts8(b, buf))
+ goto fail;
+ } else {
+ if (string_buffer_putc(b, c))
+ goto fail;
+ }
+ break;
+ }
}
- *pres = res;
- return 0;
+ if (string_buffer_putc8(b, '\"'))
+ goto fail;
+ JS_FreeValue(ctx, val);
+ return string_buffer_end(b);
+ fail:
+ JS_FreeValue(ctx, val);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
}
-#endif /* CONFIG_BIGNUM */
-
-static int js_unary_arith_bigint(JSContext *ctx,
- JSValue *pres, OPCodeEnum op, JSValue op1)
+static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
{
- bf_t a_s, *r, *a;
- int ret, v;
- JSValue res;
-
- if (op == OP_plus && !is_math_mode(ctx)) {
- JS_ThrowTypeError(ctx, "bigint argument with unary +");
- JS_FreeValue(ctx, op1);
- return -1;
- }
- res = JS_NewBigInt(ctx);
- if (JS_IsException(res)) {
- JS_FreeValue(ctx, op1);
- return -1;
- }
- r = JS_GetBigInt(res);
- a = JS_ToBigInt(ctx, &a_s, op1);
- if (!a) {
- JS_FreeValue(ctx, res);
- JS_FreeValue(ctx, op1);
- return -1;
- }
- ret = 0;
- switch(op) {
- case OP_inc:
- case OP_dec:
- v = 2 * (op - OP_dec) - 1;
- ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
- break;
- case OP_plus:
- ret = bf_set(r, a);
- break;
- case OP_neg:
- ret = bf_set(r, a);
- bf_neg(r);
- break;
- case OP_not:
- ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ);
- bf_neg(r);
- break;
- default:
- abort();
- }
- JS_FreeBigInt(ctx, a, &a_s);
- JS_FreeValue(ctx, op1);
- if (unlikely(ret)) {
- JS_FreeValue(ctx, res);
- throw_bf_exception(ctx, ret);
- return -1;
- }
- res = JS_CompactBigInt(ctx, res);
- *pres = res;
- return 0;
+ printf("%14s %4s %4s %14s %10s %s\n",
+ "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
}
-static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
- JSValue *sp,
- OPCodeEnum op)
+/* for debug only: dump an object without side effect */
+static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
{
- JSValue op1;
- int v;
- uint32_t tag;
+ uint32_t i;
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ JSShape *sh;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ BOOL is_first = TRUE;
- op1 = sp[-1];
- /* fast path for float64 */
- if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))
- goto handle_float64;
-#ifdef CONFIG_BIGNUM
- if (JS_IsObject(op1)) {
- JSValue val;
- int ret = js_call_unary_op_fallback(ctx, &val, op1, op);
- if (ret < 0)
- return -1;
- if (ret) {
- JS_FreeValue(ctx, op1);
- sp[-1] = val;
- return 0;
- }
+ /* XXX: should encode atoms with special characters */
+ sh = p->shape; /* the shape can be NULL while freeing an object */
+ printf("%14p %4d ",
+ (void *)p,
+ p->header.ref_count);
+ if (sh) {
+ printf("%3d%c %14p ",
+ sh->header.ref_count,
+ " *"[sh->is_hashed],
+ (void *)sh->proto);
+ } else {
+ printf("%3s %14s ", "-", "-");
}
-#endif
- op1 = JS_ToNumericFree(ctx, op1);
- if (JS_IsException(op1))
- goto exception;
- tag = JS_VALUE_GET_TAG(op1);
- switch(tag) {
- case JS_TAG_INT:
- {
- int64_t v64;
- v64 = JS_VALUE_GET_INT(op1);
- switch(op) {
- case OP_inc:
- case OP_dec:
- v = 2 * (op - OP_dec) - 1;
- v64 += v;
- break;
- case OP_plus:
+ printf("%10s ",
+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));
+ if (p->is_exotic && p->fast_array) {
+ printf("[ ");
+ for(i = 0; i < p->u.array.count; i++) {
+ if (i != 0)
+ printf(", ");
+ switch (p->class_id) {
+ case JS_CLASS_ARRAY:
+ case JS_CLASS_ARGUMENTS:
+ JS_DumpValueShort(rt, p->u.array.u.values[i]);
break;
- case OP_neg:
- if (v64 == 0) {
- sp[-1] = __JS_NewFloat64(ctx, -0.0);
- return 0;
- } else {
- v64 = -v64;
+ case JS_CLASS_UINT8C_ARRAY:
+ case JS_CLASS_INT8_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ case JS_CLASS_INT16_ARRAY:
+ case JS_CLASS_UINT16_ARRAY:
+ case JS_CLASS_INT32_ARRAY:
+ case JS_CLASS_UINT32_ARRAY:
+ case JS_CLASS_BIG_INT64_ARRAY:
+ case JS_CLASS_BIG_UINT64_ARRAY:
+ case JS_CLASS_FLOAT32_ARRAY:
+ case JS_CLASS_FLOAT64_ARRAY:
+ {
+ int size = 1 << typed_array_size_log2(p->class_id);
+ const uint8_t *b = p->u.array.u.uint8_ptr + i * size;
+ while (size-- > 0)
+ printf("%02X", *b++);
}
break;
- default:
- abort();
}
- sp[-1] = JS_NewInt64(ctx, v64);
}
- break;
- case JS_TAG_BIG_INT:
- handle_bigint:
- if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1))
- goto exception;
- break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1))
- goto exception;
- break;
- case JS_TAG_BIG_DECIMAL:
- if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1))
- goto exception;
- break;
-#endif
- default:
- handle_float64:
- {
- double d;
- if (is_math_mode(ctx))
- goto handle_bigint;
- d = JS_VALUE_GET_FLOAT64(op1);
- switch(op) {
- case OP_inc:
- case OP_dec:
- v = 2 * (op - OP_dec) - 1;
- d += v;
- break;
- case OP_plus:
- break;
- case OP_neg:
- d = -d;
- break;
- default:
- abort();
+ printf(" ] ");
+ }
+
+ if (sh) {
+ printf("{ ");
+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
+ if (prs->atom != JS_ATOM_NULL) {
+ pr = &p->prop[i];
+ if (!is_first)
+ printf(", ");
+ printf("%s: ",
+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ printf("[getset %p %p]", (void *)pr->u.getset.getter,
+ (void *)pr->u.getset.setter);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ printf("[varref %p]", (void *)pr->u.var_ref);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ printf("[autoinit %p %d %p]",
+ (void *)js_autoinit_get_realm(pr),
+ js_autoinit_get_id(pr),
+ (void *)pr->u.init.opaque);
+ } else {
+ JS_DumpValueShort(rt, pr->u.value);
+ }
+ is_first = FALSE;
}
- sp[-1] = __JS_NewFloat64(ctx, d);
}
- break;
+ printf(" }");
}
- return 0;
- exception:
- sp[-1] = JS_UNDEFINED;
- return -1;
-}
-
-static __exception int js_post_inc_slow(JSContext *ctx,
- JSValue *sp, OPCodeEnum op)
-{
- JSValue op1;
- /* XXX: allow custom operators */
- op1 = sp[-1];
- op1 = JS_ToNumericFree(ctx, op1);
- if (JS_IsException(op1)) {
- sp[-1] = JS_UNDEFINED;
- return -1;
+ if (js_class_has_bytecode(p->class_id)) {
+ JSFunctionBytecode *b = p->u.func.function_bytecode;
+ JSVarRef **var_refs;
+ if (b->closure_var_count) {
+ var_refs = p->u.func.var_refs;
+ printf(" Closure:");
+ for(i = 0; i < b->closure_var_count; i++) {
+ printf(" ");
+ JS_DumpValueShort(rt, var_refs[i]->value);
+ }
+ if (p->u.func.home_object) {
+ printf(" HomeObject: ");
+ JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
+ }
+ }
}
- sp[-1] = op1;
- sp[0] = JS_DupValue(ctx, op1);
- return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);
+ printf("\n");
}
-static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
+static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
{
- JSValue op1;
-
- op1 = sp[-1];
-#ifdef CONFIG_BIGNUM
- if (JS_IsObject(op1)) {
- JSValue val;
- int ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not);
- if (ret < 0)
- return -1;
- if (ret) {
- JS_FreeValue(ctx, op1);
- sp[-1] = val;
- return 0;
- }
- }
-#endif
- op1 = JS_ToNumericFree(ctx, op1);
- if (JS_IsException(op1))
- goto exception;
- if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
- if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1))
- goto exception;
+ if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
+ JS_DumpObject(rt, (JSObject *)p);
} else {
- int32_t v1;
- if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
- goto exception;
- sp[-1] = JS_NewInt32(ctx, ~v1);
+ printf("%14p %4d ",
+ (void *)p,
+ p->ref_count);
+ switch(p->gc_obj_type) {
+ case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
+ printf("[function bytecode]");
+ break;
+ case JS_GC_OBJ_TYPE_SHAPE:
+ printf("[shape]");
+ break;
+ case JS_GC_OBJ_TYPE_VAR_REF:
+ printf("[var_ref]");
+ break;
+ case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
+ printf("[async_function]");
+ break;
+ case JS_GC_OBJ_TYPE_JS_CONTEXT:
+ printf("[js_context]");
+ break;
+ default:
+ printf("[unknown %d]", p->gc_obj_type);
+ break;
+ }
+ printf("\n");
}
- return 0;
- exception:
- sp[-1] = JS_UNDEFINED;
- return -1;
}
-static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,
- JSValue *pres, JSValue op1, JSValue op2)
+static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
+ JSValueConst val)
{
- bf_t a_s, b_s, *r, *a, *b;
- int ret;
- JSValue res;
+ uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
+ const char *str;
- res = JS_NewBigInt(ctx);
- if (JS_IsException(res))
- goto fail;
- a = JS_ToBigInt(ctx, &a_s, op1);
- if (!a)
- goto fail;
- b = JS_ToBigInt(ctx, &b_s, op2);
- if (!b) {
- JS_FreeBigInt(ctx, a, &a_s);
- goto fail;
- }
- r = JS_GetBigInt(res);
- ret = 0;
- switch(op) {
- case OP_add:
- ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ);
- break;
- case OP_sub:
- ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
- break;
- case OP_mul:
- ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
- break;
- case OP_div:
- if (!is_math_mode(ctx)) {
- bf_t rem_s, *rem = &rem_s;
- bf_init(ctx->bf_ctx, rem);
- ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ,
- BF_RNDZ);
- bf_delete(rem);
- } else {
- goto math_mode_div_pow;
- }
- break;
-#ifdef CONFIG_BIGNUM
- case OP_math_mod:
- /* Euclidian remainder */
- ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
- BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP;
+ switch(tag) {
+ case JS_TAG_INT:
+ printf("%d", JS_VALUE_GET_INT(val));
+ break;
+ case JS_TAG_BOOL:
+ if (JS_VALUE_GET_BOOL(val))
+ str = "true";
+ else
+ str = "false";
+ goto print_str;
+ case JS_TAG_NULL:
+ str = "null";
+ goto print_str;
+ case JS_TAG_EXCEPTION:
+ str = "exception";
+ goto print_str;
+ case JS_TAG_UNINITIALIZED:
+ str = "uninitialized";
+ goto print_str;
+ case JS_TAG_UNDEFINED:
+ str = "undefined";
+ print_str:
+ printf("%s", str);
+ break;
+ case JS_TAG_FLOAT64:
+ printf("%.14g", JS_VALUE_GET_FLOAT64(val));
+ break;
+#if 0
+ /* XXX: TODO */
+ case JS_TAG_BIG_INT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ char *str;
+ str = bf_ftoa(NULL, &p->num, 10, 0,
+ BF_RNDZ | BF_FTOA_FORMAT_FRAC);
+ printf("%sn", str);
+ bf_realloc(&rt->bf_ctx, str, 0);
+ }
break;
#endif
- case OP_mod:
- ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
- BF_RNDZ) & BF_ST_INVALID_OP;
- break;
- case OP_pow:
- if (b->sign) {
- if (!is_math_mode(ctx)) {
- ret = BF_ST_INVALID_OP;
- } else {
- math_mode_div_pow:
-#ifdef CONFIG_BIGNUM
- JS_FreeValue(ctx, res);
- ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op);
- if (ret != 0) {
- JS_FreeBigInt(ctx, a, &a_s);
- JS_FreeBigInt(ctx, b, &b_s);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (ret < 0) {
- return -1;
- } else {
- *pres = res;
- return 0;
- }
- }
- /* if no BigInt power operator defined, return a
- bigfloat */
- res = JS_NewBigFloat(ctx);
- if (JS_IsException(res)) {
- JS_FreeBigInt(ctx, a, &a_s);
- JS_FreeBigInt(ctx, b, &b_s);
- goto fail;
- }
- r = JS_GetBigFloat(res);
- if (op == OP_div) {
- ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR;
- } else {
- ret = bf_pow(r, a, b, ctx->fp_env.prec,
- ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR;
- }
- JS_FreeBigInt(ctx, a, &a_s);
- JS_FreeBigInt(ctx, b, &b_s);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (unlikely(ret)) {
- JS_FreeValue(ctx, res);
- throw_bf_exception(ctx, ret);
- return -1;
- }
- *pres = res;
- return 0;
-#else
- abort();
-#endif
- }
- } else {
- ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS);
+ case JS_TAG_STRING:
+ {
+ JSString *p;
+ p = JS_VALUE_GET_STRING(val);
+ JS_DumpString(rt, p);
}
break;
-
- /* logical operations */
- case OP_shl:
- case OP_sar:
+ case JS_TAG_FUNCTION_BYTECODE:
{
- slimb_t v2;
-#if LIMB_BITS == 32
- bf_get_int32(&v2, b, 0);
- if (v2 == INT32_MIN)
- v2 = INT32_MIN + 1;
-#else
- bf_get_int64(&v2, b, 0);
- if (v2 == INT64_MIN)
- v2 = INT64_MIN + 1;
-#endif
- if (op == OP_sar)
- v2 = -v2;
- ret = bf_set(r, a);
- ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ);
- if (v2 < 0) {
- ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR);
- }
+ JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
}
break;
- case OP_and:
- ret = bf_logic_and(r, a, b);
+ case JS_TAG_OBJECT:
+ {
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSAtom atom = rt->class_array[p->class_id].class_name;
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ printf("[%s %p]",
+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);
+ }
break;
- case OP_or:
- ret = bf_logic_or(r, a, b);
+ case JS_TAG_SYMBOL:
+ {
+ JSAtomStruct *p = JS_VALUE_GET_PTR(val);
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ printf("Symbol(%s)",
+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));
+ }
break;
- case OP_xor:
- ret = bf_logic_xor(r, a, b);
+ case JS_TAG_MODULE:
+ printf("[module]");
break;
default:
- abort();
+ printf("[unknown tag %d]", tag);
+ break;
}
- JS_FreeBigInt(ctx, a, &a_s);
- JS_FreeBigInt(ctx, b, &b_s);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (unlikely(ret)) {
- JS_FreeValue(ctx, res);
- throw_bf_exception(ctx, ret);
+}
+
+static __maybe_unused void JS_DumpValue(JSContext *ctx,
+ JSValueConst val)
+{
+ JS_DumpValueShort(ctx->rt, val);
+}
+
+static __maybe_unused void JS_PrintValue(JSContext *ctx,
+ const char *str,
+ JSValueConst val)
+{
+ printf("%s=", str);
+ JS_DumpValueShort(ctx->rt, val);
+ printf("\n");
+}
+
+/* return -1 if exception (proxy case) or TRUE/FALSE */
+// TODO: should take flags to make proxy resolution and exceptions optional
+int JS_IsArray(JSContext *ctx, JSValueConst val)
+{
+ if (js_resolve_proxy(ctx, &val, TRUE))
return -1;
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ return p->class_id == JS_CLASS_ARRAY;
+ } else {
+ return FALSE;
}
- *pres = JS_CompactBigInt(ctx, res);
- return 0;
- fail:
- JS_FreeValue(ctx, res);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- return -1;
}
-#ifdef CONFIG_BIGNUM
-static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,
- JSValue *pres, JSValue op1, JSValue op2)
+static double js_pow(double a, double b)
{
- bf_t a_s, b_s, *r, *a, *b;
- int ret;
- JSValue res;
+ if (unlikely(!isfinite(b)) && fabs(a) == 1) {
+ /* not compatible with IEEE 754 */
+ return JS_FLOAT64_NAN;
+ } else {
+ return pow(a, b);
+ }
+}
- res = JS_NewBigFloat(ctx);
- if (JS_IsException(res))
- goto fail;
- r = JS_GetBigFloat(res);
- a = JS_ToBigFloat(ctx, &a_s, op1);
- if (!a) {
- JS_FreeValue(ctx, res);
- goto fail;
+JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
+{
+#if JS_SHORT_BIG_INT_BITS == 64
+ return __JS_NewShortBigInt(ctx, v);
+#else
+ if (v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX) {
+ return __JS_NewShortBigInt(ctx, v);
+ } else {
+ JSBigInt *p;
+ p = js_bigint_new_si64(ctx, v);
+ if (!p)
+ return JS_EXCEPTION;
+ return JS_MKPTR(JS_TAG_BIG_INT, p);
}
- b = JS_ToBigFloat(ctx, &b_s, op2);
- if (!b) {
- if (a == &a_s)
- bf_delete(a);
- JS_FreeValue(ctx, res);
- goto fail;
+#endif
+}
+
+JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
+{
+ if (v <= JS_SHORT_BIG_INT_MAX) {
+ return __JS_NewShortBigInt(ctx, v);
+ } else {
+ JSBigInt *p;
+ p = js_bigint_new_ui64(ctx, v);
+ if (!p)
+ return JS_EXCEPTION;
+ return JS_MKPTR(JS_TAG_BIG_INT, p);
}
- bf_init(ctx->bf_ctx, r);
- switch(op) {
- case OP_add:
- ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
- break;
- case OP_sub:
- ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
- break;
- case OP_mul:
- ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
- break;
- case OP_div:
- ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
- break;
- case OP_math_mod:
- /* Euclidian remainder */
- ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
- BF_DIVREM_EUCLIDIAN);
- break;
- case OP_mod:
- ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
- BF_RNDZ);
+}
+
+/* return NaN if bad bigint literal */
+static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
+{
+ const char *str, *p;
+ size_t len;
+ int flags;
+
+ str = JS_ToCStringLen(ctx, &len, val);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return JS_EXCEPTION;
+ p = str;
+ p += skip_spaces(p);
+ if ((p - str) == len) {
+ val = JS_NewBigInt64(ctx, 0);
+ } else {
+ flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
+ val = js_atof(ctx, p, &p, 0, flags);
+ p += skip_spaces(p);
+ if (!JS_IsException(val)) {
+ if ((p - str) != len) {
+ JS_FreeValue(ctx, val);
+ val = JS_NAN;
+ }
+ }
+ }
+ JS_FreeCString(ctx, str);
+ return val;
+}
+
+static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)
+{
+ val = JS_StringToBigInt(ctx, val);
+ if (JS_VALUE_IS_NAN(val))
+ return JS_ThrowSyntaxError(ctx, "invalid bigint literal");
+ return val;
+}
+
+/* JS Numbers are not allowed */
+static JSValue JS_ToBigIntFree(JSContext *ctx, JSValue val)
+{
+ uint32_t tag;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_SHORT_BIG_INT:
+ case JS_TAG_BIG_INT:
break;
- case OP_pow:
- ret = bf_pow(r, a, b, ctx->fp_env.prec,
- ctx->fp_env.flags | BF_POW_JS_QUIRKS);
+ case JS_TAG_INT:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ case JS_TAG_FLOAT64:
+ goto fail;
+ case JS_TAG_BOOL:
+ val = __JS_NewShortBigInt(ctx, JS_VALUE_GET_INT(val));
break;
+ case JS_TAG_STRING:
+ val = JS_StringToBigIntErr(ctx, val);
+ if (JS_IsException(val))
+ return val;
+ goto redo;
+ case JS_TAG_OBJECT:
+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+ if (JS_IsException(val))
+ return val;
+ goto redo;
default:
- abort();
- }
- if (a == &a_s)
- bf_delete(a);
- if (b == &b_s)
- bf_delete(b);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (unlikely(ret & BF_ST_MEM_ERROR)) {
- JS_FreeValue(ctx, res);
- throw_bf_exception(ctx, ret);
- return -1;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert to bigint");
}
- *pres = res;
- return 0;
- fail:
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- return -1;
+ return val;
}
-/* b must be a positive integer */
-static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b)
+static JSValue JS_ToBigInt(JSContext *ctx, JSValueConst val)
{
- bfdec_t b1;
- int32_t b2;
- int ret;
+ return JS_ToBigIntFree(ctx, JS_DupValue(ctx, val));
+}
- bfdec_init(b->ctx, &b1);
- ret = bfdec_set(&b1, b);
- if (ret) {
- bfdec_delete(&b1);
- return ret;
+/* XXX: merge with JS_ToInt64Free with a specific flag ? */
+static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
+{
+ uint64_t res;
+
+ val = JS_ToBigIntFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
}
- ret = bfdec_rint(&b1, BF_RNDZ);
- if (ret) {
- bfdec_delete(&b1);
- return BF_ST_INVALID_OP; /* must be an integer */
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) {
+ res = JS_VALUE_GET_SHORT_BIG_INT(val);
+ } else {
+ JSBigInt *p = JS_VALUE_GET_PTR(val);
+ /* return the value mod 2^64 */
+ res = p->tab[0];
+#if JS_LIMB_BITS == 32
+ if (p->len >= 2)
+ res |= (uint64_t)p->tab[1] << 32;
+#endif
+ JS_FreeValue(ctx, val);
}
- ret = bfdec_get_int32(&b2, &b1);
- bfdec_delete(&b1);
- if (ret)
- return ret; /* overflow */
- if (b2 < 0)
- return BF_ST_INVALID_OP; /* must be positive */
- return bfdec_pow_ui(r, a, b2);
+ *pres = res;
+ return 0;
}
-static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
- JSValue *pres, JSValue op1, JSValue op2)
+int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
{
- bfdec_t *r, *a, *b;
- int ret;
- JSValue res;
+ return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));
+}
- res = JS_NewBigDecimal(ctx);
- if (JS_IsException(res))
- goto fail;
- r = JS_GetBigDecimal(res);
+static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1;
+ int v;
+ uint32_t tag;
+ JSBigIntBuf buf1;
+ JSBigInt *p1;
- a = JS_ToBigDecimal(ctx, op1);
- if (!a)
- goto fail;
- b = JS_ToBigDecimal(ctx, op2);
- if (!b)
- goto fail;
- switch(op) {
- case OP_add:
- ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ);
- break;
- case OP_sub:
- ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
- break;
- case OP_mul:
- ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
- break;
- case OP_div:
- ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ);
- break;
- case OP_math_mod:
- /* Euclidian remainder */
- ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN);
+ op1 = sp[-1];
+ /* fast path for float64 */
+ if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))
+ goto handle_float64;
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1))
+ goto exception;
+ tag = JS_VALUE_GET_TAG(op1);
+ switch(tag) {
+ case JS_TAG_INT:
+ {
+ int64_t v64;
+ v64 = JS_VALUE_GET_INT(op1);
+ switch(op) {
+ case OP_inc:
+ case OP_dec:
+ v = 2 * (op - OP_dec) - 1;
+ v64 += v;
+ break;
+ case OP_plus:
+ break;
+ case OP_neg:
+ if (v64 == 0) {
+ sp[-1] = __JS_NewFloat64(ctx, -0.0);
+ return 0;
+ } else {
+ v64 = -v64;
+ }
+ break;
+ default:
+ abort();
+ }
+ sp[-1] = JS_NewInt64(ctx, v64);
+ }
break;
- case OP_mod:
- ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ);
+ case JS_TAG_SHORT_BIG_INT:
+ {
+ int64_t v;
+ v = JS_VALUE_GET_SHORT_BIG_INT(op1);
+ switch(op) {
+ case OP_plus:
+ JS_ThrowTypeError(ctx, "bigint argument with unary +");
+ goto exception;
+ case OP_inc:
+ if (v == JS_SHORT_BIG_INT_MAX)
+ goto bigint_slow_case;
+ sp[-1] = __JS_NewShortBigInt(ctx, v + 1);
+ break;
+ case OP_dec:
+ if (v == JS_SHORT_BIG_INT_MIN)
+ goto bigint_slow_case;
+ sp[-1] = __JS_NewShortBigInt(ctx, v - 1);
+ break;
+ case OP_neg:
+ v = JS_VALUE_GET_SHORT_BIG_INT(op1);
+ if (v == JS_SHORT_BIG_INT_MIN) {
+ bigint_slow_case:
+ p1 = js_bigint_set_short(&buf1, op1);
+ goto bigint_slow_case1;
+ }
+ sp[-1] = __JS_NewShortBigInt(ctx, -v);
+ break;
+ default:
+ abort();
+ }
+ }
break;
- case OP_pow:
- ret = js_bfdec_pow(r, a, b);
+ case JS_TAG_BIG_INT:
+ {
+ JSBigInt *r;
+ p1 = JS_VALUE_GET_PTR(op1);
+ bigint_slow_case1:
+ switch(op) {
+ case OP_plus:
+ JS_ThrowTypeError(ctx, "bigint argument with unary +");
+ goto exception;
+ case OP_inc:
+ case OP_dec:
+ {
+ JSBigIntBuf buf2;
+ JSBigInt *p2;
+ p2 = js_bigint_set_si(&buf2, 2 * (op - OP_dec) - 1);
+ r = js_bigint_add(ctx, p1, p2, 0);
+ }
+ break;
+ case OP_neg:
+ r = js_bigint_neg(ctx, p1);
+ break;
+ case OP_not:
+ r = js_bigint_not(ctx, p1);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeValue(ctx, op1);
+ if (!r)
+ goto exception;
+ sp[-1] = JS_CompactBigInt(ctx, r);
+ }
break;
default:
- abort();
+ handle_float64:
+ {
+ double d;
+ d = JS_VALUE_GET_FLOAT64(op1);
+ switch(op) {
+ case OP_inc:
+ case OP_dec:
+ v = 2 * (op - OP_dec) - 1;
+ d += v;
+ break;
+ case OP_plus:
+ break;
+ case OP_neg:
+ d = -d;
+ break;
+ default:
+ abort();
+ }
+ sp[-1] = __JS_NewFloat64(ctx, d);
+ }
+ break;
}
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (unlikely(ret)) {
- JS_FreeValue(ctx, res);
- throw_bf_exception(ctx, ret);
+ return 0;
+ exception:
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static __exception int js_post_inc_slow(JSContext *ctx,
+ JSValue *sp, OPCodeEnum op)
+{
+ JSValue op1;
+
+ /* XXX: allow custom operators */
+ op1 = sp[-1];
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ sp[-1] = JS_UNDEFINED;
return -1;
}
- *pres = res;
+ sp[-1] = op1;
+ sp[0] = JS_DupValue(ctx, op1);
+ return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);
+}
+
+static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1;
+
+ op1 = sp[-1];
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1))
+ goto exception;
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) {
+ sp[-1] = __JS_NewShortBigInt(ctx, ~JS_VALUE_GET_SHORT_BIG_INT(op1));
+ } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
+ JSBigInt *r;
+ r = js_bigint_not(ctx, JS_VALUE_GET_PTR(op1));
+ JS_FreeValue(ctx, op1);
+ if (!r)
+ goto exception;
+ sp[-1] = JS_CompactBigInt(ctx, r);
+ } else {
+ int32_t v1;
+ if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
+ goto exception;
+ sp[-1] = JS_NewInt32(ctx, ~v1);
+ }
return 0;
- fail:
- JS_FreeValue(ctx, res);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
+ exception:
+ sp[-1] = JS_UNDEFINED;
return -1;
}
-#endif /* CONFIG_BIGNUM */
static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
OPCodeEnum op)
d2 = JS_VALUE_GET_FLOAT64(op2);
goto handle_float64;
}
-
-#ifdef CONFIG_BIGNUM
- /* try to call an overloaded operator */
- if ((tag1 == JS_TAG_OBJECT &&
- (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
- (tag2 == JS_TAG_OBJECT &&
- (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
- JSValue res;
- int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
- if (ret != 0) {
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (ret < 0) {
- goto exception;
- } else {
- sp[-2] = res;
- return 0;
+ /* fast path for short big int operations */
+ if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) {
+ js_slimb_t v1, v2;
+ js_sdlimb_t v;
+ v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
+ v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
+ switch(op) {
+ case OP_sub:
+ v = (js_sdlimb_t)v1 - (js_sdlimb_t)v2;
+ break;
+ case OP_mul:
+ v = (js_sdlimb_t)v1 * (js_sdlimb_t)v2;
+ break;
+ case OP_div:
+ if (v2 == 0 ||
+ ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) &&
+ v2 == -1)) {
+ goto slow_big_int;
+ }
+ sp[-2] = __JS_NewShortBigInt(ctx, v1 / v2);
+ return 0;
+ case OP_mod:
+ if (v2 == 0 ||
+ ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) &&
+ v2 == -1)) {
+ goto slow_big_int;
}
+ sp[-2] = __JS_NewShortBigInt(ctx, v1 % v2);
+ return 0;
+ case OP_pow:
+ goto slow_big_int;
+ default:
+ abort();
+ }
+ if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) {
+ sp[-2] = __JS_NewShortBigInt(ctx, v);
+ } else {
+ JSBigInt *r = js_bigint_new_di(ctx, v);
+ if (!r)
+ goto exception;
+ sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r);
}
+ return 0;
}
-#endif
-
op1 = JS_ToNumericFree(ctx, op1);
if (JS_IsException(op1)) {
JS_FreeValue(ctx, op2);
break;
case OP_mul:
v = (int64_t)v1 * (int64_t)v2;
- if (is_math_mode(ctx) &&
- (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER))
- goto handle_bigint;
if (v == 0 && (v1 | v2) < 0) {
sp[-2] = __JS_NewFloat64(ctx, -0.0);
return 0;
}
break;
case OP_div:
- if (is_math_mode(ctx))
- goto handle_bigint;
- sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2);
+ sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
return 0;
-#ifdef CONFIG_BIGNUM
- case OP_math_mod:
- if (unlikely(v2 == 0)) {
- throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO);
- goto exception;
- }
- v = (int64_t)v1 % (int64_t)v2;
- if (v < 0) {
- if (v2 < 0)
- v -= v2;
- else
- v += v2;
- }
- break;
-#endif
case OP_mod:
if (v1 < 0 || v2 <= 0) {
sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));
}
break;
case OP_pow:
- if (!is_math_mode(ctx)) {
- sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
- return 0;
- } else {
- goto handle_bigint;
- }
- break;
+ sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
+ return 0;
default:
abort();
}
sp[-2] = JS_NewInt64(ctx, v);
- } else
-#ifdef CONFIG_BIGNUM
- if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
- if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2))
- goto exception;
- } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
- if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2))
- goto exception;
- } else
-#endif
- if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
- handle_bigint:
- if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
+ } else if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_BIG_INT) &&
+ (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_BIG_INT)) {
+ JSBigInt *p1, *p2, *r;
+ JSBigIntBuf buf1, buf2;
+ slow_big_int:
+ /* bigint result */
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
+ p1 = js_bigint_set_short(&buf1, op1);
+ else
+ p1 = JS_VALUE_GET_PTR(op1);
+ if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
+ p2 = js_bigint_set_short(&buf2, op2);
+ else
+ p2 = JS_VALUE_GET_PTR(op2);
+ switch(op) {
+ case OP_add:
+ r = js_bigint_add(ctx, p1, p2, 0);
+ break;
+ case OP_sub:
+ r = js_bigint_add(ctx, p1, p2, 1);
+ break;
+ case OP_mul:
+ r = js_bigint_mul(ctx, p1, p2);
+ break;
+ case OP_div:
+ r = js_bigint_divrem(ctx, p1, p2, FALSE);
+ break;
+ case OP_mod:
+ r = js_bigint_divrem(ctx, p1, p2, TRUE);
+ break;
+ case OP_pow:
+ r = js_bigint_pow(ctx, p1, p2);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (!r)
goto exception;
+ sp[-2] = JS_CompactBigInt(ctx, r);
} else {
double dr;
/* float64 result */
if (JS_ToFloat64Free(ctx, &d2, op2))
goto exception;
handle_float64:
- if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
- goto handle_bigint;
switch(op) {
case OP_sub:
dr = d1 - d2;
case OP_mod:
dr = fmod(d1, d2);
break;
-#ifdef CONFIG_BIGNUM
- case OP_math_mod:
- d2 = fabs(d2);
- dr = fmod(d1, d2);
- /* XXX: loss of accuracy if dr < 0 */
- if (dr < 0)
- dr += d2;
- break;
-#endif
case OP_pow:
dr = js_pow(d1, d2);
break;
sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
return 0;
}
-
- if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {
-#ifdef CONFIG_BIGNUM
- /* try to call an overloaded operator */
- if ((tag1 == JS_TAG_OBJECT &&
- (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED &&
- tag2 != JS_TAG_STRING)) ||
- (tag2 == JS_TAG_OBJECT &&
- (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED &&
- tag1 != JS_TAG_STRING))) {
- JSValue res;
- int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add,
- FALSE, HINT_NONE);
- if (ret != 0) {
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (ret < 0) {
- goto exception;
- } else {
- sp[-2] = res;
- return 0;
- }
- }
+ /* fast path for short bigint */
+ if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) {
+ js_slimb_t v1, v2;
+ js_sdlimb_t v;
+ v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
+ v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
+ v = (js_sdlimb_t)v1 + (js_sdlimb_t)v2;
+ if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) {
+ sp[-2] = __JS_NewShortBigInt(ctx, v);
+ } else {
+ JSBigInt *r = js_bigint_new_di(ctx, v);
+ if (!r)
+ goto exception;
+ sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r);
}
-#endif
+ return 0;
+ }
+
+ if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {
op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
if (JS_IsException(op1)) {
JS_FreeValue(ctx, op2);
v2 = JS_VALUE_GET_INT(op2);
v = (int64_t)v1 + (int64_t)v2;
sp[-2] = JS_NewInt64(ctx, v);
- } else
-#ifdef CONFIG_BIGNUM
- if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
- if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
- goto exception;
- } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
- if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
- goto exception;
- } else
-#endif
- if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
- handle_bigint:
- if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
+ } else if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) &&
+ (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) {
+ JSBigInt *p1, *p2, *r;
+ JSBigIntBuf buf1, buf2;
+ /* bigint result */
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
+ p1 = js_bigint_set_short(&buf1, op1);
+ else
+ p1 = JS_VALUE_GET_PTR(op1);
+ if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
+ p2 = js_bigint_set_short(&buf2, op2);
+ else
+ p2 = JS_VALUE_GET_PTR(op2);
+ r = js_bigint_add(ctx, p1, p2, 0);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (!r)
goto exception;
+ sp[-2] = JS_CompactBigInt(ctx, r);
} else {
double d1, d2;
/* float64 result */
}
if (JS_ToFloat64Free(ctx, &d2, op2))
goto exception;
- if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
- goto handle_bigint;
sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
}
return 0;
tag1 = JS_VALUE_GET_NORM_TAG(op1);
tag2 = JS_VALUE_GET_NORM_TAG(op2);
-#ifdef CONFIG_BIGNUM
- /* try to call an overloaded operator */
- if ((tag1 == JS_TAG_OBJECT &&
- (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
- (tag2 == JS_TAG_OBJECT &&
- (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
- JSValue res;
- int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
- if (ret != 0) {
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (ret < 0) {
- goto exception;
- } else {
- sp[-2] = res;
- return 0;
- }
+ if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) {
+ js_slimb_t v1, v2, v;
+ js_sdlimb_t vd;
+ v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
+ v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
+ /* bigint fast path */
+ switch(op) {
+ case OP_and:
+ v = v1 & v2;
+ break;
+ case OP_or:
+ v = v1 | v2;
+ break;
+ case OP_xor:
+ v = v1 ^ v2;
+ break;
+ case OP_sar:
+ if (v2 > (JS_LIMB_BITS - 1)) {
+ goto slow_big_int;
+ } else if (v2 < 0) {
+ if (v2 < -(JS_LIMB_BITS - 1))
+ goto slow_big_int;
+ v2 = -v2;
+ goto bigint_shl;
+ }
+ bigint_sar:
+ v = v1 >> v2;
+ break;
+ case OP_shl:
+ if (v2 > (JS_LIMB_BITS - 1)) {
+ goto slow_big_int;
+ } else if (v2 < 0) {
+ if (v2 < -(JS_LIMB_BITS - 1))
+ goto slow_big_int;
+ v2 = -v2;
+ goto bigint_sar;
+ }
+ bigint_shl:
+ vd = (js_sdlimb_t)v1 << v2;
+ if (likely(vd >= JS_SHORT_BIG_INT_MIN &&
+ vd <= JS_SHORT_BIG_INT_MAX)) {
+ v = vd;
+ } else {
+ JSBigInt *r = js_bigint_new_di(ctx, vd);
+ if (!r)
+ goto exception;
+ sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r);
+ return 0;
+ }
+ break;
+ default:
+ abort();
}
+ sp[-2] = __JS_NewShortBigInt(ctx, v);
+ return 0;
}
-#endif
-
op1 = JS_ToNumericFree(ctx, op1);
if (JS_IsException(op1)) {
JS_FreeValue(ctx, op2);
goto exception;
}
- if (is_math_mode(ctx))
- goto bigint_op;
-
tag1 = JS_VALUE_GET_TAG(op1);
tag2 = JS_VALUE_GET_TAG(op2);
- if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
- if (tag1 != tag2) {
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- JS_ThrowTypeError(ctx, "both operands must be bigint");
- goto exception;
- } else {
- bigint_op:
- if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
- goto exception;
+ if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) &&
+ (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) {
+ JSBigInt *p1, *p2, *r;
+ JSBigIntBuf buf1, buf2;
+ slow_big_int:
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
+ p1 = js_bigint_set_short(&buf1, op1);
+ else
+ p1 = JS_VALUE_GET_PTR(op1);
+ if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
+ p2 = js_bigint_set_short(&buf2, op2);
+ else
+ p2 = JS_VALUE_GET_PTR(op2);
+ switch(op) {
+ case OP_and:
+ case OP_or:
+ case OP_xor:
+ r = js_bigint_logic(ctx, p1, p2, op);
+ break;
+ case OP_shl:
+ case OP_sar:
+ {
+ js_slimb_t shift;
+ shift = js_bigint_get_si_sat(p2);
+ if (shift > INT32_MAX)
+ shift = INT32_MAX;
+ else if (shift < -INT32_MAX)
+ shift = -INT32_MAX;
+ if (op == OP_sar)
+ shift = -shift;
+ if (shift >= 0)
+ r = js_bigint_shl(ctx, p1, shift);
+ else
+ r = js_bigint_shr(ctx, p1, -shift);
+ }
+ break;
+ default:
+ abort();
}
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (!r)
+ goto exception;
+ sp[-2] = JS_CompactBigInt(ctx, r);
} else {
if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
JS_FreeValue(ctx, op2);
return -1;
}
-/* Note: also used for bigint */
-static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op,
- JSValue op1, JSValue op2)
+/* op1 must be a bigint or int. */
+static JSBigInt *JS_ToBigIntBuf(JSContext *ctx, JSBigIntBuf *buf1,
+ JSValue op1)
{
- bf_t a_s, b_s, *a, *b;
- int res;
-
- a = JS_ToBigFloat(ctx, &a_s, op1);
- if (!a) {
- JS_FreeValue(ctx, op2);
- return -1;
- }
- b = JS_ToBigFloat(ctx, &b_s, op2);
- if (!b) {
- if (a == &a_s)
- bf_delete(a);
- JS_FreeValue(ctx, op1);
- return -1;
- }
- switch(op) {
- case OP_lt:
- res = bf_cmp_lt(a, b); /* if NaN return false */
- break;
- case OP_lte:
- res = bf_cmp_le(a, b); /* if NaN return false */
- break;
- case OP_gt:
- res = bf_cmp_lt(b, a); /* if NaN return false */
+ JSBigInt *p1;
+
+ switch(JS_VALUE_GET_TAG(op1)) {
+ case JS_TAG_INT:
+ p1 = js_bigint_set_si(buf1, JS_VALUE_GET_INT(op1));
break;
- case OP_gte:
- res = bf_cmp_le(b, a); /* if NaN return false */
+ case JS_TAG_SHORT_BIG_INT:
+ p1 = js_bigint_set_short(buf1, op1);
break;
- case OP_eq:
- res = bf_cmp_eq(a, b); /* if NaN return false */
+ case JS_TAG_BIG_INT:
+ p1 = JS_VALUE_GET_PTR(op1);
break;
default:
abort();
}
- if (a == &a_s)
- bf_delete(a);
- if (b == &b_s)
- bf_delete(b);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- return res;
+ return p1;
}
-#ifdef CONFIG_BIGNUM
-static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op,
- JSValue op1, JSValue op2)
+/* op1 and op2 must be numeric types and at least one must be a
+ bigint. No exception is generated. */
+static int js_compare_bigint(JSContext *ctx, OPCodeEnum op,
+ JSValue op1, JSValue op2)
{
- bfdec_t *a, *b;
- int res;
-
- /* Note: binary floats are converted to bigdecimal with
- toString(). It is not mathematically correct but is consistent
- with the BigDecimal() constructor behavior */
- op1 = JS_ToBigDecimalFree(ctx, op1, TRUE);
- if (JS_IsException(op1)) {
- JS_FreeValue(ctx, op2);
- return -1;
- }
- op2 = JS_ToBigDecimalFree(ctx, op2, TRUE);
- if (JS_IsException(op2)) {
+ int res, val, tag1, tag2;
+ JSBigIntBuf buf1, buf2;
+ JSBigInt *p1, *p2;
+
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_INT) &&
+ (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_INT)) {
+ /* fast path */
+ js_slimb_t v1, v2;
+ if (tag1 == JS_TAG_INT)
+ v1 = JS_VALUE_GET_INT(op1);
+ else
+ v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
+ if (tag2 == JS_TAG_INT)
+ v2 = JS_VALUE_GET_INT(op2);
+ else
+ v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
+ val = (v1 > v2) - (v1 < v2);
+ } else {
+ if (tag1 == JS_TAG_FLOAT64) {
+ p2 = JS_ToBigIntBuf(ctx, &buf2, op2);
+ val = js_bigint_float64_cmp(ctx, p2, JS_VALUE_GET_FLOAT64(op1));
+ if (val == 2)
+ goto unordered;
+ val = -val;
+ } else if (tag2 == JS_TAG_FLOAT64) {
+ p1 = JS_ToBigIntBuf(ctx, &buf1, op1);
+ val = js_bigint_float64_cmp(ctx, p1, JS_VALUE_GET_FLOAT64(op2));
+ if (val == 2) {
+ unordered:
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return FALSE;
+ }
+ } else {
+ p1 = JS_ToBigIntBuf(ctx, &buf1, op1);
+ p2 = JS_ToBigIntBuf(ctx, &buf2, op2);
+ val = js_bigint_cmp(ctx, p1, p2);
+ }
JS_FreeValue(ctx, op1);
- return -1;
+ JS_FreeValue(ctx, op2);
}
- a = JS_ToBigDecimal(ctx, op1); /* cannot fail */
- b = JS_ToBigDecimal(ctx, op2); /* cannot fail */
switch(op) {
case OP_lt:
- res = bfdec_cmp_lt(a, b); /* if NaN return false */
+ res = val < 0;
break;
case OP_lte:
- res = bfdec_cmp_le(a, b); /* if NaN return false */
+ res = val <= 0;
break;
case OP_gt:
- res = bfdec_cmp_lt(b, a); /* if NaN return false */
+ res = val > 0;
break;
case OP_gte:
- res = bfdec_cmp_le(b, a); /* if NaN return false */
+ res = val >= 0;
break;
case OP_eq:
- res = bfdec_cmp_eq(a, b); /* if NaN return false */
+ res = val == 0;
break;
default:
abort();
}
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
return res;
}
-#endif /* !CONFIG_BIGNUM */
static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
OPCodeEnum op)
op2 = sp[-1];
tag1 = JS_VALUE_GET_NORM_TAG(op1);
tag2 = JS_VALUE_GET_NORM_TAG(op2);
-#ifdef CONFIG_BIGNUM
- /* try to call an overloaded operator */
- if ((tag1 == JS_TAG_OBJECT &&
- (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
- (tag2 == JS_TAG_OBJECT &&
- (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
- JSValue ret;
- res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op,
- FALSE, HINT_NUMBER);
- if (res != 0) {
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (res < 0) {
- goto exception;
- } else {
- sp[-2] = ret;
- return 0;
- }
- }
- }
-#endif
op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
if (JS_IsException(op1)) {
JS_FreeValue(ctx, op2);
/* fast path for float64/int */
goto float64_compare;
} else {
- if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) ||
- (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&
- !is_math_mode(ctx)) {
+ if ((((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) &&
+ tag2 == JS_TAG_STRING) ||
+ ((tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) &&
+ tag1 == JS_TAG_STRING))) {
if (tag1 == JS_TAG_STRING) {
op1 = JS_StringToBigInt(ctx, op1);
- if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
+ if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT &&
+ JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT)
goto invalid_bigint_string;
}
if (tag2 == JS_TAG_STRING) {
op2 = JS_StringToBigInt(ctx, op2);
- if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
+ if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT &&
+ JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT) {
invalid_bigint_string:
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
tag1 = JS_VALUE_GET_NORM_TAG(op1);
tag2 = JS_VALUE_GET_NORM_TAG(op2);
-#ifdef CONFIG_BIGNUM
- if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
- res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2);
- if (res < 0)
- goto exception;
- } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
- res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2);
- if (res < 0)
- goto exception;
- } else
-#endif
- if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
- res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2);
- if (res < 0)
- goto exception;
+ if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT ||
+ tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) {
+ res = js_compare_bigint(ctx, op, op1, op2);
} else {
double d1, d2;
static BOOL tag_is_number(uint32_t tag)
{
- return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT ||
- tag == JS_TAG_FLOAT64
-#ifdef CONFIG_BIGNUM
- || tag == JS_TAG_BIG_FLOAT || tag == JS_TAG_BIG_DECIMAL
-#endif
- );
+ return (tag == JS_TAG_INT ||
+ tag == JS_TAG_FLOAT64 ||
+ tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT);
}
static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
BOOL is_neq)
{
JSValue op1, op2;
-#ifdef CONFIG_BIGNUM
- JSValue ret;
-#endif
int res;
uint32_t tag1, tag2;
d2 = JS_VALUE_GET_INT(op2);
}
res = (d1 == d2);
- } else
-#ifdef CONFIG_BIGNUM
- if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
- res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2);
- if (res < 0)
- goto exception;
- } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
- res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2);
- if (res < 0)
- goto exception;
- } else
-#endif
- {
- res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2);
- if (res < 0)
- goto exception;
+ } else {
+ res = js_compare_bigint(ctx, OP_eq, op1, op2);
}
} else if (tag1 == tag2) {
-#ifdef CONFIG_BIGNUM
- if (tag1 == JS_TAG_OBJECT) {
- /* try the fallback operator */
- res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
- is_neq ? OP_neq : OP_eq,
- FALSE, HINT_NONE);
- if (res != 0) {
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (res < 0) {
- goto exception;
- } else {
- sp[-2] = ret;
- return 0;
- }
- }
- }
-#endif
res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
} else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
(tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
} else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) ||
(tag2 == JS_TAG_STRING && tag_is_number(tag1))) {
- if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) &&
- !is_math_mode(ctx)) {
+ if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT ||
+ tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) {
if (tag1 == JS_TAG_STRING) {
op1 = JS_StringToBigInt(ctx, op1);
- if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
+ if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT &&
+ JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT)
goto invalid_bigint_string;
}
if (tag2 == JS_TAG_STRING) {
op2 = JS_StringToBigInt(ctx, op2);
- if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
+ if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT &&
+ JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT ) {
invalid_bigint_string:
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
(tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) ||
(tag2 == JS_TAG_OBJECT &&
(tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) {
-#ifdef CONFIG_BIGNUM
- /* try the fallback operator */
- res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
- is_neq ? OP_neq : OP_eq,
- FALSE, HINT_NONE);
- if (res != 0) {
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (res < 0) {
- goto exception;
- } else {
- sp[-2] = ret;
- return 0;
- }
- }
-#endif
op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
if (JS_IsException(op1)) {
JS_FreeValue(ctx, op2);
JS_FreeValue(ctx, op1);
goto exception;
}
- /* XXX: could forbid >>> in bignum mode */
- if (!is_math_mode(ctx) &&
- (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
- JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) {
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
+ JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT ||
+ JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT ||
+ JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) {
JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
return -1;
}
-#ifdef CONFIG_BIGNUM
-static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
- int64_t exponent)
-{
- bf_t r_s, *r = &r_s;
- double d;
- int ret;
-
- /* always convert to Float64 */
- bf_init(ctx->bf_ctx, r);
- ret = bf_mul_pow_radix(r, a, 10, exponent,
- 53, bf_set_exp_bits(11) | BF_RNDN |
- BF_FLAG_SUBNORMAL);
- bf_get_float64(r, &d, BF_RNDN);
- bf_delete(r);
- if (ret & BF_ST_MEM_ERROR)
- return JS_ThrowOutOfMemory(ctx);
- else
- return __JS_NewFloat64(ctx, d);
-}
-
-static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp)
-{
- bf_t a_s, *a, *r;
- JSValue op1, op2, res;
- int64_t e;
- int ret;
-
- res = JS_NewBigFloat(ctx);
- if (JS_IsException(res))
- return -1;
- r = JS_GetBigFloat(res);
- op1 = sp[-2];
- op2 = sp[-1];
- a = JS_ToBigFloat(ctx, &a_s, op1);
- if (!a) {
- JS_FreeValue(ctx, res);
- return -1;
- }
- if (JS_IsBigInt(ctx, op2)) {
- ret = JS_ToBigInt64(ctx, &e, op2);
- } else {
- ret = JS_ToInt64(ctx, &e, op2);
- }
- if (ret) {
- if (a == &a_s)
- bf_delete(a);
- JS_FreeValue(ctx, res);
- return -1;
- }
-
- bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags);
- if (a == &a_s)
- bf_delete(a);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- sp[-2] = res;
- return 0;
-}
-#endif
-
/* XXX: Should take JSValueConst arguments */
static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
JSStrictEqModeEnum eq_mode)
res = (d1 == d2); /* if NaN return false and +0 == -0 */
}
goto done_no_free;
+ case JS_TAG_SHORT_BIG_INT:
case JS_TAG_BIG_INT:
{
- bf_t a_s, *a, b_s, *b;
- if (tag1 != tag2) {
- res = FALSE;
- break;
- }
- a = JS_ToBigFloat(ctx, &a_s, op1); /* cannot fail */
- b = JS_ToBigFloat(ctx, &b_s, op2); /* cannot fail */
- res = bf_cmp_eq(a, b);
- if (a == &a_s)
- bf_delete(a);
- if (b == &b_s)
- bf_delete(b);
- }
- break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- {
- JSBigFloat *p1, *p2;
- const bf_t *a, *b;
- if (tag1 != tag2) {
- res = FALSE;
- break;
- }
- p1 = JS_VALUE_GET_PTR(op1);
- p2 = JS_VALUE_GET_PTR(op2);
- a = &p1->num;
- b = &p2->num;
- if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
- if (eq_mode == JS_EQ_SAME_VALUE_ZERO &&
- a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) {
- res = TRUE;
- } else {
- res = (bf_cmp_full(a, b) == 0);
- }
- } else {
- res = bf_cmp_eq(a, b);
- }
- }
- break;
- case JS_TAG_BIG_DECIMAL:
- {
- JSBigDecimal *p1, *p2;
- const bfdec_t *a, *b;
- if (tag1 != tag2) {
+ JSBigIntBuf buf1, buf2;
+ JSBigInt *p1, *p2;
+
+ if (tag2 != JS_TAG_SHORT_BIG_INT &&
+ tag2 != JS_TAG_BIG_INT) {
res = FALSE;
break;
}
- p1 = JS_VALUE_GET_PTR(op1);
- p2 = JS_VALUE_GET_PTR(op2);
- a = &p1->num;
- b = &p2->num;
- res = bfdec_cmp_eq(a, b);
+
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
+ p1 = js_bigint_set_short(&buf1, op1);
+ else
+ p1 = JS_VALUE_GET_PTR(op1);
+ if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
+ p2 = js_bigint_set_short(&buf2, op2);
+ else
+ p2 = JS_VALUE_GET_PTR(op2);
+ res = (js_bigint_cmp(ctx, p1, p2) == 0);
}
break;
-#endif
default:
res = FALSE;
break;
tag = JS_VALUE_GET_NORM_TAG(op1);
switch(tag) {
+ case JS_TAG_SHORT_BIG_INT:
case JS_TAG_BIG_INT:
atom = JS_ATOM_bigint;
break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- atom = JS_ATOM_bigfloat;
- break;
- case JS_TAG_BIG_DECIMAL:
- atom = JS_ATOM_bigdecimal;
- break;
-#endif
case JS_TAG_INT:
case JS_TAG_FLOAT64:
atom = JS_ATOM_number;
sf->prev_frame = prev_sf;
rt->current_stack_frame = sf;
ctx = p->u.cfunc.realm; /* change the current realm */
-
-#ifdef CONFIG_BIGNUM
- /* we only propagate the bignum mode as some runtime functions
- test it */
- if (prev_sf)
- sf->js_mode = prev_sf->js_mode & JS_MODE_MATH;
- else
- sf->js_mode = 0;
-#else
sf->js_mode = 0;
-#endif
sf->cur_func = (JSValue)func_obj;
sf->arg_count = argc;
arg_buf = argv;
*sp++ = JS_NewInt32(ctx, get_u32(pc));
pc += 4;
BREAK;
+ CASE(OP_push_bigint_i32):
+ *sp++ = __JS_NewShortBigInt(ctx, (int)get_u32(pc));
+ pc += 4;
+ BREAK;
CASE(OP_push_const):
*sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
pc += 4;
v2 = JS_VALUE_GET_INT(op2);
r = (int64_t)v1 * v2;
if (unlikely((int)r != r)) {
-#ifdef CONFIG_BIGNUM
- if (unlikely(sf->js_mode & JS_MODE_MATH) &&
- (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER))
- goto binary_arith_slow;
-#endif
d = (double)r;
goto mul_fp_res;
}
sp[-2] = JS_NewInt32(ctx, r);
sp--;
} else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
-#ifdef CONFIG_BIGNUM
- if (unlikely(sf->js_mode & JS_MODE_MATH))
- goto binary_arith_slow;
-#endif
d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);
mul_fp_res:
sp[-2] = __JS_NewFloat64(ctx, d);
}
BREAK;
CASE(OP_mod):
-#ifdef CONFIG_BIGNUM
- CASE(OP_math_mod):
-#endif
{
JSValue op1, op2;
op1 = sp[-2];
uint32_t v1, v2;
v1 = JS_VALUE_GET_INT(op1);
v2 = JS_VALUE_GET_INT(op2);
-#ifdef CONFIG_BIGNUM
- {
- int64_t r;
- if (unlikely(sf->js_mode & JS_MODE_MATH)) {
- if (v2 > 0x1f)
- goto shl_slow;
- r = (int64_t)v1 << v2;
- if ((int)r != r)
- goto shl_slow;
- } else {
- v2 &= 0x1f;
- }
- }
-#else
v2 &= 0x1f;
-#endif
sp[-2] = JS_NewInt32(ctx, v1 << v2);
sp--;
} else {
-#ifdef CONFIG_BIGNUM
- shl_slow:
-#endif
if (js_binary_logic_slow(ctx, sp, opcode))
goto exception;
sp--;
if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
uint32_t v2;
v2 = JS_VALUE_GET_INT(op2);
- /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */
v2 &= 0x1f;
sp[-2] = JS_NewUint32(ctx,
(uint32_t)JS_VALUE_GET_INT(op1) >>
if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
uint32_t v2;
v2 = JS_VALUE_GET_INT(op2);
-#ifdef CONFIG_BIGNUM
- if (unlikely(v2 > 0x1f)) {
- if (unlikely(sf->js_mode & JS_MODE_MATH))
- goto sar_slow;
- else
- v2 &= 0x1f;
- }
-#else
v2 &= 0x1f;
-#endif
sp[-2] = JS_NewInt32(ctx,
(int)JS_VALUE_GET_INT(op1) >> v2);
sp--;
} else {
-#ifdef CONFIG_BIGNUM
- sar_slow:
-#endif
if (js_binary_logic_slow(ctx, sp, opcode))
goto exception;
sp--;
OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0));
OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1));
-#ifdef CONFIG_BIGNUM
- CASE(OP_mul_pow10):
- if (rt->bigfloat_ops.mul_pow10(ctx, sp))
- goto exception;
- sp--;
- BREAK;
-#endif
CASE(OP_in):
if (js_operator_in(ctx, sp))
goto exception;
TOK_AND_ASSIGN,
TOK_XOR_ASSIGN,
TOK_OR_ASSIGN,
-#ifdef CONFIG_BIGNUM
- TOK_MATH_POW_ASSIGN,
-#endif
TOK_POW_ASSIGN,
TOK_LAND_ASSIGN,
TOK_LOR_ASSIGN,
TOK_STRICT_NEQ,
TOK_LAND,
TOK_LOR,
-#ifdef CONFIG_BIGNUM
- TOK_MATH_POW,
-#endif
TOK_POW,
TOK_ARROW,
TOK_ELLIPSIS,
} str;
struct {
JSValue val;
-#ifdef CONFIG_BIGNUM
- slimb_t exponent; /* may be != 0 only if val is a float */
-#endif
} num;
struct {
JSAtom atom;
{
JSValue ret;
const uint8_t *p1;
- int flags, radix;
+ int flags;
flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |
- ATOD_ACCEPT_UNDERSCORES;
- flags |= ATOD_ACCEPT_SUFFIX;
-#ifdef CONFIG_BIGNUM
- if (s->cur_func->js_mode & JS_MODE_MATH) {
- flags |= ATOD_MODE_BIGINT;
- if (s->cur_func->js_mode & JS_MODE_MATH)
- flags |= ATOD_TYPE_BIG_FLOAT;
- }
-#endif
- radix = 0;
-#ifdef CONFIG_BIGNUM
- s->token.u.num.exponent = 0;
- ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix,
- flags, &s->token.u.num.exponent);
-#else
- ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
+ ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX;
+ ret = js_atof(s->ctx, (const char *)p, (const char **)&p, 0,
flags);
-#endif
if (JS_IsException(ret))
goto fail;
/* reject `10instanceof Number` */
goto def_token;
}
break;
-#ifdef CONFIG_BIGNUM
- /* in math mode, '^' is the power operator. '^^' is always the
- xor operator and '**' is always the power operator */
- case '^':
- if (p[1] == '=') {
- p += 2;
- if (s->cur_func->js_mode & JS_MODE_MATH)
- s->token.val = TOK_MATH_POW_ASSIGN;
- else
- s->token.val = TOK_XOR_ASSIGN;
- } else if (p[1] == '^') {
- if (p[2] == '=') {
- p += 3;
- s->token.val = TOK_XOR_ASSIGN;
- } else {
- p += 2;
- s->token.val = '^';
- }
- } else {
- p++;
- if (s->cur_func->js_mode & JS_MODE_MATH)
- s->token.val = TOK_MATH_POW;
- else
- s->token.val = '^';
- }
- break;
-#else
case '^':
if (p[1] == '=') {
p += 2;
goto def_token;
}
break;
-#endif
case '|':
if (p[1] == '=') {
p += 2;
} else if (s->token.val == TOK_NUMBER) {
JSValue val;
val = s->token.u.num.val;
-#ifdef CONFIG_BIGNUM
- if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- val = s->ctx->rt->bigfloat_ops.
- mul_pow10_to_float64(s->ctx, &p->num,
- s->token.u.num.exponent);
- if (JS_IsException(val))
- goto fail;
- name = JS_ValueToAtom(s->ctx, val);
- JS_FreeValue(s->ctx, val);
- } else
-#endif
- {
- name = JS_ValueToAtom(s->ctx, val);
- }
+ name = JS_ValueToAtom(s->ctx, val);
if (name == JS_ATOM_NULL)
goto fail;
if (next_token(s))
if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
emit_op(s, OP_push_i32);
emit_u32(s, JS_VALUE_GET_INT(val));
- } else
-#ifdef CONFIG_BIGNUM
- if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
- slimb_t e;
- int ret;
-
- /* need a runtime conversion */
- /* XXX: could add a cache and/or do it once at
- the start of the function */
- if (emit_push_const(s, val, 0) < 0)
- return -1;
- e = s->token.u.num.exponent;
- if (e == (int32_t)e) {
- emit_op(s, OP_push_i32);
- emit_u32(s, e);
+ } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) {
+ int64_t v;
+ v = JS_VALUE_GET_SHORT_BIG_INT(val);
+ if (v >= INT32_MIN && v <= INT32_MAX) {
+ emit_op(s, OP_push_bigint_i32);
+ emit_u32(s, v);
} else {
- val = JS_NewBigInt64_1(s->ctx, e);
- if (JS_IsException(val))
- return -1;
- ret = emit_push_const(s, val, 0);
- JS_FreeValue(s->ctx, val);
- if (ret < 0)
- return -1;
+ goto large_number;
}
- emit_op(s, OP_mul_pow10);
- } else
-#endif
- {
+ } else {
+ large_number:
if (emit_push_const(s, val, 0) < 0)
return -1;
}
break;
}
if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) {
-#ifdef CONFIG_BIGNUM
- if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) {
- /* Extended exponentiation syntax rules: we extend the ES7
- grammar in order to have more intuitive semantics:
- -2**2 evaluates to -4. */
- if (!(s->cur_func->js_mode & JS_MODE_MATH)) {
- if (parse_flags & PF_POW_FORBIDDEN) {
- JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
- return -1;
- }
- }
- if (next_token(s))
- return -1;
- if (js_parse_unary(s, PF_POW_ALLOWED))
- return -1;
- emit_op(s, OP_pow);
- }
-#else
if (s->token.val == TOK_POW) {
/* Strict ES7 exponentiation syntax rules: To solve
conficting semantics between different implementations
return -1;
emit_op(s, OP_pow);
}
-#endif
}
return 0;
}
opcode = OP_div;
break;
case '%':
-#ifdef CONFIG_BIGNUM
- if (s->cur_func->js_mode & JS_MODE_MATH)
- opcode = OP_math_mod;
- else
-#endif
- opcode = OP_mod;
+ opcode = OP_mod;
break;
default:
return 0;
static const uint8_t assign_opcodes[] = {
OP_mul, OP_div, OP_mod, OP_add, OP_sub,
OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or,
-#ifdef CONFIG_BIGNUM
- OP_pow,
-#endif
OP_pow,
};
op = assign_opcodes[op - TOK_MUL_ASSIGN];
-#ifdef CONFIG_BIGNUM
- if (s->cur_func->js_mode & JS_MODE_MATH) {
- if (op == OP_mod)
- op = OP_math_mod;
- }
-#endif
emit_op(s, op);
}
put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);
printf(" mode:");
if (b->js_mode & JS_MODE_STRICT)
printf(" strict");
-#ifdef CONFIG_BIGNUM
- if (b->js_mode & JS_MODE_MATH)
- printf(" math");
-#endif
printf("\n");
}
if (b->arg_count && b->vardefs) {
}
goto no_change;
+ case OP_push_bigint_i32:
+ if (OPTIMIZE) {
+ /* transform i32(val) neg -> i32(-val) */
+ val = get_i32(bc_buf + pos + 1);
+ if (val != INT32_MIN
+ && code_match(&cc, pos_next, OP_neg, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ if (code_match(&cc, cc.pos, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ } else {
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_push_bigint_i32);
+ dbuf_put_u32(&bc_out, -val);
+ }
+ pos_next = cc.pos;
+ break;
+ }
+ }
+ goto no_change;
+
#if SHORT_OPCODES
case OP_push_const:
case OP_fclosure:
else if (!strcmp(str, "use strip")) {
s->cur_func->js_mode |= JS_MODE_STRIP;
}
-#endif
-#ifdef CONFIG_BIGNUM
- else if (s->ctx->bignum_ext && !strcmp(str, "use math")) {
- s->cur_func->js_mode |= JS_MODE_MATH;
- }
#endif
}
return js_parse_seek_token(s, &pos);
BC_TAG_DATE,
BC_TAG_OBJECT_VALUE,
BC_TAG_OBJECT_REFERENCE,
-#ifdef CONFIG_BIGNUM
- BC_TAG_BIG_FLOAT,
- BC_TAG_BIG_DECIMAL,
-#endif
} BCTagEnum;
-#ifdef CONFIG_BIGNUM
-#define BC_VERSION 0x43
-#else
-#define BC_VERSION 3
-#endif
+#define BC_VERSION 4
typedef struct BCWriterState {
JSContext *ctx;
"Date",
"ObjectValue",
"ObjectReference",
-#ifdef CONFIG_BIGNUM
- "bigfloat",
- "bigdecimal",
-#endif
};
#endif
}
}
-static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj)
+static int JS_WriteBigInt(BCWriterState *s, JSValueConst obj)
{
- uint32_t tag, tag1;
- int64_t e;
- JSBigFloat *bf = JS_VALUE_GET_PTR(obj);
- bf_t *a = &bf->num;
- size_t len, i, n1, j;
- limb_t v;
-
- tag = JS_VALUE_GET_TAG(obj);
- switch(tag) {
- case JS_TAG_BIG_INT:
- tag1 = BC_TAG_BIG_INT;
- break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- tag1 = BC_TAG_BIG_FLOAT;
- break;
- case JS_TAG_BIG_DECIMAL:
- tag1 = BC_TAG_BIG_DECIMAL;
- break;
-#endif
- default:
- abort();
- }
- bc_put_u8(s, tag1);
+ JSBigIntBuf buf;
+ JSBigInt *p;
+ uint32_t len, i;
+ js_limb_t v, b;
+ int shift;
+
+ bc_put_u8(s, BC_TAG_BIG_INT);
- /* sign + exponent */
- if (a->expn == BF_EXP_ZERO)
- e = 0;
- else if (a->expn == BF_EXP_INF)
- e = 1;
- else if (a->expn == BF_EXP_NAN)
- e = 2;
- else if (a->expn >= 0)
- e = a->expn + 3;
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_SHORT_BIG_INT)
+ p = js_bigint_set_short(&buf, obj);
else
- e = a->expn;
- e = (e * 2) | a->sign;
- if (e < INT32_MIN || e > INT32_MAX) {
- JS_ThrowInternalError(s->ctx, "bignum exponent is too large");
- return -1;
+ p = JS_VALUE_GET_PTR(obj);
+ if (p->len == 1 && p->tab[0] == 0) {
+ /* zero case */
+ len = 0;
+ } else {
+ /* compute the length of the two's complement representation
+ in bytes */
+ len = p->len * (JS_LIMB_BITS / 8);
+ v = p->tab[p->len - 1];
+ shift = JS_LIMB_BITS - 8;
+ while (shift > 0) {
+ b = (v >> shift) & 0xff;
+ if (b != 0x00 && b != 0xff)
+ break;
+ if ((b & 1) != ((v >> (shift - 1)) & 1))
+ break;
+ shift -= 8;
+ len--;
+ }
}
- bc_put_sleb128(s, e);
-
- /* mantissa */
- if (a->len != 0) {
- if (tag != JS_TAG_BIG_DECIMAL) {
- i = 0;
- while (i < a->len && a->tab[i] == 0)
- i++;
- assert(i < a->len);
- v = a->tab[i];
- n1 = sizeof(limb_t);
- while ((v & 0xff) == 0) {
- n1--;
- v >>= 8;
- }
- i++;
- len = (a->len - i) * sizeof(limb_t) + n1;
- if (len > INT32_MAX) {
- JS_ThrowInternalError(s->ctx, "bignum is too large");
- return -1;
- }
- bc_put_leb128(s, len);
- /* always saved in byte based little endian representation */
- for(j = 0; j < n1; j++) {
- bc_put_u8(s, v >> (j * 8));
- }
- for(; i < a->len; i++) {
- limb_t v = a->tab[i];
-#if LIMB_BITS == 32
- bc_put_u32(s, v);
+ bc_put_leb128(s, len);
+ if (len > 0) {
+ for(i = 0; i < (len / (JS_LIMB_BITS / 8)); i++) {
+#if JS_LIMB_BITS == 32
+ bc_put_u32(s, p->tab[i]);
#else
- bc_put_u64(s, v);
+ bc_put_u64(s, p->tab[i]);
#endif
- }
- } else {
- int bpos, d;
- uint8_t v8;
- size_t i0;
-
- /* little endian BCD */
- i = 0;
- while (i < a->len && a->tab[i] == 0)
- i++;
- assert(i < a->len);
- len = a->len * LIMB_DIGITS;
- v = a->tab[i];
- j = 0;
- while ((v % 10) == 0) {
- j++;
- v /= 10;
- }
- len -= j;
- assert(len > 0);
- if (len > INT32_MAX) {
- JS_ThrowInternalError(s->ctx, "bignum is too large");
- return -1;
- }
- bc_put_leb128(s, len);
-
- bpos = 0;
- v8 = 0;
- i0 = i;
- for(; i < a->len; i++) {
- if (i != i0) {
- v = a->tab[i];
- j = 0;
- }
- for(; j < LIMB_DIGITS; j++) {
- d = v % 10;
- v /= 10;
- if (bpos == 0) {
- v8 = d;
- bpos = 1;
- } else {
- bc_put_u8(s, v8 | (d << 4));
- bpos = 0;
- }
- }
- }
- /* flush the last digit */
- if (bpos) {
- bc_put_u8(s, v8);
- }
+ }
+ for(i = 0; i < len % (JS_LIMB_BITS / 8); i++) {
+ bc_put_u8(s, (p->tab[p->len - 1] >> (i * 8)) & 0xff);
}
}
return 0;
case JS_CLASS_STRING:
case JS_CLASS_BOOLEAN:
case JS_CLASS_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_CLASS_BIG_FLOAT:
- case JS_CLASS_BIG_DECIMAL:
-#endif
bc_put_u8(s, BC_TAG_OBJECT_VALUE);
ret = JS_WriteObjectRec(s, p->u.object_data);
break;
goto fail;
}
break;
+ case JS_TAG_SHORT_BIG_INT:
case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- case JS_TAG_BIG_DECIMAL:
-#endif
- if (JS_WriteBigNum(s, obj))
+ if (JS_WriteBigInt(s, obj))
goto fail;
break;
default:
return 0;
}
-static JSValue JS_ReadBigNum(BCReaderState *s, int tag)
+static JSValue JS_ReadBigInt(BCReaderState *s)
{
JSValue obj = JS_UNDEFINED;
+ uint32_t len, i, n;
+ JSBigInt *p;
+ js_limb_t v;
uint8_t v8;
- int32_t e;
- uint32_t len;
- limb_t l, i, n;
- JSBigFloat *p;
- limb_t v;
- bf_t *a;
-
- p = js_new_bf(s->ctx);
- if (!p)
+
+ if (bc_get_leb128(s, &len))
goto fail;
- switch(tag) {
- case BC_TAG_BIG_INT:
- obj = JS_MKPTR(JS_TAG_BIG_INT, p);
- break;
-#ifdef CONFIG_BIGNUM
- case BC_TAG_BIG_FLOAT:
- obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
- break;
- case BC_TAG_BIG_DECIMAL:
- obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
- break;
-#endif
- default:
- abort();
+ bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);
+ if (len == 0) {
+ /* zero case */
+ bc_read_trace(s, "}\n");
+ return __JS_NewShortBigInt(s->ctx, 0);
}
-
- /* sign + exponent */
- if (bc_get_sleb128(s, &e))
+ p = js_bigint_new(s->ctx,
+ (len + (JS_LIMB_BITS / 8) - 1) / (JS_LIMB_BITS / 8));
+ if (!p)
goto fail;
-
- a = &p->num;
- a->sign = e & 1;
- e >>= 1;
- if (e == 0)
- a->expn = BF_EXP_ZERO;
- else if (e == 1)
- a->expn = BF_EXP_INF;
- else if (e == 2)
- a->expn = BF_EXP_NAN;
- else if (e >= 3)
- a->expn = e - 3;
- else
- a->expn = e;
-
- /* mantissa */
- if (a->expn != BF_EXP_ZERO &&
- a->expn != BF_EXP_INF &&
- a->expn != BF_EXP_NAN) {
- if (bc_get_leb128(s, &len))
+ for(i = 0; i < len / (JS_LIMB_BITS / 8); i++) {
+#if JS_LIMB_BITS == 32
+ if (bc_get_u32(s, &v))
goto fail;
- bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);
- if (len == 0) {
- JS_ThrowInternalError(s->ctx, "invalid bignum length");
+#else
+ if (bc_get_u64(s, &v))
goto fail;
- }
-#ifdef CONFIG_BIGNUM
- if (tag == BC_TAG_BIG_DECIMAL) {
- l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS;
- } else
#endif
- {
- l = (len + sizeof(limb_t) - 1) / sizeof(limb_t);
- }
- if (bf_resize(a, l)) {
- JS_ThrowOutOfMemory(s->ctx);
- goto fail;
+ p->tab[i] = v;
+ }
+ n = len % (JS_LIMB_BITS / 8);
+ if (n != 0) {
+ int shift;
+ v = 0;
+ for(i = 0; i < n; i++) {
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ v |= (js_limb_t)v8 << (i * 8);
}
-#ifdef CONFIG_BIGNUM
- if (tag == BC_TAG_BIG_DECIMAL) {
- limb_t j;
- int bpos, d;
-
- bpos = 0;
- for(i = 0; i < l; i++) {
- if (i == 0 && (n = len % LIMB_DIGITS) != 0) {
- j = LIMB_DIGITS - n;
- } else {
- j = 0;
- }
- v = 0;
- for(; j < LIMB_DIGITS; j++) {
- if (bpos == 0) {
- if (bc_get_u8(s, &v8))
- goto fail;
- d = v8 & 0xf;
- bpos = 1;
- } else {
- d = v8 >> 4;
- bpos = 0;
- }
- if (d >= 10) {
- JS_ThrowInternalError(s->ctx, "invalid digit");
- goto fail;
- }
- v += mp_pow_dec[j] * d;
- }
- a->tab[i] = v;
- }
- } else
-#endif /* CONFIG_BIGNUM */
- {
- n = len & (sizeof(limb_t) - 1);
- if (n != 0) {
- v = 0;
- for(i = 0; i < n; i++) {
- if (bc_get_u8(s, &v8))
- goto fail;
- v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8);
- }
- a->tab[0] = v;
- i = 1;
- } else {
- i = 0;
- }
- for(; i < l; i++) {
-#if LIMB_BITS == 32
- if (bc_get_u32(s, &v))
- goto fail;
-#else
- if (bc_get_u64(s, &v))
- goto fail;
-#endif
- a->tab[i] = v;
- }
+ shift = JS_LIMB_BITS - n * 8;
+ /* extend the sign */
+ if (shift != 0) {
+ v = (js_slimb_t)(v << shift) >> shift;
}
+ p->tab[p->len - 1] = v;
}
bc_read_trace(s, "}\n");
- return obj;
+ return JS_CompactBigInt(s->ctx, p);
fail:
JS_FreeValue(s->ctx, obj);
return JS_EXCEPTION;
obj = JS_ReadObjectValue(s);
break;
case BC_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case BC_TAG_BIG_FLOAT:
- case BC_TAG_BIG_DECIMAL:
-#endif
- obj = JS_ReadBigNum(s, tag);
+ obj = JS_ReadBigInt(s);
break;
case BC_TAG_OBJECT_REFERENCE:
{
case JS_TAG_OBJECT:
case JS_TAG_EXCEPTION:
return JS_DupValue(ctx, val);
+ case JS_TAG_SHORT_BIG_INT:
case JS_TAG_BIG_INT:
obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);
goto set_value;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT);
- goto set_value;
- case JS_TAG_BIG_DECIMAL:
- obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL);
- goto set_value;
-#endif
case JS_TAG_INT:
case JS_TAG_FLOAT64:
obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
if (JS_IsException(val))
return val;
switch(JS_VALUE_GET_TAG(val)) {
+ case JS_TAG_SHORT_BIG_INT:
+ val = JS_NewInt64(ctx, JS_VALUE_GET_SHORT_BIG_INT(val));
+ if (JS_IsException(val))
+ return val;
+ break;
case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
-#endif
{
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ JSBigInt *p = JS_VALUE_GET_PTR(val);
double d;
- bf_get_float64(&p->num, &d, BF_RNDN);
+ d = js_bigint_to_float64(ctx, p);
JS_FreeValue(ctx, val);
- val = __JS_NewFloat64(ctx, d);
+ val = JS_NewFloat64(ctx, d);
}
break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_DECIMAL:
- val = JS_ToStringFree(ctx, val);
- if (JS_IsException(val))
- return val;
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val))
- return val;
- break;
-#endif
default:
break;
}
/* check for object.toJSON method */
/* ECMA specifies this is done only for Object and BigInt */
/* we do it for BigFloat and BigDecimal as an extension */
- if (JS_IsObject(val) || JS_IsBigInt(ctx, val)
-#ifdef CONFIG_BIGNUM
- || JS_IsBigFloat(val) || JS_IsBigDecimal(val)
-#endif
- ) {
+ if (JS_IsObject(val) || JS_IsBigInt(ctx, val)) {
JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);
if (JS_IsException(f))
goto exception;
case JS_TAG_FLOAT64:
case JS_TAG_BOOL:
case JS_TAG_NULL:
+ case JS_TAG_SHORT_BIG_INT:
case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- case JS_TAG_BIG_DECIMAL:
-#endif
case JS_TAG_EXCEPTION:
return val;
default:
if (JS_IsException(val))
goto exception;
goto concat_primitive;
- } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT
-#ifdef CONFIG_BIGNUM
- || cl == JS_CLASS_BIG_FLOAT
- || cl == JS_CLASS_BIG_DECIMAL
-#endif
- )
+ } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT)
{
/* This will thow the same error as for the primitive object */
set_value(ctx, &val, JS_DupValue(ctx, p->u.object_data));
case JS_TAG_NULL:
concat_value:
return string_buffer_concat_value_free(jsc->b, val);
+ case JS_TAG_SHORT_BIG_INT:
case JS_TAG_BIG_INT:
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
- case JS_TAG_BIG_DECIMAL:
-#endif
/* reject big numbers: use toJSON method to override */
JS_ThrowTypeError(ctx, "Do not know how to serialize a BigInt");
goto exception;
for (i = 0, len = p->len; i < len; i++) {
c = string_get(p, i);
if (isUnescaped(c)) {
- string_buffer_putc16(b, c);
- } else {
- encodeURI_hex(b, c);
- }
- }
- JS_FreeValue(ctx, str);
- return string_buffer_end(b);
-}
-
-static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSValue str;
- StringBuffer b_s, *b = &b_s;
- JSString *p;
- int i, len, c, n;
-
- str = JS_ToString(ctx, argv[0]);
- if (JS_IsException(str))
- return str;
-
- string_buffer_init(ctx, b, 0);
- p = JS_VALUE_GET_STRING(str);
- for (i = 0, len = p->len; i < len; i++) {
- c = string_get(p, i);
- if (c == '%') {
- if (i + 6 <= len
- && string_get(p, i + 1) == 'u'
- && (n = string_get_hex(p, i + 2, 4)) >= 0) {
- c = n;
- i += 6 - 1;
- } else
- if (i + 3 <= len
- && (n = string_get_hex(p, i + 1, 2)) >= 0) {
- c = n;
- i += 3 - 1;
- }
- }
- string_buffer_putc16(b, c);
- }
- JS_FreeValue(ctx, str);
- return string_buffer_end(b);
-}
-
-/* global object */
-
-static const JSCFunctionListEntry js_global_funcs[] = {
- JS_CFUNC_DEF("parseInt", 2, js_parseInt ),
- JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),
- JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
- JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
-
- JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),
- JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),
- JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),
- JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),
- JS_CFUNC_DEF("escape", 1, js_global_escape ),
- JS_CFUNC_DEF("unescape", 1, js_global_unescape ),
- JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
- JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
- JS_PROP_UNDEFINED_DEF("undefined", 0 ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ),
-};
-
-/* Date */
-
-static int64_t math_mod(int64_t a, int64_t b) {
- /* return positive modulo */
- int64_t m = a % b;
- return m + (m < 0) * b;
-}
-
-static int64_t floor_div(int64_t a, int64_t b) {
- /* integer division rounding toward -Infinity */
- int64_t m = a % b;
- return (a - (m + (m < 0) * b)) / b;
-}
-
-static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv);
-
-static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
-{
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))
- return JS_ToFloat64(ctx, valp, p->u.object_data);
- }
- JS_ThrowTypeError(ctx, "not a Date object");
- return -1;
-}
-
-static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)
-{
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id == JS_CLASS_DATE) {
- JS_FreeValue(ctx, p->u.object_data);
- p->u.object_data = JS_NewFloat64(ctx, v);
- return JS_DupValue(ctx, p->u.object_data);
- }
- }
- return JS_ThrowTypeError(ctx, "not a Date object");
-}
-
-static int64_t days_from_year(int64_t y) {
- return 365 * (y - 1970) + floor_div(y - 1969, 4) -
- floor_div(y - 1901, 100) + floor_div(y - 1601, 400);
-}
-
-static int64_t days_in_year(int64_t y) {
- return 365 + !(y % 4) - !(y % 100) + !(y % 400);
-}
-
-/* return the year, update days */
-static int64_t year_from_days(int64_t *days) {
- int64_t y, d1, nd, d = *days;
- y = floor_div(d * 10000, 3652425) + 1970;
- /* the initial approximation is very good, so only a few
- iterations are necessary */
- for(;;) {
- d1 = d - days_from_year(y);
- if (d1 < 0) {
- y--;
- d1 += days_in_year(y);
- } else {
- nd = days_in_year(y);
- if (d1 < nd)
- break;
- d1 -= nd;
- y++;
- }
- }
- *days = d1;
- return y;
-}
-
-static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
-static char const day_names[] = "SunMonTueWedThuFriSat";
-
-static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
- double fields[minimum_length(9)], int is_local, int force)
-{
- double dval;
- int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
-
- if (JS_ThisTimeValue(ctx, &dval, obj))
- return -1;
-
- if (isnan(dval)) {
- if (!force)
- return FALSE; /* NaN */
- d = 0; /* initialize all fields to 0 */
- } else {
- d = dval; /* assuming -8.64e15 <= dval <= -8.64e15 */
- if (is_local) {
- tz = -getTimezoneOffset(d);
- d += tz * 60000;
- }
- }
-
- /* result is >= 0, we can use % */
- h = math_mod(d, 86400000);
- days = (d - h) / 86400000;
- ms = h % 1000;
- h = (h - ms) / 1000;
- s = h % 60;
- h = (h - s) / 60;
- m = h % 60;
- h = (h - m) / 60;
- wd = math_mod(days + 4, 7); /* week day */
- y = year_from_days(&days);
-
- for(i = 0; i < 11; i++) {
- md = month_days[i];
- if (i == 1)
- md += days_in_year(y) - 365;
- if (days < md)
- break;
- days -= md;
- }
- fields[0] = y;
- fields[1] = i;
- fields[2] = days + 1;
- fields[3] = h;
- fields[4] = m;
- fields[5] = s;
- fields[6] = ms;
- fields[7] = wd;
- fields[8] = tz;
- return TRUE;
-}
-
-static double time_clip(double t) {
- if (t >= -8.64e15 && t <= 8.64e15)
- return trunc(t) + 0.0; /* convert -0 to +0 */
- else
- return NAN;
-}
-
-/* The spec mandates the use of 'double' and it specifies the order
- of the operations */
-static double set_date_fields(double fields[minimum_length(7)], int is_local) {
- double y, m, dt, ym, mn, day, h, s, milli, time, tv;
- int yi, mi, i;
- int64_t days;
- volatile double temp; /* enforce evaluation order */
-
- /* emulate 21.4.1.15 MakeDay ( year, month, date ) */
- y = fields[0];
- m = fields[1];
- dt = fields[2];
- ym = y + floor(m / 12);
- mn = fmod(m, 12);
- if (mn < 0)
- mn += 12;
- if (ym < -271821 || ym > 275760)
- return NAN;
-
- yi = ym;
- mi = mn;
- days = days_from_year(yi);
- for(i = 0; i < mi; i++) {
- days += month_days[i];
- if (i == 1)
- days += days_in_year(yi) - 365;
- }
- day = days + dt - 1;
-
- /* emulate 21.4.1.14 MakeTime ( hour, min, sec, ms ) */
- h = fields[3];
- m = fields[4];
- s = fields[5];
- milli = fields[6];
- /* Use a volatile intermediary variable to ensure order of evaluation
- * as specified in ECMA. This fixes a test262 error on
- * test262/test/built-ins/Date/UTC/fp-evaluation-order.js.
- * Without the volatile qualifier, the compile can generate code
- * that performs the computation in a different order or with instructions
- * that produce a different result such as FMA (float multiply and add).
- */
- time = h * 3600000;
- time += (temp = m * 60000);
- time += (temp = s * 1000);
- time += milli;
-
- /* emulate 21.4.1.16 MakeDate ( day, time ) */
- tv = (temp = day * 86400000) + time; /* prevent generation of FMA */
- if (!isfinite(tv))
- return NAN;
-
- /* adjust for local time and clip */
- if (is_local) {
- int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv;
- tv += getTimezoneOffset(ti) * 60000;
- }
- return time_clip(tv);
-}
-
-static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
-{
- // get_date_field(obj, n, is_local)
- double fields[9];
- int res, n, is_local;
-
- is_local = magic & 0x0F;
- n = (magic >> 4) & 0x0F;
- res = get_date_fields(ctx, this_val, fields, is_local, 0);
- if (res < 0)
- return JS_EXCEPTION;
- if (!res)
- return JS_NAN;
-
- if (magic & 0x100) { // getYear
- fields[0] -= 1900;
- }
- return JS_NewFloat64(ctx, fields[n]);
-}
-
-static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
-{
- // _field(obj, first_field, end_field, args, is_local)
- double fields[9];
- int res, first_field, end_field, is_local, i, n;
- double d, a;
-
- d = NAN;
- first_field = (magic >> 8) & 0x0F;
- end_field = (magic >> 4) & 0x0F;
- is_local = magic & 0x0F;
-
- res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);
- if (res < 0)
- return JS_EXCEPTION;
-
- // Argument coercion is observable and must be done unconditionally.
- n = min_int(argc, end_field - first_field);
- for(i = 0; i < n; i++) {
- if (JS_ToFloat64(ctx, &a, argv[i]))
- return JS_EXCEPTION;
- if (!isfinite(a))
- res = FALSE;
- fields[first_field + i] = trunc(a);
- }
- if (res && argc > 0)
- d = set_date_fields(fields, is_local);
-
- return JS_SetThisTimeValue(ctx, this_val, d);
-}
-
-/* fmt:
- 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"
- 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"
- 2: toISOString: "2018-01-02T23:02:56.927Z"
- 3: toLocaleString: "1/2/2018, 11:40:40 PM"
- part: 1=date, 2=time 3=all
- XXX: should use a variant of strftime().
- */
-static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
-{
- // _string(obj, fmt, part)
- char buf[64];
- double fields[9];
- int res, fmt, part, pos;
- int y, mon, d, h, m, s, ms, wd, tz;
-
- fmt = (magic >> 4) & 0x0F;
- part = magic & 0x0F;
-
- res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);
- if (res < 0)
- return JS_EXCEPTION;
- if (!res) {
- if (fmt == 2)
- return JS_ThrowRangeError(ctx, "Date value is NaN");
- else
- return JS_NewString(ctx, "Invalid Date");
- }
-
- y = fields[0];
- mon = fields[1];
- d = fields[2];
- h = fields[3];
- m = fields[4];
- s = fields[5];
- ms = fields[6];
- wd = fields[7];
- tz = fields[8];
-
- pos = 0;
-
- if (part & 1) { /* date part */
- switch(fmt) {
- case 0:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%.3s, %02d %.3s %0*d ",
- day_names + wd * 3, d,
- month_names + mon * 3, 4 + (y < 0), y);
- break;
- case 1:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%.3s %.3s %02d %0*d",
- day_names + wd * 3,
- month_names + mon * 3, d, 4 + (y < 0), y);
- if (part == 3) {
- buf[pos++] = ' ';
- }
- break;
- case 2:
- if (y >= 0 && y <= 9999) {
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%04d", y);
- } else {
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%+07d", y);
- }
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "-%02d-%02dT", mon + 1, d);
- break;
- case 3:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);
- if (part == 3) {
- buf[pos++] = ',';
- buf[pos++] = ' ';
- }
- break;
- }
- }
- if (part & 2) { /* time part */
- switch(fmt) {
- case 0:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%02d:%02d:%02d GMT", h, m, s);
- break;
- case 1:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%02d:%02d:%02d GMT", h, m, s);
- if (tz < 0) {
- buf[pos++] = '-';
- tz = -tz;
- } else {
- buf[pos++] = '+';
- }
- /* tz is >= 0, can use % */
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%02d%02d", tz / 60, tz % 60);
- /* XXX: tack the time zone code? */
- break;
- case 2:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%02d:%02d:%02d.%03dZ", h, m, s, ms);
- break;
- case 3:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s,
- (h < 12) ? 'A' : 'P');
- break;
- }
- }
- return JS_NewStringLen(ctx, buf, pos);
-}
-
-/* OS dependent: return the UTC time in ms since 1970. */
-static int64_t date_now(void) {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
-}
-
-static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
-{
- // Date(y, mon, d, h, m, s, ms)
- JSValue rv;
- int i, n;
- double a, val;
-
- if (JS_IsUndefined(new_target)) {
- /* invoked as function */
- argc = 0;
- }
- n = argc;
- if (n == 0) {
- val = date_now();
- } else if (n == 1) {
- JSValue v, dv;
- if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
- if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {
- if (JS_ToFloat64(ctx, &val, p->u.object_data))
- return JS_EXCEPTION;
- val = time_clip(val);
- goto has_val;
- }
- }
- v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);
- if (JS_IsString(v)) {
- dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);
- JS_FreeValue(ctx, v);
- if (JS_IsException(dv))
- return JS_EXCEPTION;
- if (JS_ToFloat64Free(ctx, &val, dv))
- return JS_EXCEPTION;
- } else {
- if (JS_ToFloat64Free(ctx, &val, v))
- return JS_EXCEPTION;
- }
- val = time_clip(val);
- } else {
- double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
- if (n > 7)
- n = 7;
- for(i = 0; i < n; i++) {
- if (JS_ToFloat64(ctx, &a, argv[i]))
- return JS_EXCEPTION;
- if (!isfinite(a))
- break;
- fields[i] = trunc(a);
- if (i == 0 && fields[0] >= 0 && fields[0] < 100)
- fields[0] += 1900;
- }
- val = (i == n) ? set_date_fields(fields, 1) : NAN;
- }
-has_val:
-#if 0
- JSValueConst args[3];
- args[0] = new_target;
- args[1] = ctx->class_proto[JS_CLASS_DATE];
- args[2] = JS_NewFloat64(ctx, val);
- rv = js___date_create(ctx, JS_UNDEFINED, 3, args);
-#else
- rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);
- if (!JS_IsException(rv))
- JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val));
-#endif
- if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {
- /* invoked as a function, return (new Date()).toString(); */
- JSValue s;
- s = get_date_string(ctx, rv, 0, NULL, 0x13);
- JS_FreeValue(ctx, rv);
- rv = s;
- }
- return rv;
-}
-
-static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- // UTC(y, mon, d, h, m, s, ms)
- double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
- int i, n;
- double a;
-
- n = argc;
- if (n == 0)
- return JS_NAN;
- if (n > 7)
- n = 7;
- for(i = 0; i < n; i++) {
- if (JS_ToFloat64(ctx, &a, argv[i]))
- return JS_EXCEPTION;
- if (!isfinite(a))
- return JS_NAN;
- fields[i] = trunc(a);
- if (i == 0 && fields[0] >= 0 && fields[0] < 100)
- fields[0] += 1900;
- }
- return JS_NewFloat64(ctx, set_date_fields(fields, 0));
-}
-
-/* Date string parsing */
-
-static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) {
- if (sp[*pp] == c) {
- *pp += 1;
- return TRUE;
- } else {
- return FALSE;
- }
-}
-
-/* skip spaces, update offset, return next char */
-static int string_skip_spaces(const uint8_t *sp, int *pp) {
- int c;
- while ((c = sp[*pp]) == ' ')
- *pp += 1;
- return c;
-}
-
-/* skip dashes dots and commas */
-static int string_skip_separators(const uint8_t *sp, int *pp) {
- int c;
- while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',')
- *pp += 1;
- return c;
-}
-
-/* skip a word, stop on spaces, digits and separators, update offset */
-static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) {
- int c;
- while (!strchr(stoplist, c = sp[*pp]))
- *pp += 1;
- return c;
-}
-
-/* parse a numeric field (max_digits = 0 -> no maximum) */
-static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval,
- int min_digits, int max_digits)
-{
- int v = 0;
- int c, p = *pp, p_start;
-
- p_start = p;
- while ((c = sp[p]) >= '0' && c <= '9') {
- /* arbitrary limit to 9 digits */
- if (v >= 100000000)
- return FALSE;
- v = v * 10 + c - '0';
- p++;
- if (p - p_start == max_digits)
- break;
- }
- if (p - p_start < min_digits)
- return FALSE;
- *pval = v;
- *pp = p;
- return TRUE;
-}
-
-static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) {
- /* parse optional fractional part as milliseconds and truncate. */
- /* spec does not indicate which rounding should be used */
- int mul = 100, ms = 0, c, p_start, p = *pp;
-
- c = sp[p];
- if (c == '.' || c == ',') {
- p++;
- p_start = p;
- while ((c = sp[p]) >= '0' && c <= '9') {
- ms += (c - '0') * mul;
- mul /= 10;
- p++;
- if (p - p_start == 9)
- break;
- }
- if (p > p_start) {
- /* only consume the separator if digits are present */
- *pval = ms;
- *pp = p;
- }
- }
- return TRUE;
-}
-
-static uint8_t upper_ascii(uint8_t c) {
- return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c;
-}
-
-static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) {
- int tz = 0, sgn, hh, mm, p = *pp;
-
- sgn = sp[p++];
- if (sgn == '+' || sgn == '-') {
- int n = p;
- if (!string_get_digits(sp, &p, &hh, 1, 0))
- return FALSE;
- n = p - n;
- if (strict && n != 2 && n != 4)
- return FALSE;
- while (n > 4) {
- n -= 2;
- hh /= 100;
- }
- if (n > 2) {
- mm = hh % 100;
- hh = hh / 100;
- } else {
- mm = 0;
- if (string_skip_char(sp, &p, ':') /* optional separator */
- && !string_get_digits(sp, &p, &mm, 2, 2))
- return FALSE;
- }
- if (hh > 23 || mm > 59)
- return FALSE;
- tz = hh * 60 + mm;
- if (sgn != '+')
- tz = -tz;
- } else
- if (sgn != 'Z') {
- return FALSE;
- }
- *pp = p;
- *tzp = tz;
- return TRUE;
-}
-
-static BOOL string_match(const uint8_t *sp, int *pp, const char *s) {
- int p = *pp;
- while (*s != '\0') {
- if (upper_ascii(sp[p]) != upper_ascii(*s++))
- return FALSE;
- p++;
- }
- *pp = p;
- return TRUE;
-}
-
-static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) {
- int n, i;
-
- for (n = 0; n < count; n++) {
- for (i = 0;; i++) {
- if (upper_ascii(sp[p + i]) != upper_ascii(list[n * 3 + i]))
- break;
- if (i == 2)
- return n;
- }
- }
- return -1;
-}
-
-static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) {
- int n;
-
- n = find_abbrev(sp, *pp, month_names, 12);
- if (n < 0)
- return FALSE;
-
- *pval = n + 1;
- *pp += 3;
- return TRUE;
-}
-
-/* parse toISOString format */
-static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) {
- int sgn, i, p = 0;
-
- /* initialize fields to the beginning of the Epoch */
- for (i = 0; i < 9; i++) {
- fields[i] = (i == 2);
- }
- *is_local = FALSE;
-
- /* year is either yyyy digits or [+-]yyyyyy */
- sgn = sp[p];
- if (sgn == '-' || sgn == '+') {
- p++;
- if (!string_get_digits(sp, &p, &fields[0], 6, 6))
- return FALSE;
- if (sgn == '-') {
- if (fields[0] == 0)
- return FALSE; // reject -000000
- fields[0] = -fields[0];
- }
- } else {
- if (!string_get_digits(sp, &p, &fields[0], 4, 4))
- return FALSE;
- }
- if (string_skip_char(sp, &p, '-')) {
- if (!string_get_digits(sp, &p, &fields[1], 2, 2)) /* month */
- return FALSE;
- if (fields[1] < 1)
- return FALSE;
- fields[1] -= 1;
- if (string_skip_char(sp, &p, '-')) {
- if (!string_get_digits(sp, &p, &fields[2], 2, 2)) /* day */
- return FALSE;
- if (fields[2] < 1)
- return FALSE;
- }
- }
- if (string_skip_char(sp, &p, 'T')) {
- *is_local = TRUE;
- if (!string_get_digits(sp, &p, &fields[3], 2, 2) /* hour */
- || !string_skip_char(sp, &p, ':')
- || !string_get_digits(sp, &p, &fields[4], 2, 2)) { /* minute */
- fields[3] = 100; // reject unconditionally
- return TRUE;
- }
- if (string_skip_char(sp, &p, ':')) {
- if (!string_get_digits(sp, &p, &fields[5], 2, 2)) /* second */
- return FALSE;
- string_get_milliseconds(sp, &p, &fields[6]);
- }
- }
- /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */
- if (sp[p]) {
- *is_local = FALSE;
- if (!string_get_tzoffset(sp, &p, &fields[8], TRUE))
- return FALSE;
- }
- /* error if extraneous characters */
- return sp[p] == '\0';
-}
-
-static struct {
- char name[6];
- int16_t offset;
-} const js_tzabbr[] = {
- { "GMT", 0 }, // Greenwich Mean Time
- { "UTC", 0 }, // Coordinated Universal Time
- { "UT", 0 }, // Universal Time
- { "Z", 0 }, // Zulu Time
- { "EDT", -4 * 60 }, // Eastern Daylight Time
- { "EST", -5 * 60 }, // Eastern Standard Time
- { "CDT", -5 * 60 }, // Central Daylight Time
- { "CST", -6 * 60 }, // Central Standard Time
- { "MDT", -6 * 60 }, // Mountain Daylight Time
- { "MST", -7 * 60 }, // Mountain Standard Time
- { "PDT", -7 * 60 }, // Pacific Daylight Time
- { "PST", -8 * 60 }, // Pacific Standard Time
- { "WET", +0 * 60 }, // Western European Time
- { "WEST", +1 * 60 }, // Western European Summer Time
- { "CET", +1 * 60 }, // Central European Time
- { "CEST", +2 * 60 }, // Central European Summer Time
- { "EET", +2 * 60 }, // Eastern European Time
- { "EEST", +3 * 60 }, // Eastern European Summer Time
-};
-
-static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) {
- for (size_t i = 0; i < countof(js_tzabbr); i++) {
- if (string_match(sp, pp, js_tzabbr[i].name)) {
- *offset = js_tzabbr[i].offset;
- return TRUE;
- }
- }
- return FALSE;
-}
-
-/* parse toString, toUTCString and other formats */
-static BOOL js_date_parse_otherstring(const uint8_t *sp,
- int fields[minimum_length(9)],
- BOOL *is_local) {
- int c, i, val, p = 0, p_start;
- int num[3];
- BOOL has_year = FALSE;
- BOOL has_mon = FALSE;
- BOOL has_time = FALSE;
- int num_index = 0;
-
- /* initialize fields to the beginning of 2001-01-01 */
- fields[0] = 2001;
- fields[1] = 1;
- fields[2] = 1;
- for (i = 3; i < 9; i++) {
- fields[i] = 0;
- }
- *is_local = TRUE;
-
- while (string_skip_spaces(sp, &p)) {
- p_start = p;
- if ((c = sp[p]) == '+' || c == '-') {
- if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) {
- *is_local = FALSE;
- } else {
- p++;
- if (string_get_digits(sp, &p, &val, 1, 0)) {
- if (c == '-') {
- if (val == 0)
- return FALSE;
- val = -val;
- }
- fields[0] = val;
- has_year = TRUE;
- }
- }
- } else
- if (string_get_digits(sp, &p, &val, 1, 0)) {
- if (string_skip_char(sp, &p, ':')) {
- /* time part */
- fields[3] = val;
- if (!string_get_digits(sp, &p, &fields[4], 1, 2))
- return FALSE;
- if (string_skip_char(sp, &p, ':')) {
- if (!string_get_digits(sp, &p, &fields[5], 1, 2))
- return FALSE;
- string_get_milliseconds(sp, &p, &fields[6]);
- }
- has_time = TRUE;
- } else {
- if (p - p_start > 2) {
- fields[0] = val;
- has_year = TRUE;
- } else
- if (val < 1 || val > 31) {
- fields[0] = val + (val < 100) * 1900 + (val < 50) * 100;
- has_year = TRUE;
- } else {
- if (num_index == 3)
- return FALSE;
- num[num_index++] = val;
- }
- }
- } else
- if (string_get_month(sp, &p, &fields[1])) {
- has_mon = TRUE;
- string_skip_until(sp, &p, "0123456789 -/(");
- } else
- if (has_time && string_match(sp, &p, "PM")) {
- if (fields[3] < 12)
- fields[3] += 12;
- continue;
- } else
- if (has_time && string_match(sp, &p, "AM")) {
- if (fields[3] == 12)
- fields[3] -= 12;
- continue;
- } else
- if (string_get_tzabbr(sp, &p, &fields[8])) {
- *is_local = FALSE;
- continue;
- } else
- if (c == '(') { /* skip parenthesized phrase */
- int level = 0;
- while ((c = sp[p]) != '\0') {
- p++;
- level += (c == '(');
- level -= (c == ')');
- if (!level)
- break;
- }
- if (level > 0)
- return FALSE;
- } else
- if (c == ')') {
- return FALSE;
- } else {
- if (has_year + has_mon + has_time + num_index)
- return FALSE;
- /* skip a word */
- string_skip_until(sp, &p, " -/(");
- }
- string_skip_separators(sp, &p);
- }
- if (num_index + has_year + has_mon > 3)
- return FALSE;
-
- switch (num_index) {
- case 0:
- if (!has_year)
- return FALSE;
- break;
- case 1:
- if (has_mon)
- fields[2] = num[0];
- else
- fields[1] = num[0];
- break;
- case 2:
- if (has_year) {
- fields[1] = num[0];
- fields[2] = num[1];
- } else
- if (has_mon) {
- fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100;
- fields[2] = num[0];
- } else {
- fields[1] = num[0];
- fields[2] = num[1];
- }
- break;
- case 3:
- fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100;
- fields[1] = num[0];
- fields[2] = num[1];
- break;
- default:
- return FALSE;
- }
- if (fields[1] < 1 || fields[2] < 1)
- return FALSE;
- fields[1] -= 1;
- return TRUE;
-}
-
-static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSValue s, rv;
- int fields[9];
- double fields1[9];
- double d;
- int i, c;
- JSString *sp;
- uint8_t buf[128];
- BOOL is_local;
-
- rv = JS_NAN;
-
- s = JS_ToString(ctx, argv[0]);
- if (JS_IsException(s))
- return JS_EXCEPTION;
-
- sp = JS_VALUE_GET_STRING(s);
- /* convert the string as a byte array */
- for (i = 0; i < sp->len && i < (int)countof(buf) - 1; i++) {
- c = string_get(sp, i);
- if (c > 255)
- c = (c == 0x2212) ? '-' : 'x';
- buf[i] = c;
- }
- buf[i] = '\0';
- if (js_date_parse_isostring(buf, fields, &is_local)
- || js_date_parse_otherstring(buf, fields, &is_local)) {
- static int const field_max[6] = { 0, 11, 31, 24, 59, 59 };
- BOOL valid = TRUE;
- /* check field maximum values */
- for (i = 1; i < 6; i++) {
- if (fields[i] > field_max[i])
- valid = FALSE;
- }
- /* special case 24:00:00.000 */
- if (fields[3] == 24 && (fields[4] | fields[5] | fields[6]))
- valid = FALSE;
- if (valid) {
- for(i = 0; i < 7; i++)
- fields1[i] = fields[i];
- d = set_date_fields(fields1, is_local) - fields[8] * 60000;
- rv = JS_NewFloat64(ctx, d);
+ string_buffer_putc16(b, c);
+ } else {
+ encodeURI_hex(b, c);
}
}
- JS_FreeValue(ctx, s);
- return rv;
-}
-
-static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- // now()
- return JS_NewInt64(ctx, date_now());
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
}
-static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
+static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
- // Symbol_toPrimitive(hint)
- JSValueConst obj = this_val;
- JSAtom hint = JS_ATOM_NULL;
- int hint_num;
+ JSValue str;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p;
+ int i, len, c, n;
- if (!JS_IsObject(obj))
- return JS_ThrowTypeErrorNotAnObject(ctx);
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return str;
- if (JS_IsString(argv[0])) {
- hint = JS_ValueToAtom(ctx, argv[0]);
- if (hint == JS_ATOM_NULL)
- return JS_EXCEPTION;
- JS_FreeAtom(ctx, hint);
- }
- switch (hint) {
- case JS_ATOM_number:
- case JS_ATOM_integer:
- hint_num = HINT_NUMBER;
- break;
- case JS_ATOM_string:
- case JS_ATOM_default:
- hint_num = HINT_STRING;
- break;
- default:
- return JS_ThrowTypeError(ctx, "invalid hint");
+ string_buffer_init(ctx, b, 0);
+ p = JS_VALUE_GET_STRING(str);
+ for (i = 0, len = p->len; i < len; i++) {
+ c = string_get(p, i);
+ if (c == '%') {
+ if (i + 6 <= len
+ && string_get(p, i + 1) == 'u'
+ && (n = string_get_hex(p, i + 2, 4)) >= 0) {
+ c = n;
+ i += 6 - 1;
+ } else
+ if (i + 3 <= len
+ && (n = string_get_hex(p, i + 1, 2)) >= 0) {
+ c = n;
+ i += 3 - 1;
+ }
+ }
+ string_buffer_putc16(b, c);
}
- return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
}
-static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- // getTimezoneOffset()
- double v;
+/* global object */
- if (JS_ThisTimeValue(ctx, &v, this_val))
- return JS_EXCEPTION;
- if (isnan(v))
- return JS_NAN;
- else
- /* assuming -8.64e15 <= v <= -8.64e15 */
- return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));
-}
+static const JSCFunctionListEntry js_global_funcs[] = {
+ JS_CFUNC_DEF("parseInt", 2, js_parseInt ),
+ JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),
+ JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
+ JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
-static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- // getTime()
- double v;
+ JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),
+ JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),
+ JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),
+ JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),
+ JS_CFUNC_DEF("escape", 1, js_global_escape ),
+ JS_CFUNC_DEF("unescape", 1, js_global_unescape ),
+ JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
+ JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
+ JS_PROP_UNDEFINED_DEF("undefined", 0 ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ),
+};
- if (JS_ThisTimeValue(ctx, &v, this_val))
- return JS_EXCEPTION;
- return JS_NewFloat64(ctx, v);
-}
+/* Date */
-static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- // setTime(v)
- double v;
+static int64_t math_mod(int64_t a, int64_t b) {
+ /* return positive modulo */
+ int64_t m = a % b;
+ return m + (m < 0) * b;
+}
- if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))
- return JS_EXCEPTION;
- return JS_SetThisTimeValue(ctx, this_val, time_clip(v));
+static int64_t floor_div(int64_t a, int64_t b) {
+ /* integer division rounding toward -Infinity */
+ int64_t m = a % b;
+ return (a - (m + (m < 0) * b)) / b;
}
-static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- // setYear(y)
- double y;
- JSValueConst args[1];
+static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
- if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))
- return JS_EXCEPTION;
- y = +y;
- if (isfinite(y)) {
- y = trunc(y);
- if (y >= 0 && y < 100)
- y += 1900;
+static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
+{
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))
+ return JS_ToFloat64(ctx, valp, p->u.object_data);
}
- args[0] = JS_NewFloat64(ctx, y);
- return set_date_field(ctx, this_val, 1, args, 0x011);
+ JS_ThrowTypeError(ctx, "not a Date object");
+ return -1;
}
-static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
+static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)
{
- // toJSON(key)
- JSValue obj, tv, method, rv;
- double d;
-
- rv = JS_EXCEPTION;
- tv = JS_UNDEFINED;
-
- obj = JS_ToObject(ctx, this_val);
- tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);
- if (JS_IsException(tv))
- goto exception;
- if (JS_IsNumber(tv)) {
- if (JS_ToFloat64(ctx, &d, tv) < 0)
- goto exception;
- if (!isfinite(d)) {
- rv = JS_NULL;
- goto done;
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_DATE) {
+ JS_FreeValue(ctx, p->u.object_data);
+ p->u.object_data = JS_NewFloat64(ctx, v);
+ return JS_DupValue(ctx, p->u.object_data);
}
}
- method = JS_GetPropertyStr(ctx, obj, "toISOString");
- if (JS_IsException(method))
- goto exception;
- if (!JS_IsFunction(ctx, method)) {
- JS_ThrowTypeError(ctx, "object needs toISOString method");
- JS_FreeValue(ctx, method);
- goto exception;
- }
- rv = JS_CallFree(ctx, method, obj, 0, NULL);
-exception:
-done:
- JS_FreeValue(ctx, obj);
- JS_FreeValue(ctx, tv);
- return rv;
+ return JS_ThrowTypeError(ctx, "not a Date object");
}
-static const JSCFunctionListEntry js_date_funcs[] = {
- JS_CFUNC_DEF("now", 0, js_Date_now ),
- JS_CFUNC_DEF("parse", 1, js_Date_parse ),
- JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),
-};
-
-static const JSCFunctionListEntry js_date_proto_funcs[] = {
- JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),
- JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),
- JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),
- JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),
- JS_ALIAS_DEF("toGMTString", "toUTCString" ),
- JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),
- JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),
- JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),
- JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),
- JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),
- JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),
- JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),
- JS_CFUNC_DEF("getTime", 0, js_date_getTime ),
- JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),
- JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),
- JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),
- JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),
- JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),
- JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),
- JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),
- JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),
- JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),
- JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),
- JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),
- JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),
- JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),
- JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),
- JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),
- JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),
- JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),
- JS_CFUNC_DEF("setTime", 1, js_date_setTime ),
- JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),
- JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),
- JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),
- JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),
- JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),
- JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),
- JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),
- JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),
- JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),
- JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),
- JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),
- JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),
- JS_CFUNC_DEF("setYear", 1, js_date_setYear ),
- JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),
- JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),
- JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),
-};
-
-JSValue JS_NewDate(JSContext *ctx, double epoch_ms)
-{
- JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- JS_SetObjectData(ctx, obj, __JS_NewFloat64(ctx, time_clip(epoch_ms)));
- return obj;
+static int64_t days_from_year(int64_t y) {
+ return 365 * (y - 1970) + floor_div(y - 1969, 4) -
+ floor_div(y - 1901, 100) + floor_div(y - 1601, 400);
}
-void JS_AddIntrinsicDate(JSContext *ctx)
-{
- JSValueConst obj;
-
- /* Date */
- ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx);
- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs,
- countof(js_date_proto_funcs));
- obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7,
- ctx->class_proto[JS_CLASS_DATE]);
- JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs));
+static int64_t days_in_year(int64_t y) {
+ return 365 + !(y % 4) - !(y % 100) + !(y % 400);
}
-/* eval */
-
-void JS_AddIntrinsicEval(JSContext *ctx)
-{
- ctx->eval_internal = __JS_EvalInternal;
+/* return the year, update days */
+static int64_t year_from_days(int64_t *days) {
+ int64_t y, d1, nd, d = *days;
+ y = floor_div(d * 10000, 3652425) + 1970;
+ /* the initial approximation is very good, so only a few
+ iterations are necessary */
+ for(;;) {
+ d1 = d - days_from_year(y);
+ if (d1 < 0) {
+ y--;
+ d1 += days_in_year(y);
+ } else {
+ nd = days_in_year(y);
+ if (d1 < nd)
+ break;
+ d1 -= nd;
+ y++;
+ }
+ }
+ *days = d1;
+ return y;
}
-#ifdef CONFIG_BIGNUM
-
-/* Operators */
+static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
+static char const day_names[] = "SunMonTueWedThuFriSat";
-static void js_operator_set_finalizer(JSRuntime *rt, JSValue val)
+static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
+ double fields[minimum_length(9)], int is_local, int force)
{
- JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
- int i, j;
- JSBinaryOperatorDefEntry *ent;
+ double dval;
+ int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
- if (opset) {
- for(i = 0; i < JS_OVOP_COUNT; i++) {
- if (opset->self_ops[i])
- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]));
- }
- for(j = 0; j < opset->left.count; j++) {
- ent = &opset->left.tab[j];
- for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
- if (ent->ops[i])
- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
- }
- }
- js_free_rt(rt, opset->left.tab);
- for(j = 0; j < opset->right.count; j++) {
- ent = &opset->right.tab[j];
- for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
- if (ent->ops[i])
- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
- }
+ if (JS_ThisTimeValue(ctx, &dval, obj))
+ return -1;
+
+ if (isnan(dval)) {
+ if (!force)
+ return FALSE; /* NaN */
+ d = 0; /* initialize all fields to 0 */
+ } else {
+ d = dval; /* assuming -8.64e15 <= dval <= -8.64e15 */
+ if (is_local) {
+ tz = -getTimezoneOffset(d);
+ d += tz * 60000;
}
- js_free_rt(rt, opset->right.tab);
- js_free_rt(rt, opset);
}
-}
-static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
-{
- JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
- int i, j;
- JSBinaryOperatorDefEntry *ent;
+ /* result is >= 0, we can use % */
+ h = math_mod(d, 86400000);
+ days = (d - h) / 86400000;
+ ms = h % 1000;
+ h = (h - ms) / 1000;
+ s = h % 60;
+ h = (h - s) / 60;
+ m = h % 60;
+ h = (h - m) / 60;
+ wd = math_mod(days + 4, 7); /* week day */
+ y = year_from_days(&days);
- if (opset) {
- for(i = 0; i < JS_OVOP_COUNT; i++) {
- if (opset->self_ops[i])
- JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]),
- mark_func);
- }
- for(j = 0; j < opset->left.count; j++) {
- ent = &opset->left.tab[j];
- for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
- if (ent->ops[i])
- JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
- mark_func);
- }
- }
- for(j = 0; j < opset->right.count; j++) {
- ent = &opset->right.tab[j];
- for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
- if (ent->ops[i])
- JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
- mark_func);
- }
- }
+ for(i = 0; i < 11; i++) {
+ md = month_days[i];
+ if (i == 1)
+ md += days_in_year(y) - 365;
+ if (days < md)
+ break;
+ days -= md;
}
+ fields[0] = y;
+ fields[1] = i;
+ fields[2] = days + 1;
+ fields[3] = h;
+ fields[4] = m;
+ fields[5] = s;
+ fields[6] = ms;
+ fields[7] = wd;
+ fields[8] = tz;
+ return TRUE;
}
+static double time_clip(double t) {
+ if (t >= -8.64e15 && t <= 8.64e15)
+ return trunc(t) + 0.0; /* convert -0 to +0 */
+ else
+ return NAN;
+}
-/* create an OperatorSet object */
-static JSValue js_operators_create_internal(JSContext *ctx,
- int argc, JSValueConst *argv,
- BOOL is_primitive)
-{
- JSValue opset_obj, prop, obj;
- JSOperatorSetData *opset, *opset1;
- JSBinaryOperatorDef *def;
- JSValueConst arg;
- int i, j;
- JSBinaryOperatorDefEntry *new_tab;
- JSBinaryOperatorDefEntry *ent;
- uint32_t op_count;
+/* The spec mandates the use of 'double' and it specifies the order
+ of the operations */
+static double set_date_fields(double fields[minimum_length(7)], int is_local) {
+ double y, m, dt, ym, mn, day, h, s, milli, time, tv;
+ int yi, mi, i;
+ int64_t days;
+ volatile double temp; /* enforce evaluation order */
- if (ctx->rt->operator_count == UINT32_MAX) {
- return JS_ThrowTypeError(ctx, "too many operators");
- }
- opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET);
- if (JS_IsException(opset_obj))
- goto fail;
- opset = js_mallocz(ctx, sizeof(*opset));
- if (!opset)
- goto fail;
- JS_SetOpaque(opset_obj, opset);
- if (argc >= 1) {
- arg = argv[0];
- /* self operators */
- for(i = 0; i < JS_OVOP_COUNT; i++) {
- prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]);
- if (JS_IsException(prop))
- goto fail;
- if (!JS_IsUndefined(prop)) {
- if (check_function(ctx, prop)) {
- JS_FreeValue(ctx, prop);
- goto fail;
- }
- opset->self_ops[i] = JS_VALUE_GET_OBJ(prop);
- }
- }
+ /* emulate 21.4.1.15 MakeDay ( year, month, date ) */
+ y = fields[0];
+ m = fields[1];
+ dt = fields[2];
+ ym = y + floor(m / 12);
+ mn = fmod(m, 12);
+ if (mn < 0)
+ mn += 12;
+ if (ym < -271821 || ym > 275760)
+ return NAN;
+
+ yi = ym;
+ mi = mn;
+ days = days_from_year(yi);
+ for(i = 0; i < mi; i++) {
+ days += month_days[i];
+ if (i == 1)
+ days += days_in_year(yi) - 365;
}
- /* left & right operators */
- for(j = 1; j < argc; j++) {
- arg = argv[j];
- prop = JS_GetPropertyStr(ctx, arg, "left");
- if (JS_IsException(prop))
- goto fail;
- def = &opset->right;
- if (JS_IsUndefined(prop)) {
- prop = JS_GetPropertyStr(ctx, arg, "right");
- if (JS_IsException(prop))
- goto fail;
- if (JS_IsUndefined(prop)) {
- JS_ThrowTypeError(ctx, "left or right property must be present");
- goto fail;
- }
- def = &opset->left;
- }
- /* get the operator set */
- obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype);
- JS_FreeValue(ctx, prop);
- if (JS_IsException(obj))
- goto fail;
- prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
- JS_FreeValue(ctx, obj);
- if (JS_IsException(prop))
- goto fail;
- opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET);
- if (!opset1) {
- JS_FreeValue(ctx, prop);
- goto fail;
- }
- op_count = opset1->operator_counter;
- JS_FreeValue(ctx, prop);
+ day = days + dt - 1;
- /* we assume there are few entries */
- new_tab = js_realloc(ctx, def->tab,
- (def->count + 1) * sizeof(def->tab[0]));
- if (!new_tab)
- goto fail;
- def->tab = new_tab;
- def->count++;
- ent = def->tab + def->count - 1;
- memset(ent, 0, sizeof(def->tab[0]));
- ent->operator_index = op_count;
-
- for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
- prop = JS_GetPropertyStr(ctx, arg,
- js_overloadable_operator_names[i]);
- if (JS_IsException(prop))
- goto fail;
- if (!JS_IsUndefined(prop)) {
- if (check_function(ctx, prop)) {
- JS_FreeValue(ctx, prop);
- goto fail;
- }
- ent->ops[i] = JS_VALUE_GET_OBJ(prop);
- }
- }
+ /* emulate 21.4.1.14 MakeTime ( hour, min, sec, ms ) */
+ h = fields[3];
+ m = fields[4];
+ s = fields[5];
+ milli = fields[6];
+ /* Use a volatile intermediary variable to ensure order of evaluation
+ * as specified in ECMA. This fixes a test262 error on
+ * test262/test/built-ins/Date/UTC/fp-evaluation-order.js.
+ * Without the volatile qualifier, the compile can generate code
+ * that performs the computation in a different order or with instructions
+ * that produce a different result such as FMA (float multiply and add).
+ */
+ time = h * 3600000;
+ time += (temp = m * 60000);
+ time += (temp = s * 1000);
+ time += milli;
+
+ /* emulate 21.4.1.16 MakeDate ( day, time ) */
+ tv = (temp = day * 86400000) + time; /* prevent generation of FMA */
+ if (!isfinite(tv))
+ return NAN;
+
+ /* adjust for local time and clip */
+ if (is_local) {
+ int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv;
+ tv += getTimezoneOffset(ti) * 60000;
}
- opset->is_primitive = is_primitive;
- opset->operator_counter = ctx->rt->operator_count++;
- return opset_obj;
- fail:
- JS_FreeValue(ctx, opset_obj);
- return JS_EXCEPTION;
+ return time_clip(tv);
}
-static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
+static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
{
- return js_operators_create_internal(ctx, argc, argv, FALSE);
+ // get_date_field(obj, n, is_local)
+ double fields[9];
+ int res, n, is_local;
+
+ is_local = magic & 0x0F;
+ n = (magic >> 4) & 0x0F;
+ res = get_date_fields(ctx, this_val, fields, is_local, 0);
+ if (res < 0)
+ return JS_EXCEPTION;
+ if (!res)
+ return JS_NAN;
+
+ if (magic & 0x100) { // getYear
+ fields[0] -= 1900;
+ }
+ return JS_NewFloat64(ctx, fields[n]);
}
-static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
+static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
{
- JSValue opset_obj, prop;
- JSOperatorSetData *opset;
- const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW };
- JSOverloadableOperatorEnum op;
- int i;
+ // _field(obj, first_field, end_field, args, is_local)
+ double fields[9];
+ int res, first_field, end_field, is_local, i, n;
+ double d, a;
- opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
- JS_ATOM_Symbol_operatorSet);
- if (JS_IsException(opset_obj))
- goto fail;
- opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET);
- if (!opset)
- goto fail;
- for(i = 0; i < countof(ops); i++) {
- op = ops[i];
- prop = JS_GetPropertyStr(ctx, argv[0],
- js_overloadable_operator_names[op]);
- if (JS_IsException(prop))
- goto fail;
- if (!JS_IsUndefined(prop)) {
- if (!JS_IsNull(prop) && check_function(ctx, prop)) {
- JS_FreeValue(ctx, prop);
- goto fail;
- }
- if (opset->self_ops[op])
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op]));
- if (JS_IsNull(prop)) {
- opset->self_ops[op] = NULL;
- } else {
- opset->self_ops[op] = JS_VALUE_GET_PTR(prop);
- }
- }
+ d = NAN;
+ first_field = (magic >> 8) & 0x0F;
+ end_field = (magic >> 4) & 0x0F;
+ is_local = magic & 0x0F;
+
+ res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);
+ if (res < 0)
+ return JS_EXCEPTION;
+
+ // Argument coercion is observable and must be done unconditionally.
+ n = min_int(argc, end_field - first_field);
+ for(i = 0; i < n; i++) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ if (!isfinite(a))
+ res = FALSE;
+ fields[first_field + i] = trunc(a);
}
- JS_FreeValue(ctx, opset_obj);
- return JS_UNDEFINED;
- fail:
- JS_FreeValue(ctx, opset_obj);
- return JS_EXCEPTION;
-}
-
-static int js_operators_set_default(JSContext *ctx, JSValueConst obj)
-{
- JSValue opset_obj;
+ if (res && argc > 0)
+ d = set_date_fields(fields, is_local);
- if (!JS_IsObject(obj)) /* in case the prototype is not defined */
- return 0;
- opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE);
- if (JS_IsException(opset_obj))
- return -1;
- /* cannot be modified by the user */
- JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet,
- opset_obj, 0);
- return 0;
+ return JS_SetThisTimeValue(ctx, this_val, d);
}
-static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
+/* fmt:
+ 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"
+ 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"
+ 2: toISOString: "2018-01-02T23:02:56.927Z"
+ 3: toLocaleString: "1/2/2018, 11:40:40 PM"
+ part: 1=date, 2=time 3=all
+ XXX: should use a variant of strftime().
+ */
+static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
{
- return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
-}
+ // _string(obj, fmt, part)
+ char buf[64];
+ double fields[9];
+ int res, fmt, part, pos;
+ int y, mon, d, h, m, s, ms, wd, tz;
-static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSValue func_obj, proto, opset_obj;
+ fmt = (magic >> 4) & 0x0F;
+ part = magic & 0x0F;
- func_obj = JS_UNDEFINED;
- proto = JS_NewObject(ctx);
- if (JS_IsException(proto))
+ res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);
+ if (res < 0)
return JS_EXCEPTION;
- opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE);
- if (JS_IsException(opset_obj))
- goto fail;
- JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet,
- opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators",
- 0, JS_CFUNC_constructor, 0);
- if (JS_IsException(func_obj))
- goto fail;
- JS_SetConstructor2(ctx, func_obj, proto,
- 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- JS_FreeValue(ctx, proto);
- return func_obj;
- fail:
- JS_FreeValue(ctx, proto);
- JS_FreeValue(ctx, func_obj);
- return JS_EXCEPTION;
-}
-
-static const JSCFunctionListEntry js_operators_funcs[] = {
- JS_CFUNC_DEF("create", 1, js_operators_create ),
- JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ),
-};
-
-/* must be called after all overloadable base types are initialized */
-void JS_AddIntrinsicOperators(JSContext *ctx)
-{
- JSValue obj;
-
- ctx->allow_operator_overloading = TRUE;
- obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1);
- JS_SetPropertyFunctionList(ctx, obj,
- js_operators_funcs,
- countof(js_operators_funcs));
- JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators,
- obj,
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- /* add default operatorSets */
- js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]);
- js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]);
- js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]);
- js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]);
- js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]);
- js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
-}
-#endif /* CONFIG_BIGNUM */
-
-/* BigInt */
+ if (!res) {
+ if (fmt == 2)
+ return JS_ThrowRangeError(ctx, "Date value is NaN");
+ else
+ return JS_NewString(ctx, "Invalid Date");
+ }
-static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
-{
- uint32_t tag;
+ y = fields[0];
+ mon = fields[1];
+ d = fields[2];
+ h = fields[3];
+ m = fields[4];
+ s = fields[5];
+ ms = fields[6];
+ wd = fields[7];
+ tz = fields[8];
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
- break;
- case JS_TAG_BIG_INT:
- break;
- case JS_TAG_FLOAT64:
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_FLOAT:
-#endif
- {
- bf_t *a, a_s;
+ pos = 0;
- a = JS_ToBigFloat(ctx, &a_s, val);
- if (!a) {
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
+ if (part & 1) { /* date part */
+ switch(fmt) {
+ case 0:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%.3s, %02d %.3s %0*d ",
+ day_names + wd * 3, d,
+ month_names + mon * 3, 4 + (y < 0), y);
+ break;
+ case 1:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%.3s %.3s %02d %0*d",
+ day_names + wd * 3,
+ month_names + mon * 3, d, 4 + (y < 0), y);
+ if (part == 3) {
+ buf[pos++] = ' ';
}
- if (!bf_is_finite(a)) {
- JS_FreeValue(ctx, val);
- val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt");
+ break;
+ case 2:
+ if (y >= 0 && y <= 9999) {
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%04d", y);
} else {
- JSValue val1 = JS_NewBigInt(ctx);
- bf_t *r;
- int ret;
- if (JS_IsException(val1)) {
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- r = JS_GetBigInt(val1);
- ret = bf_set(r, a);
- ret |= bf_rint(r, BF_RNDZ);
- JS_FreeValue(ctx, val);
- if (ret & BF_ST_MEM_ERROR) {
- JS_FreeValue(ctx, val1);
- val = JS_ThrowOutOfMemory(ctx);
- } else if (ret & BF_ST_INEXACT) {
- JS_FreeValue(ctx, val1);
- val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer");
- } else {
- val = JS_CompactBigInt(ctx, val1);
- }
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%+07d", y);
+ }
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "-%02d-%02dT", mon + 1, d);
+ break;
+ case 3:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);
+ if (part == 3) {
+ buf[pos++] = ',';
+ buf[pos++] = ' ';
}
- if (a == &a_s)
- bf_delete(a);
+ break;
}
- break;
-#ifdef CONFIG_BIGNUM
- case JS_TAG_BIG_DECIMAL:
- val = JS_ToStringFree(ctx, val);
- if (JS_IsException(val))
+ }
+ if (part & 2) { /* time part */
+ switch(fmt) {
+ case 0:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d:%02d:%02d GMT", h, m, s);
break;
- goto redo;
-#endif
- case JS_TAG_STRING:
- val = JS_StringToBigIntErr(ctx, val);
- break;
- case JS_TAG_OBJECT:
- val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
- if (JS_IsException(val))
+ case 1:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d:%02d:%02d GMT", h, m, s);
+ if (tz < 0) {
+ buf[pos++] = '-';
+ tz = -tz;
+ } else {
+ buf[pos++] = '+';
+ }
+ /* tz is >= 0, can use % */
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d%02d", tz / 60, tz % 60);
+ /* XXX: tack the time zone code? */
break;
- goto redo;
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- default:
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeError(ctx, "cannot convert to BigInt");
+ case 2:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d:%02d:%02d.%03dZ", h, m, s, ms);
+ break;
+ case 3:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s,
+ (h < 12) ? 'A' : 'P');
+ break;
+ }
}
- return val;
+ return JS_NewStringLen(ctx, buf, pos);
}
-static JSValue js_bigint_constructor(JSContext *ctx,
- JSValueConst new_target,
- int argc, JSValueConst *argv)
-{
- if (!JS_IsUndefined(new_target))
- return JS_ThrowTypeError(ctx, "not a constructor");
- return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
+/* OS dependent: return the UTC time in ms since 1970. */
+static int64_t date_now(void) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
}
-static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
+static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
{
- if (JS_IsBigInt(ctx, this_val))
- return JS_DupValue(ctx, this_val);
+ // Date(y, mon, d, h, m, s, ms)
+ JSValue rv;
+ int i, n;
+ double a, val;
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id == JS_CLASS_BIG_INT) {
- if (JS_IsBigInt(ctx, p->u.object_data))
- return JS_DupValue(ctx, p->u.object_data);
+ if (JS_IsUndefined(new_target)) {
+ /* invoked as function */
+ argc = 0;
+ }
+ n = argc;
+ if (n == 0) {
+ val = date_now();
+ } else if (n == 1) {
+ JSValue v, dv;
+ if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
+ if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {
+ if (JS_ToFloat64(ctx, &val, p->u.object_data))
+ return JS_EXCEPTION;
+ val = time_clip(val);
+ goto has_val;
+ }
+ }
+ v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);
+ if (JS_IsString(v)) {
+ dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);
+ JS_FreeValue(ctx, v);
+ if (JS_IsException(dv))
+ return JS_EXCEPTION;
+ if (JS_ToFloat64Free(ctx, &val, dv))
+ return JS_EXCEPTION;
+ } else {
+ if (JS_ToFloat64Free(ctx, &val, v))
+ return JS_EXCEPTION;
+ }
+ val = time_clip(val);
+ } else {
+ double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
+ if (n > 7)
+ n = 7;
+ for(i = 0; i < n; i++) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ if (!isfinite(a))
+ break;
+ fields[i] = trunc(a);
+ if (i == 0 && fields[0] >= 0 && fields[0] < 100)
+ fields[0] += 1900;
}
+ val = (i == n) ? set_date_fields(fields, 1) : NAN;
}
- return JS_ThrowTypeError(ctx, "not a BigInt");
+has_val:
+#if 0
+ JSValueConst args[3];
+ args[0] = new_target;
+ args[1] = ctx->class_proto[JS_CLASS_DATE];
+ args[2] = JS_NewFloat64(ctx, val);
+ rv = js___date_create(ctx, JS_UNDEFINED, 3, args);
+#else
+ rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);
+ if (!JS_IsException(rv))
+ JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val));
+#endif
+ if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {
+ /* invoked as a function, return (new Date()).toString(); */
+ JSValue s;
+ s = get_date_string(ctx, rv, 0, NULL, 0x13);
+ JS_FreeValue(ctx, rv);
+ rv = s;
+ }
+ return rv;
}
-static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
+static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
- JSValue val;
- int base;
- JSValue ret;
+ // UTC(y, mon, d, h, m, s, ms)
+ double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
+ int i, n;
+ double a;
- val = js_thisBigIntValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (argc == 0 || JS_IsUndefined(argv[0])) {
- base = 10;
- } else {
- base = js_get_radix(ctx, argv[0]);
- if (base < 0)
- goto fail;
+ n = argc;
+ if (n == 0)
+ return JS_NAN;
+ if (n > 7)
+ n = 7;
+ for(i = 0; i < n; i++) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ if (!isfinite(a))
+ return JS_NAN;
+ fields[i] = trunc(a);
+ if (i == 0 && fields[0] >= 0 && fields[0] < 100)
+ fields[0] += 1900;
}
- ret = js_bigint_to_string1(ctx, val, base);
- JS_FreeValue(ctx, val);
- return ret;
- fail:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
-}
-
-static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- return js_thisBigIntValue(ctx, this_val);
+ return JS_NewFloat64(ctx, set_date_fields(fields, 0));
}
-#ifdef CONFIG_BIGNUM
-static JSValue js_bigint_div(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
-{
- bf_t a_s, b_s, *a, *b, *r, *q;
- int status;
- JSValue q_val, r_val;
+/* Date string parsing */
- q_val = JS_NewBigInt(ctx);
- if (JS_IsException(q_val))
- return JS_EXCEPTION;
- r_val = JS_NewBigInt(ctx);
- if (JS_IsException(r_val))
- goto fail;
- b = NULL;
- a = JS_ToBigInt(ctx, &a_s, argv[0]);
- if (!a)
- goto fail;
- b = JS_ToBigInt(ctx, &b_s, argv[1]);
- if (!b) {
- JS_FreeBigInt(ctx, a, &a_s);
- goto fail;
- }
- q = JS_GetBigInt(q_val);
- r = JS_GetBigInt(r_val);
- status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf);
- JS_FreeBigInt(ctx, a, &a_s);
- JS_FreeBigInt(ctx, b, &b_s);
- if (unlikely(status)) {
- throw_bf_exception(ctx, status);
- goto fail;
- }
- q_val = JS_CompactBigInt(ctx, q_val);
- if (magic & 0x10) {
- JSValue ret;
- ret = JS_NewArray(ctx);
- if (JS_IsException(ret))
- goto fail;
- JS_SetPropertyUint32(ctx, ret, 0, q_val);
- JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val));
- return ret;
+static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) {
+ if (sp[*pp] == c) {
+ *pp += 1;
+ return TRUE;
} else {
- JS_FreeValue(ctx, r_val);
- return q_val;
+ return FALSE;
}
- fail:
- JS_FreeValue(ctx, q_val);
- JS_FreeValue(ctx, r_val);
- return JS_EXCEPTION;
}
-static JSValue js_bigint_sqrt(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
-{
- bf_t a_s, *a, *r, *rem;
- int status;
- JSValue r_val, rem_val;
+/* skip spaces, update offset, return next char */
+static int string_skip_spaces(const uint8_t *sp, int *pp) {
+ int c;
+ while ((c = sp[*pp]) == ' ')
+ *pp += 1;
+ return c;
+}
- r_val = JS_NewBigInt(ctx);
- if (JS_IsException(r_val))
- return JS_EXCEPTION;
- rem_val = JS_NewBigInt(ctx);
- if (JS_IsException(rem_val))
- return JS_EXCEPTION;
- r = JS_GetBigInt(r_val);
- rem = JS_GetBigInt(rem_val);
+/* skip dashes dots and commas */
+static int string_skip_separators(const uint8_t *sp, int *pp) {
+ int c;
+ while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',')
+ *pp += 1;
+ return c;
+}
- a = JS_ToBigInt(ctx, &a_s, argv[0]);
- if (!a)
- goto fail;
- status = bf_sqrtrem(r, rem, a);
- JS_FreeBigInt(ctx, a, &a_s);
- if (unlikely(status & ~BF_ST_INEXACT)) {
- throw_bf_exception(ctx, status);
- goto fail;
- }
- r_val = JS_CompactBigInt(ctx, r_val);
- if (magic) {
- JSValue ret;
- ret = JS_NewArray(ctx);
- if (JS_IsException(ret))
- goto fail;
- JS_SetPropertyUint32(ctx, ret, 0, r_val);
- JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val));
- return ret;
- } else {
- JS_FreeValue(ctx, rem_val);
- return r_val;
- }
- fail:
- JS_FreeValue(ctx, r_val);
- JS_FreeValue(ctx, rem_val);
- return JS_EXCEPTION;
+/* skip a word, stop on spaces, digits and separators, update offset */
+static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) {
+ int c;
+ while (!strchr(stoplist, c = sp[*pp]))
+ *pp += 1;
+ return c;
}
-static JSValue js_bigint_op1(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv,
- int magic)
+/* parse a numeric field (max_digits = 0 -> no maximum) */
+static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval,
+ int min_digits, int max_digits)
{
- bf_t a_s, *a;
- int64_t res;
+ int v = 0;
+ int c, p = *pp, p_start;
- a = JS_ToBigInt(ctx, &a_s, argv[0]);
- if (!a)
- return JS_EXCEPTION;
- switch(magic) {
- case 0: /* floorLog2 */
- if (a->sign || a->expn <= 0) {
- res = -1;
- } else {
- res = a->expn - 1;
- }
- break;
- case 1: /* ctz */
- if (bf_is_zero(a)) {
- res = -1;
- } else {
- res = bf_get_exp_min(a);
- }
- break;
- default:
- abort();
+ p_start = p;
+ while ((c = sp[p]) >= '0' && c <= '9') {
+ /* arbitrary limit to 9 digits */
+ if (v >= 100000000)
+ return FALSE;
+ v = v * 10 + c - '0';
+ p++;
+ if (p - p_start == max_digits)
+ break;
}
- JS_FreeBigInt(ctx, a, &a_s);
- return JS_NewBigInt64(ctx, res);
+ if (p - p_start < min_digits)
+ return FALSE;
+ *pval = v;
+ *pp = p;
+ return TRUE;
}
-#endif
-static JSValue js_bigint_asUintN(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv, int asIntN)
-{
- uint64_t bits;
- bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s;
- JSValue res;
+static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) {
+ /* parse optional fractional part as milliseconds and truncate. */
+ /* spec does not indicate which rounding should be used */
+ int mul = 100, ms = 0, c, p_start, p = *pp;
- if (JS_ToIndex(ctx, &bits, argv[0]))
- return JS_EXCEPTION;
- res = JS_NewBigInt(ctx);
- if (JS_IsException(res))
- return JS_EXCEPTION;
- r = JS_GetBigInt(res);
- a = JS_ToBigInt(ctx, &a_s, argv[1]);
- if (!a) {
- JS_FreeValue(ctx, res);
- return JS_EXCEPTION;
+ c = sp[p];
+ if (c == '.' || c == ',') {
+ p++;
+ p_start = p;
+ while ((c = sp[p]) >= '0' && c <= '9') {
+ ms += (c - '0') * mul;
+ mul /= 10;
+ p++;
+ if (p - p_start == 9)
+ break;
+ }
+ if (p > p_start) {
+ /* only consume the separator if digits are present */
+ *pval = ms;
+ *pp = p;
+ }
}
- /* XXX: optimize */
- r = JS_GetBigInt(res);
- bf_init(ctx->bf_ctx, mask);
- bf_set_ui(mask, 1);
- bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
- bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ);
- bf_logic_and(r, a, mask);
- if (asIntN && bits != 0) {
- bf_set_ui(mask, 1);
- bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ);
- if (bf_cmpu(r, mask) >= 0) {
- bf_set_ui(mask, 1);
- bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
- bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ);
- }
- }
- bf_delete(mask);
- JS_FreeBigInt(ctx, a, &a_s);
- return JS_CompactBigInt(ctx, res);
+ return TRUE;
}
-static const JSCFunctionListEntry js_bigint_funcs[] = {
- JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),
- JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),
-#ifdef CONFIG_BIGNUM
- /* QuickJS extensions */
- JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ),
- JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ),
- JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ),
- JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ),
- JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ),
- JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ),
- JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ),
- JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ),
- JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ),
- JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ),
- JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ),
- JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ),
-#endif
-};
-
-static const JSCFunctionListEntry js_bigint_proto_funcs[] = {
- JS_CFUNC_DEF("toString", 0, js_bigint_toString ),
- JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),
-};
-
-void JS_AddIntrinsicBigInt(JSContext *ctx)
-{
- JSRuntime *rt = ctx->rt;
- JSValueConst obj1;
-
- rt->bigint_ops.to_string = js_bigint_to_string;
- rt->bigint_ops.from_string = js_string_to_bigint;
- rt->bigint_ops.unary_arith = js_unary_arith_bigint;
- rt->bigint_ops.binary_arith = js_binary_arith_bigint;
- rt->bigint_ops.compare = js_compare_bigfloat;
-
- ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);
- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
- js_bigint_proto_funcs,
- countof(js_bigint_proto_funcs));
- obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1,
- ctx->class_proto[JS_CLASS_BIG_INT]);
- JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,
- countof(js_bigint_funcs));
+static uint8_t upper_ascii(uint8_t c) {
+ return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c;
}
-#ifdef CONFIG_BIGNUM
-
-/* BigFloat */
-
-static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val)
-{
- if (JS_IsBigFloat(this_val))
- return JS_DupValue(ctx, this_val);
+static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) {
+ int tz = 0, sgn, hh, mm, p = *pp;
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id == JS_CLASS_BIG_FLOAT) {
- if (JS_IsBigFloat(p->u.object_data))
- return JS_DupValue(ctx, p->u.object_data);
+ sgn = sp[p++];
+ if (sgn == '+' || sgn == '-') {
+ int n = p;
+ if (!string_get_digits(sp, &p, &hh, 1, 0))
+ return FALSE;
+ n = p - n;
+ if (strict && n != 2 && n != 4)
+ return FALSE;
+ while (n > 4) {
+ n -= 2;
+ hh /= 100;
+ }
+ if (n > 2) {
+ mm = hh % 100;
+ hh = hh / 100;
+ } else {
+ mm = 0;
+ if (string_skip_char(sp, &p, ':') /* optional separator */
+ && !string_get_digits(sp, &p, &mm, 2, 2))
+ return FALSE;
}
+ if (hh > 23 || mm > 59)
+ return FALSE;
+ tz = hh * 60 + mm;
+ if (sgn != '+')
+ tz = -tz;
+ } else
+ if (sgn != 'Z') {
+ return FALSE;
+ }
+ *pp = p;
+ *tzp = tz;
+ return TRUE;
+}
+
+static BOOL string_match(const uint8_t *sp, int *pp, const char *s) {
+ int p = *pp;
+ while (*s != '\0') {
+ if (upper_ascii(sp[p]) != upper_ascii(*s++))
+ return FALSE;
+ p++;
}
- return JS_ThrowTypeError(ctx, "not a bigfloat");
+ *pp = p;
+ return TRUE;
}
-static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSValue val;
- int base;
- JSValue ret;
+static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) {
+ int n, i;
- val = js_thisBigFloatValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (argc == 0 || JS_IsUndefined(argv[0])) {
- base = 10;
- } else {
- base = js_get_radix(ctx, argv[0]);
- if (base < 0)
- goto fail;
+ for (n = 0; n < count; n++) {
+ for (i = 0;; i++) {
+ if (upper_ascii(sp[p + i]) != upper_ascii(list[n * 3 + i]))
+ break;
+ if (i == 2)
+ return n;
+ }
}
- ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
- JS_FreeValue(ctx, val);
- return ret;
- fail:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
+ return -1;
}
-static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- return js_thisBigFloatValue(ctx, this_val);
-}
+static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) {
+ int n;
-static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val)
-{
- int rnd_mode;
- if (JS_ToInt32Sat(ctx, &rnd_mode, val))
- return -1;
- if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) {
- JS_ThrowRangeError(ctx, "invalid rounding mode");
- return -1;
- }
- return rnd_mode;
+ n = find_abbrev(sp, *pp, month_names, 12);
+ if (n < 0)
+ return FALSE;
+
+ *pval = n + 1;
+ *pp += 3;
+ return TRUE;
}
-static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSValue val, ret;
- int64_t f;
- int rnd_mode, radix;
+/* parse toISOString format */
+static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) {
+ int sgn, i, p = 0;
- val = js_thisBigFloatValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (JS_ToInt64Sat(ctx, &f, argv[0]))
- goto fail;
- if (f < 0 || f > BF_PREC_MAX) {
- JS_ThrowRangeError(ctx, "invalid number of digits");
- goto fail;
- }
- rnd_mode = BF_RNDNA;
- radix = 10;
- /* XXX: swap parameter order for rounding mode and radix */
- if (argc > 1) {
- rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
- if (rnd_mode < 0)
- goto fail;
- }
- if (argc > 2) {
- radix = js_get_radix(ctx, argv[2]);
- if (radix < 0)
- goto fail;
+ /* initialize fields to the beginning of the Epoch */
+ for (i = 0; i < 9; i++) {
+ fields[i] = (i == 2);
}
- ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
- JS_FreeValue(ctx, val);
- return ret;
- fail:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
-}
-
-static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val)
-{
- BOOL res;
- uint32_t tag;
+ *is_local = FALSE;
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_BIG_FLOAT:
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- res = bf_is_finite(&p->num);
+ /* year is either yyyy digits or [+-]yyyyyy */
+ sgn = sp[p];
+ if (sgn == '-' || sgn == '+') {
+ p++;
+ if (!string_get_digits(sp, &p, &fields[0], 6, 6))
+ return FALSE;
+ if (sgn == '-') {
+ if (fields[0] == 0)
+ return FALSE; // reject -000000
+ fields[0] = -fields[0];
}
- break;
- default:
- res = FALSE;
- break;
+ } else {
+ if (!string_get_digits(sp, &p, &fields[0], 4, 4))
+ return FALSE;
}
- return res;
-}
-
-static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSValue val, ret;
- int64_t f;
- int rnd_mode, radix;
-
- val = js_thisBigFloatValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (JS_ToInt64Sat(ctx, &f, argv[0]))
- goto fail;
- if (!js_bigfloat_is_finite(ctx, val)) {
- ret = JS_ToString(ctx, val);
- } else if (JS_IsUndefined(argv[0])) {
- ret = js_ftoa(ctx, val, 10, 0,
- BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
- } else {
- if (f < 0 || f > BF_PREC_MAX) {
- JS_ThrowRangeError(ctx, "invalid number of digits");
- goto fail;
+ if (string_skip_char(sp, &p, '-')) {
+ if (!string_get_digits(sp, &p, &fields[1], 2, 2)) /* month */
+ return FALSE;
+ if (fields[1] < 1)
+ return FALSE;
+ fields[1] -= 1;
+ if (string_skip_char(sp, &p, '-')) {
+ if (!string_get_digits(sp, &p, &fields[2], 2, 2)) /* day */
+ return FALSE;
+ if (fields[2] < 1)
+ return FALSE;
}
- rnd_mode = BF_RNDNA;
- radix = 10;
- if (argc > 1) {
- rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
- if (rnd_mode < 0)
- goto fail;
+ }
+ if (string_skip_char(sp, &p, 'T')) {
+ *is_local = TRUE;
+ if (!string_get_digits(sp, &p, &fields[3], 2, 2) /* hour */
+ || !string_skip_char(sp, &p, ':')
+ || !string_get_digits(sp, &p, &fields[4], 2, 2)) { /* minute */
+ fields[3] = 100; // reject unconditionally
+ return TRUE;
}
- if (argc > 2) {
- radix = js_get_radix(ctx, argv[2]);
- if (radix < 0)
- goto fail;
+ if (string_skip_char(sp, &p, ':')) {
+ if (!string_get_digits(sp, &p, &fields[5], 2, 2)) /* second */
+ return FALSE;
+ string_get_milliseconds(sp, &p, &fields[6]);
}
- ret = js_ftoa(ctx, val, radix, f + 1,
- rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
}
- JS_FreeValue(ctx, val);
- return ret;
- fail:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
+ /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */
+ if (sp[p]) {
+ *is_local = FALSE;
+ if (!string_get_tzoffset(sp, &p, &fields[8], TRUE))
+ return FALSE;
+ }
+ /* error if extraneous characters */
+ return sp[p] == '\0';
}
-static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSValue val, ret;
- int64_t p;
- int rnd_mode, radix;
+static struct {
+ char name[6];
+ int16_t offset;
+} const js_tzabbr[] = {
+ { "GMT", 0 }, // Greenwich Mean Time
+ { "UTC", 0 }, // Coordinated Universal Time
+ { "UT", 0 }, // Universal Time
+ { "Z", 0 }, // Zulu Time
+ { "EDT", -4 * 60 }, // Eastern Daylight Time
+ { "EST", -5 * 60 }, // Eastern Standard Time
+ { "CDT", -5 * 60 }, // Central Daylight Time
+ { "CST", -6 * 60 }, // Central Standard Time
+ { "MDT", -6 * 60 }, // Mountain Daylight Time
+ { "MST", -7 * 60 }, // Mountain Standard Time
+ { "PDT", -7 * 60 }, // Pacific Daylight Time
+ { "PST", -8 * 60 }, // Pacific Standard Time
+ { "WET", +0 * 60 }, // Western European Time
+ { "WEST", +1 * 60 }, // Western European Summer Time
+ { "CET", +1 * 60 }, // Central European Time
+ { "CEST", +2 * 60 }, // Central European Summer Time
+ { "EET", +2 * 60 }, // Eastern European Time
+ { "EEST", +3 * 60 }, // Eastern European Summer Time
+};
- val = js_thisBigFloatValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (JS_IsUndefined(argv[0]))
- goto to_string;
- if (JS_ToInt64Sat(ctx, &p, argv[0]))
- goto fail;
- if (!js_bigfloat_is_finite(ctx, val)) {
- to_string:
- ret = JS_ToString(ctx, this_val);
- } else {
- if (p < 1 || p > BF_PREC_MAX) {
- JS_ThrowRangeError(ctx, "invalid number of digits");
- goto fail;
- }
- rnd_mode = BF_RNDNA;
- radix = 10;
- if (argc > 1) {
- rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
- if (rnd_mode < 0)
- goto fail;
- }
- if (argc > 2) {
- radix = js_get_radix(ctx, argv[2]);
- if (radix < 0)
- goto fail;
+static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) {
+ for (size_t i = 0; i < countof(js_tzabbr); i++) {
+ if (string_match(sp, pp, js_tzabbr[i].name)) {
+ *offset = js_tzabbr[i].offset;
+ return TRUE;
}
- ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED);
}
- JS_FreeValue(ctx, val);
- return ret;
- fail:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
+ return FALSE;
}
-static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = {
- JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ),
- JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ),
- JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ),
- JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ),
- JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ),
-};
+/* parse toString, toUTCString and other formats */
+static BOOL js_date_parse_otherstring(const uint8_t *sp,
+ int fields[minimum_length(9)],
+ BOOL *is_local) {
+ int c, i, val, p = 0, p_start;
+ int num[3];
+ BOOL has_year = FALSE;
+ BOOL has_mon = FALSE;
+ BOOL has_time = FALSE;
+ int num_index = 0;
-static JSValue js_bigfloat_constructor(JSContext *ctx,
- JSValueConst new_target,
- int argc, JSValueConst *argv)
-{
- JSValue val;
- if (!JS_IsUndefined(new_target))
- return JS_ThrowTypeError(ctx, "not a constructor");
- if (argc == 0) {
- bf_t *r;
- val = JS_NewBigFloat(ctx);
- if (JS_IsException(val))
- return val;
- r = JS_GetBigFloat(val);
- bf_set_zero(r, 0);
- } else {
- val = JS_DupValue(ctx, argv[0]);
- redo:
- switch(JS_VALUE_GET_NORM_TAG(val)) {
- case JS_TAG_BIG_FLOAT:
- break;
- case JS_TAG_FLOAT64:
- {
- bf_t *r;
- double d = JS_VALUE_GET_FLOAT64(val);
- val = JS_NewBigFloat(ctx);
- if (JS_IsException(val))
- break;
- r = JS_GetBigFloat(val);
- if (bf_set_float64(r, d))
- goto fail;
- }
- break;
- case JS_TAG_INT:
- {
- bf_t *r;
- int32_t v = JS_VALUE_GET_INT(val);
- val = JS_NewBigFloat(ctx);
- if (JS_IsException(val))
- break;
- r = JS_GetBigFloat(val);
- if (bf_set_si(r, v))
- goto fail;
- }
- break;
- case JS_TAG_BIG_INT:
- /* We keep the full precision of the integer */
- {
- JSBigFloat *p = JS_VALUE_GET_PTR(val);
- val = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
- }
- break;
- case JS_TAG_BIG_DECIMAL:
- val = JS_ToStringFree(ctx, val);
- if (JS_IsException(val))
- break;
- goto redo;
- case JS_TAG_STRING:
- {
- const char *str, *p;
- size_t len;
- int err;
+ /* initialize fields to the beginning of 2001-01-01 */
+ fields[0] = 2001;
+ fields[1] = 1;
+ fields[2] = 1;
+ for (i = 3; i < 9; i++) {
+ fields[i] = 0;
+ }
+ *is_local = TRUE;
- str = JS_ToCStringLen(ctx, &len, val);
- JS_FreeValue(ctx, val);
- if (!str)
- return JS_EXCEPTION;
- p = str;
- p += skip_spaces(p);
- if ((p - str) == len) {
- bf_t *r;
- val = JS_NewBigFloat(ctx);
- if (JS_IsException(val))
- break;
- r = JS_GetBigFloat(val);
- bf_set_zero(r, 0);
- err = 0;
- } else {
- val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT |
- ATOD_TYPE_BIG_FLOAT |
- ATOD_ACCEPT_PREFIX_AFTER_SIGN);
- if (JS_IsException(val)) {
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
+ while (string_skip_spaces(sp, &p)) {
+ p_start = p;
+ if ((c = sp[p]) == '+' || c == '-') {
+ if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) {
+ *is_local = FALSE;
+ } else {
+ p++;
+ if (string_get_digits(sp, &p, &val, 1, 0)) {
+ if (c == '-') {
+ if (val == 0)
+ return FALSE;
+ val = -val;
}
- p += skip_spaces(p);
- err = ((p - str) != len);
+ fields[0] = val;
+ has_year = TRUE;
}
- JS_FreeCString(ctx, str);
- if (err) {
- JS_FreeValue(ctx, val);
- return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal");
+ }
+ } else
+ if (string_get_digits(sp, &p, &val, 1, 0)) {
+ if (string_skip_char(sp, &p, ':')) {
+ /* time part */
+ fields[3] = val;
+ if (!string_get_digits(sp, &p, &fields[4], 1, 2))
+ return FALSE;
+ if (string_skip_char(sp, &p, ':')) {
+ if (!string_get_digits(sp, &p, &fields[5], 1, 2))
+ return FALSE;
+ string_get_milliseconds(sp, &p, &fields[6]);
+ }
+ has_time = TRUE;
+ } else {
+ if (p - p_start > 2) {
+ fields[0] = val;
+ has_year = TRUE;
+ } else
+ if (val < 1 || val > 31) {
+ fields[0] = val + (val < 100) * 1900 + (val < 50) * 100;
+ has_year = TRUE;
+ } else {
+ if (num_index == 3)
+ return FALSE;
+ num[num_index++] = val;
}
}
- break;
- case JS_TAG_OBJECT:
- val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
- if (JS_IsException(val))
- break;
- goto redo;
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- default:
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeError(ctx, "cannot convert to bigfloat");
+ } else
+ if (string_get_month(sp, &p, &fields[1])) {
+ has_mon = TRUE;
+ string_skip_until(sp, &p, "0123456789 -/(");
+ } else
+ if (has_time && string_match(sp, &p, "PM")) {
+ if (fields[3] < 12)
+ fields[3] += 12;
+ continue;
+ } else
+ if (has_time && string_match(sp, &p, "AM")) {
+ if (fields[3] == 12)
+ fields[3] -= 12;
+ continue;
+ } else
+ if (string_get_tzabbr(sp, &p, &fields[8])) {
+ *is_local = FALSE;
+ continue;
+ } else
+ if (c == '(') { /* skip parenthesized phrase */
+ int level = 0;
+ while ((c = sp[p]) != '\0') {
+ p++;
+ level += (c == '(');
+ level -= (c == ')');
+ if (!level)
+ break;
+ }
+ if (level > 0)
+ return FALSE;
+ } else
+ if (c == ')') {
+ return FALSE;
+ } else {
+ if (has_year + has_mon + has_time + num_index)
+ return FALSE;
+ /* skip a word */
+ string_skip_until(sp, &p, " -/(");
}
+ string_skip_separators(sp, &p);
}
- return val;
- fail:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
-}
+ if (num_index + has_year + has_mon > 3)
+ return FALSE;
-static JSValue js_bigfloat_get_const(JSContext *ctx,
- JSValueConst this_val, int magic)
-{
- bf_t *r;
- JSValue val;
- val = JS_NewBigFloat(ctx);
- if (JS_IsException(val))
- return val;
- r = JS_GetBigFloat(val);
- switch(magic) {
- case 0: /* PI */
- bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags);
+ switch (num_index) {
+ case 0:
+ if (!has_year)
+ return FALSE;
break;
- case 1: /* LN2 */
- bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags);
+ case 1:
+ if (has_mon)
+ fields[2] = num[0];
+ else
+ fields[1] = num[0];
break;
- case 2: /* MIN_VALUE */
- case 3: /* MAX_VALUE */
- {
- slimb_t e_range, e;
- e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1);
- bf_set_ui(r, 1);
- if (magic == 2) {
- e = -e_range + 2;
- if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL)
- e -= ctx->fp_env.prec - 1;
- bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags);
- } else {
- bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec,
- ctx->fp_env.flags);
- bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags);
- bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec,
- ctx->fp_env.flags);
- }
+ case 2:
+ if (has_year) {
+ fields[1] = num[0];
+ fields[2] = num[1];
+ } else
+ if (has_mon) {
+ fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100;
+ fields[2] = num[0];
+ } else {
+ fields[1] = num[0];
+ fields[2] = num[1];
}
break;
- case 4: /* EPSILON */
- bf_set_ui(r, 1);
- bf_mul_2exp(r, 1 - ctx->fp_env.prec,
- ctx->fp_env.prec, ctx->fp_env.flags);
+ case 3:
+ fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100;
+ fields[1] = num[0];
+ fields[2] = num[1];
break;
default:
- abort();
+ return FALSE;
}
- return val;
+ if (fields[1] < 1 || fields[2] < 1)
+ return FALSE;
+ fields[1] -= 1;
+ return TRUE;
}
-static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
+static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
- bf_t *a;
- const char *str;
- JSValue ret;
- int radix;
- JSFloatEnv *fe;
+ JSValue s, rv;
+ int fields[9];
+ double fields1[9];
+ double d;
+ int i, c;
+ JSString *sp;
+ uint8_t buf[128];
+ BOOL is_local;
- str = JS_ToCString(ctx, argv[0]);
- if (!str)
- return JS_EXCEPTION;
- if (JS_ToInt32(ctx, &radix, argv[1])) {
- fail:
- JS_FreeCString(ctx, str);
+ rv = JS_NAN;
+
+ s = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(s))
return JS_EXCEPTION;
+
+ sp = JS_VALUE_GET_STRING(s);
+ /* convert the string as a byte array */
+ for (i = 0; i < sp->len && i < (int)countof(buf) - 1; i++) {
+ c = string_get(sp, i);
+ if (c > 255)
+ c = (c == 0x2212) ? '-' : 'x';
+ buf[i] = c;
}
- if (radix != 0 && (radix < 2 || radix > 36)) {
- JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
- goto fail;
- }
- fe = &ctx->fp_env;
- if (argc > 2) {
- fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
- if (!fe)
- goto fail;
+ buf[i] = '\0';
+ if (js_date_parse_isostring(buf, fields, &is_local)
+ || js_date_parse_otherstring(buf, fields, &is_local)) {
+ static int const field_max[6] = { 0, 11, 31, 24, 59, 59 };
+ BOOL valid = TRUE;
+ /* check field maximum values */
+ for (i = 1; i < 6; i++) {
+ if (fields[i] > field_max[i])
+ valid = FALSE;
+ }
+ /* special case 24:00:00.000 */
+ if (fields[3] == 24 && (fields[4] | fields[5] | fields[6]))
+ valid = FALSE;
+ if (valid) {
+ for(i = 0; i < 7; i++)
+ fields1[i] = fields[i];
+ d = set_date_fields(fields1, is_local) - fields[8] * 60000;
+ rv = JS_NewFloat64(ctx, d);
+ }
}
- ret = JS_NewBigFloat(ctx);
- if (JS_IsException(ret))
- goto done;
- a = JS_GetBigFloat(ret);
- /* XXX: use js_atof() */
- bf_atof(a, str, NULL, radix, fe->prec, fe->flags);
- done:
- JS_FreeCString(ctx, str);
- return ret;
-}
-
-static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSValueConst val = argv[0];
- JSBigFloat *p;
-
- if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
- return JS_FALSE;
- p = JS_VALUE_GET_PTR(val);
- return JS_NewBool(ctx, bf_is_finite(&p->num));
-}
-
-static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSValueConst val = argv[0];
- JSBigFloat *p;
-
- if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
- return JS_FALSE;
- p = JS_VALUE_GET_PTR(val);
- return JS_NewBool(ctx, bf_is_nan(&p->num));
+ JS_FreeValue(ctx, s);
+ return rv;
}
-enum {
- MATH_OP_ABS,
- MATH_OP_FLOOR,
- MATH_OP_CEIL,
- MATH_OP_ROUND,
- MATH_OP_TRUNC,
- MATH_OP_SQRT,
- MATH_OP_FPROUND,
- MATH_OP_ACOS,
- MATH_OP_ASIN,
- MATH_OP_ATAN,
- MATH_OP_ATAN2,
- MATH_OP_COS,
- MATH_OP_EXP,
- MATH_OP_LOG,
- MATH_OP_POW,
- MATH_OP_SIN,
- MATH_OP_TAN,
- MATH_OP_FMOD,
- MATH_OP_REM,
- MATH_OP_SIGN,
-
- MATH_OP_ADD,
- MATH_OP_SUB,
- MATH_OP_MUL,
- MATH_OP_DIV,
-};
-
-static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
+static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
- bf_t a_s, *a, *r;
- JSFloatEnv *fe;
- int rnd_mode;
- JSValue op1, res;
-
- op1 = JS_ToNumeric(ctx, argv[0]);
- if (JS_IsException(op1))
- return op1;
- a = JS_ToBigFloat(ctx, &a_s, op1);
- if (!a) {
- JS_FreeValue(ctx, op1);
- return JS_EXCEPTION;
- }
- fe = &ctx->fp_env;
- if (argc > 1) {
- fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV);
- if (!fe)
- goto fail;
- }
- res = JS_NewBigFloat(ctx);
- if (JS_IsException(res)) {
- fail:
- if (a == &a_s)
- bf_delete(a);
- JS_FreeValue(ctx, op1);
- return JS_EXCEPTION;
- }
- r = JS_GetBigFloat(res);
- switch (magic) {
- case MATH_OP_ABS:
- bf_set(r, a);
- r->sign = 0;
- break;
- case MATH_OP_FLOOR:
- rnd_mode = BF_RNDD;
- goto rint;
- case MATH_OP_CEIL:
- rnd_mode = BF_RNDU;
- goto rint;
- case MATH_OP_ROUND:
- rnd_mode = BF_RNDNA;
- goto rint;
- case MATH_OP_TRUNC:
- rnd_mode = BF_RNDZ;
- rint:
- bf_set(r, a);
- fe->status |= bf_rint(r, rnd_mode);
- break;
- case MATH_OP_SQRT:
- fe->status |= bf_sqrt(r, a, fe->prec, fe->flags);
- break;
- case MATH_OP_FPROUND:
- bf_set(r, a);
- fe->status |= bf_round(r, fe->prec, fe->flags);
- break;
- case MATH_OP_ACOS:
- fe->status |= bf_acos(r, a, fe->prec, fe->flags);
- break;
- case MATH_OP_ASIN:
- fe->status |= bf_asin(r, a, fe->prec, fe->flags);
- break;
- case MATH_OP_ATAN:
- fe->status |= bf_atan(r, a, fe->prec, fe->flags);
- break;
- case MATH_OP_COS:
- fe->status |= bf_cos(r, a, fe->prec, fe->flags);
- break;
- case MATH_OP_EXP:
- fe->status |= bf_exp(r, a, fe->prec, fe->flags);
- break;
- case MATH_OP_LOG:
- fe->status |= bf_log(r, a, fe->prec, fe->flags);
- break;
- case MATH_OP_SIN:
- fe->status |= bf_sin(r, a, fe->prec, fe->flags);
- break;
- case MATH_OP_TAN:
- fe->status |= bf_tan(r, a, fe->prec, fe->flags);
- break;
- case MATH_OP_SIGN:
- if (bf_is_nan(a) || bf_is_zero(a)) {
- bf_set(r, a);
- } else {
- bf_set_si(r, 1 - 2 * a->sign);
- }
- break;
- default:
- abort();
- }
- if (a == &a_s)
- bf_delete(a);
- JS_FreeValue(ctx, op1);
- return res;
+ // now()
+ return JS_NewInt64(ctx, date_now());
}
-static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
+static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
- bf_t a_s, *a, b_s, *b, r_s, *r = &r_s;
- JSFloatEnv *fe;
- JSValue op1, op2, res;
+ // Symbol_toPrimitive(hint)
+ JSValueConst obj = this_val;
+ JSAtom hint = JS_ATOM_NULL;
+ int hint_num;
- op1 = JS_ToNumeric(ctx, argv[0]);
- if (JS_IsException(op1))
- return op1;
- op2 = JS_ToNumeric(ctx, argv[1]);
- if (JS_IsException(op2)) {
- JS_FreeValue(ctx, op1);
- return op2;
- }
- a = JS_ToBigFloat(ctx, &a_s, op1);
- if (!a)
- goto fail1;
- b = JS_ToBigFloat(ctx, &b_s, op2);
- if (!b)
- goto fail2;
- fe = &ctx->fp_env;
- if (argc > 2) {
- fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
- if (!fe)
- goto fail;
- }
- res = JS_NewBigFloat(ctx);
- if (JS_IsException(res)) {
- fail:
- if (b == &b_s)
- bf_delete(b);
- fail2:
- if (a == &a_s)
- bf_delete(a);
- fail1:
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- return JS_EXCEPTION;
+ if (!JS_IsObject(obj))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ if (JS_IsString(argv[0])) {
+ hint = JS_ValueToAtom(ctx, argv[0]);
+ if (hint == JS_ATOM_NULL)
+ return JS_EXCEPTION;
+ JS_FreeAtom(ctx, hint);
}
- r = JS_GetBigFloat(res);
- switch (magic) {
- case MATH_OP_ATAN2:
- fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags);
- break;
- case MATH_OP_POW:
- fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS);
- break;
- case MATH_OP_FMOD:
- fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
- break;
- case MATH_OP_REM:
- fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN);
- break;
- case MATH_OP_ADD:
- fe->status |= bf_add(r, a, b, fe->prec, fe->flags);
- break;
- case MATH_OP_SUB:
- fe->status |= bf_sub(r, a, b, fe->prec, fe->flags);
- break;
- case MATH_OP_MUL:
- fe->status |= bf_mul(r, a, b, fe->prec, fe->flags);
+ switch (hint) {
+ case JS_ATOM_number:
+ case JS_ATOM_integer:
+ hint_num = HINT_NUMBER;
break;
- case MATH_OP_DIV:
- fe->status |= bf_div(r, a, b, fe->prec, fe->flags);
+ case JS_ATOM_string:
+ case JS_ATOM_default:
+ hint_num = HINT_STRING;
break;
default:
- abort();
+ return JS_ThrowTypeError(ctx, "invalid hint");
}
- if (a == &a_s)
- bf_delete(a);
- if (b == &b_s)
- bf_delete(b);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- return res;
+ return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);
}
-static const JSCFunctionListEntry js_bigfloat_funcs[] = {
- JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ),
- JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ),
- JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ),
- JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ),
- JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ),
- JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ),
- JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ),
- JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ),
- JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ),
- JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ),
- JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ),
- JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ),
- JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ),
- JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ),
- JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ),
- JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ),
- JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ),
- JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ),
- JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ),
- JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ),
- JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ),
- JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ),
- JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ),
- JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ),
- JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ),
- JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ),
- JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ),
- JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ),
- JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ),
- JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ),
- JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ),
- JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ),
-};
-
-/* FloatEnv */
-
-static JSValue js_float_env_constructor(JSContext *ctx,
- JSValueConst new_target,
- int argc, JSValueConst *argv)
+static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
- JSValue obj;
- JSFloatEnv *fe;
- int64_t prec;
- int flags, rndmode;
-
- prec = ctx->fp_env.prec;
- flags = ctx->fp_env.flags;
- if (!JS_IsUndefined(argv[0])) {
- if (JS_ToInt64Sat(ctx, &prec, argv[0]))
- return JS_EXCEPTION;
- if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
- return JS_ThrowRangeError(ctx, "invalid precision");
- flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */
- if (argc > 1 && !JS_IsUndefined(argv[1])) {
- if (JS_ToInt32Sat(ctx, &rndmode, argv[1]))
- return JS_EXCEPTION;
- if (rndmode < BF_RNDN || rndmode > BF_RNDF)
- return JS_ThrowRangeError(ctx, "invalid rounding mode");
- flags = rndmode;
- }
- }
+ // getTimezoneOffset()
+ double v;
- obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- fe = js_malloc(ctx, sizeof(*fe));
- if (!fe)
+ if (JS_ThisTimeValue(ctx, &v, this_val))
return JS_EXCEPTION;
- fe->prec = prec;
- fe->flags = flags;
- fe->status = 0;
- JS_SetOpaque(obj, fe);
- return obj;
+ if (isnan(v))
+ return JS_NAN;
+ else
+ /* assuming -8.64e15 <= v <= -8.64e15 */
+ return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));
}
-static void js_float_env_finalizer(JSRuntime *rt, JSValue val)
+static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
- JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV);
- js_free_rt(rt, fe);
-}
+ // getTime()
+ double v;
-static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val)
-{
- return JS_NewInt64(ctx, ctx->fp_env.prec);
+ if (JS_ThisTimeValue(ctx, &v, this_val))
+ return JS_EXCEPTION;
+ return JS_NewFloat64(ctx, v);
}
-static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val)
+static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
- return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags));
+ // setTime(v)
+ double v;
+
+ if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))
+ return JS_EXCEPTION;
+ return JS_SetThisTimeValue(ctx, this_val, time_clip(v));
}
-static JSValue js_float_env_setPrec(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv)
+static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
- JSValueConst func;
- int exp_bits, flags, saved_flags;
- JSValue ret;
- limb_t saved_prec;
- int64_t prec;
+ // setYear(y)
+ double y;
+ JSValueConst args[1];
- func = argv[0];
- if (JS_ToInt64Sat(ctx, &prec, argv[1]))
+ if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))
return JS_EXCEPTION;
- if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
- return JS_ThrowRangeError(ctx, "invalid precision");
- exp_bits = BF_EXP_BITS_MAX;
-
- if (argc > 2 && !JS_IsUndefined(argv[2])) {
- if (JS_ToInt32Sat(ctx, &exp_bits, argv[2]))
- return JS_EXCEPTION;
- if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX)
- return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
+ y = +y;
+ if (isfinite(y)) {
+ y = trunc(y);
+ if (y >= 0 && y < 100)
+ y += 1900;
}
+ args[0] = JS_NewFloat64(ctx, y);
+ return set_date_field(ctx, this_val, 1, args, 0x011);
+}
- flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits);
-
- saved_prec = ctx->fp_env.prec;
- saved_flags = ctx->fp_env.flags;
+static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // toJSON(key)
+ JSValue obj, tv, method, rv;
+ double d;
- ctx->fp_env.prec = prec;
- ctx->fp_env.flags = flags;
+ rv = JS_EXCEPTION;
+ tv = JS_UNDEFINED;
- ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL);
- /* always restore the floating point precision */
- ctx->fp_env.prec = saved_prec;
- ctx->fp_env.flags = saved_flags;
- return ret;
+ obj = JS_ToObject(ctx, this_val);
+ tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);
+ if (JS_IsException(tv))
+ goto exception;
+ if (JS_IsNumber(tv)) {
+ if (JS_ToFloat64(ctx, &d, tv) < 0)
+ goto exception;
+ if (!isfinite(d)) {
+ rv = JS_NULL;
+ goto done;
+ }
+ }
+ method = JS_GetPropertyStr(ctx, obj, "toISOString");
+ if (JS_IsException(method))
+ goto exception;
+ if (!JS_IsFunction(ctx, method)) {
+ JS_ThrowTypeError(ctx, "object needs toISOString method");
+ JS_FreeValue(ctx, method);
+ goto exception;
+ }
+ rv = JS_CallFree(ctx, method, obj, 0, NULL);
+exception:
+done:
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, tv);
+ return rv;
}
-#define FE_PREC (-1)
-#define FE_EXP (-2)
-#define FE_RNDMODE (-3)
-#define FE_SUBNORMAL (-4)
+static const JSCFunctionListEntry js_date_funcs[] = {
+ JS_CFUNC_DEF("now", 0, js_Date_now ),
+ JS_CFUNC_DEF("parse", 1, js_Date_parse ),
+ JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),
+};
+
+static const JSCFunctionListEntry js_date_proto_funcs[] = {
+ JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),
+ JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),
+ JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),
+ JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),
+ JS_ALIAS_DEF("toGMTString", "toUTCString" ),
+ JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),
+ JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),
+ JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),
+ JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),
+ JS_CFUNC_DEF("getTime", 0, js_date_getTime ),
+ JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),
+ JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),
+ JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),
+ JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),
+ JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),
+ JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),
+ JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),
+ JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),
+ JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),
+ JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),
+ JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),
+ JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),
+ JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),
+ JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),
+ JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),
+ JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),
+ JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),
+ JS_CFUNC_DEF("setTime", 1, js_date_setTime ),
+ JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),
+ JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),
+ JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),
+ JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),
+ JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),
+ JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),
+ JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),
+ JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),
+ JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),
+ JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),
+ JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),
+ JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),
+ JS_CFUNC_DEF("setYear", 1, js_date_setYear ),
+ JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),
+ JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),
+ JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),
+};
-static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic)
+JSValue JS_NewDate(JSContext *ctx, double epoch_ms)
{
- JSFloatEnv *fe;
- fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
- if (!fe)
+ JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE);
+ if (JS_IsException(obj))
return JS_EXCEPTION;
- switch(magic) {
- case FE_PREC:
- return JS_NewInt64(ctx, fe->prec);
- case FE_EXP:
- return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags));
- case FE_RNDMODE:
- return JS_NewInt32(ctx, fe->flags & BF_RND_MASK);
- case FE_SUBNORMAL:
- return JS_NewBool(ctx, fe->flags & BF_FLAG_SUBNORMAL);
- default:
- return JS_NewBool(ctx, fe->status & magic);
- }
+ JS_SetObjectData(ctx, obj, __JS_NewFloat64(ctx, time_clip(epoch_ms)));
+ return obj;
}
-static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic)
+void JS_AddIntrinsicDate(JSContext *ctx)
{
- JSFloatEnv *fe;
- int b;
- int64_t prec;
+ JSValueConst obj;
- fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
- if (!fe)
- return JS_EXCEPTION;
- switch(magic) {
- case FE_PREC:
- if (JS_ToInt64Sat(ctx, &prec, val))
- return JS_EXCEPTION;
- if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
- return JS_ThrowRangeError(ctx, "invalid precision");
- fe->prec = prec;
- break;
- case FE_EXP:
- if (JS_ToInt32Sat(ctx, &b, val))
- return JS_EXCEPTION;
- if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX)
- return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
- fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) |
- bf_set_exp_bits(b);
- break;
- case FE_RNDMODE:
- b = bigfloat_get_rnd_mode(ctx, val);
- if (b < 0)
- return JS_EXCEPTION;
- fe->flags = (fe->flags & ~BF_RND_MASK) | b;
- break;
- case FE_SUBNORMAL:
- b = JS_ToBool(ctx, val);
- fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0);
- break;
- default:
- b = JS_ToBool(ctx, val);
- fe->status = (fe->status & ~magic) & ((-b) & magic);
- break;
- }
- return JS_UNDEFINED;
+ /* Date */
+ ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs,
+ countof(js_date_proto_funcs));
+ obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7,
+ ctx->class_proto[JS_CLASS_DATE]);
+ JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs));
}
-static JSValue js_float_env_clearStatus(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv)
+/* eval */
+
+void JS_AddIntrinsicEval(JSContext *ctx)
{
- JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
- if (!fe)
- return JS_EXCEPTION;
- fe->status = 0;
- return JS_UNDEFINED;
+ ctx->eval_internal = __JS_EvalInternal;
}
-static const JSCFunctionListEntry js_float_env_funcs[] = {
- JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ),
- JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ),
- JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ),
- JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ),
- JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ),
- JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ),
- JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ),
- JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ),
- JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ),
- JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ),
- JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ),
- JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ),
- JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ),
- JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ),
-};
-
-static const JSCFunctionListEntry js_float_env_proto_funcs[] = {
- JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status,
- js_float_env_proto_set_status, FE_PREC ),
- JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status,
- js_float_env_proto_set_status, FE_EXP ),
- JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status,
- js_float_env_proto_set_status, FE_RNDMODE ),
- JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status,
- js_float_env_proto_set_status, FE_SUBNORMAL ),
- JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status,
- js_float_env_proto_set_status, BF_ST_INVALID_OP ),
- JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status,
- js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ),
- JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status,
- js_float_env_proto_set_status, BF_ST_OVERFLOW ),
- JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status,
- js_float_env_proto_set_status, BF_ST_UNDERFLOW ),
- JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status,
- js_float_env_proto_set_status, BF_ST_INEXACT ),
- JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ),
-};
+/* BigInt */
-void JS_AddIntrinsicBigFloat(JSContext *ctx)
+static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
{
- JSRuntime *rt = ctx->rt;
- JSValueConst obj1;
+ uint32_t tag;
- rt->bigfloat_ops.to_string = js_bigfloat_to_string;
- rt->bigfloat_ops.from_string = js_string_to_bigfloat;
- rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat;
- rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat;
- rt->bigfloat_ops.compare = js_compare_bigfloat;
- rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64;
- rt->bigfloat_ops.mul_pow10 = js_mul_pow10;
-
- ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx);
- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT],
- js_bigfloat_proto_funcs,
- countof(js_bigfloat_proto_funcs));
- obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1,
- ctx->class_proto[JS_CLASS_BIG_FLOAT]);
- JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs,
- countof(js_bigfloat_funcs));
-
- ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx);
- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV],
- js_float_env_proto_funcs,
- countof(js_float_env_proto_funcs));
- obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv",
- js_float_env_constructor, 1,
- ctx->class_proto[JS_CLASS_FLOAT_ENV]);
- JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs,
- countof(js_float_env_funcs));
-}
-
-/* BigDecimal */
-
-static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
- BOOL allow_null_or_undefined)
-{
redo:
- switch(JS_VALUE_GET_NORM_TAG(val)) {
- case JS_TAG_BIG_DECIMAL:
- break;
- case JS_TAG_NULL:
- if (!allow_null_or_undefined)
- goto fail;
- /* fall thru */
- case JS_TAG_BOOL:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
case JS_TAG_INT:
- {
- bfdec_t *r;
- int32_t v = JS_VALUE_GET_INT(val);
-
- val = JS_NewBigDecimal(ctx);
- if (JS_IsException(val))
- break;
- r = JS_GetBigDecimal(val);
- if (bfdec_set_si(r, v)) {
- JS_FreeValue(ctx, val);
- val = JS_EXCEPTION;
- break;
- }
- }
+ case JS_TAG_BOOL:
+ val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
break;
- case JS_TAG_FLOAT64:
+ case JS_TAG_SHORT_BIG_INT:
case JS_TAG_BIG_INT:
- case JS_TAG_BIG_FLOAT:
- val = JS_ToStringFree(ctx, val);
- if (JS_IsException(val))
- break;
- goto redo;
- case JS_TAG_STRING:
+ break;
+ case JS_TAG_FLOAT64:
{
- const char *str, *p;
- size_t len;
- int err;
-
- str = JS_ToCStringLen(ctx, &len, val);
- JS_FreeValue(ctx, val);
- if (!str)
- return JS_EXCEPTION;
- p = str;
- p += skip_spaces(p);
- if ((p - str) == len) {
- bfdec_t *r;
- val = JS_NewBigDecimal(ctx);
- if (JS_IsException(val))
- break;
- r = JS_GetBigDecimal(val);
- bfdec_set_zero(r, 0);
- err = 0;
+ double d = JS_VALUE_GET_FLOAT64(val);
+ JSBigInt *r;
+ int res;
+ r = js_bigint_from_float64(ctx, &res, d);
+ if (!r) {
+ if (res == 0) {
+ val = JS_EXCEPTION;
+ } else if (res == 1) {
+ val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer");
+ } else {
+ val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); }
} else {
- val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL);
- if (JS_IsException(val)) {
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
- }
- p += skip_spaces(p);
- err = ((p - str) != len);
- }
- JS_FreeCString(ctx, str);
- if (err) {
- JS_FreeValue(ctx, val);
- return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal");
+ val = JS_CompactBigInt(ctx, r);
}
}
break;
+ case JS_TAG_STRING:
+ val = JS_StringToBigIntErr(ctx, val);
+ break;
case JS_TAG_OBJECT:
val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
if (JS_IsException(val))
break;
goto redo;
+ case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
- {
- bfdec_t *r;
- if (!allow_null_or_undefined)
- goto fail;
- val = JS_NewBigDecimal(ctx);
- if (JS_IsException(val))
- break;
- r = JS_GetBigDecimal(val);
- bfdec_set_nan(r);
- }
- break;
default:
- fail:
JS_FreeValue(ctx, val);
- return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal");
+ return JS_ThrowTypeError(ctx, "cannot convert to BigInt");
}
return val;
}
-static JSValue js_bigdecimal_constructor(JSContext *ctx,
- JSValueConst new_target,
- int argc, JSValueConst *argv)
+static JSValue js_bigint_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
{
- JSValue val;
if (!JS_IsUndefined(new_target))
return JS_ThrowTypeError(ctx, "not a constructor");
- if (argc == 0) {
- bfdec_t *r;
- val = JS_NewBigDecimal(ctx);
- if (JS_IsException(val))
- return val;
- r = JS_GetBigDecimal(val);
- bfdec_set_zero(r, 0);
- } else {
- val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE);
- }
- return val;
+ return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
}
-static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val)
+static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
{
- if (JS_IsBigDecimal(this_val))
+ if (JS_IsBigInt(ctx, this_val))
return JS_DupValue(ctx, this_val);
if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
JSObject *p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id == JS_CLASS_BIG_DECIMAL) {
- if (JS_IsBigDecimal(p->u.object_data))
+ if (p->class_id == JS_CLASS_BIG_INT) {
+ if (JS_IsBigInt(ctx, p->u.object_data))
return JS_DupValue(ctx, p->u.object_data);
}
}
- return JS_ThrowTypeError(ctx, "not a bigdecimal");
+ return JS_ThrowTypeError(ctx, "not a BigInt");
}
-static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
+static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
JSValue val;
+ int base;
+ JSValue ret;
- val = js_thisBigDecimalValue(ctx, this_val);
+ val = js_thisBigIntValue(ctx, this_val);
if (JS_IsException(val))
return val;
- return JS_ToStringFree(ctx, val);
-}
-
-static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- return js_thisBigDecimalValue(ctx, this_val);
-}
-
-static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj)
-{
- const char *str;
- size_t size;
- int rnd_mode;
-
- str = JS_ToCStringLen(ctx, &size, obj);
- if (!str)
- return -1;
- if (strlen(str) != size)
- goto invalid_rounding_mode;
- if (!strcmp(str, "floor")) {
- rnd_mode = BF_RNDD;
- } else if (!strcmp(str, "ceiling")) {
- rnd_mode = BF_RNDU;
- } else if (!strcmp(str, "down")) {
- rnd_mode = BF_RNDZ;
- } else if (!strcmp(str, "up")) {
- rnd_mode = BF_RNDA;
- } else if (!strcmp(str, "half-even")) {
- rnd_mode = BF_RNDN;
- } else if (!strcmp(str, "half-up")) {
- rnd_mode = BF_RNDNA;
- } else {
- invalid_rounding_mode:
- JS_FreeCString(ctx, str);
- JS_ThrowTypeError(ctx, "invalid rounding mode");
- return -1;
- }
- JS_FreeCString(ctx, str);
- return rnd_mode;
-}
-
-typedef struct {
- int64_t prec;
- bf_flags_t flags;
-} BigDecimalEnv;
-
-static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,
- JSValueConst obj)
-{
- JSValue prop;
- int64_t val;
- BOOL has_prec;
- int rnd_mode;
-
- if (!JS_IsObject(obj)) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- return -1;
- }
- prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode);
- if (JS_IsException(prop))
- return -1;
- rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop);
- JS_FreeValue(ctx, prop);
- if (rnd_mode < 0)
- return -1;
- fe->flags = rnd_mode;
-
- prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits);
- if (JS_IsException(prop))
- return -1;
- has_prec = FALSE;
- if (!JS_IsUndefined(prop)) {
- if (JS_ToInt64SatFree(ctx, &val, prop))
- return -1;
- if (val < 1 || val > BF_PREC_MAX)
- goto invalid_precision;
- fe->prec = val;
- has_prec = TRUE;
- }
-
- prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits);
- if (JS_IsException(prop))
- return -1;
- if (!JS_IsUndefined(prop)) {
- if (has_prec) {
- JS_FreeValue(ctx, prop);
- JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits");
- return -1;
- }
- if (JS_ToInt64SatFree(ctx, &val, prop))
- return -1;
- if (val < 0 || val > BF_PREC_MAX) {
- invalid_precision:
- JS_ThrowTypeError(ctx, "invalid precision");
- return -1;
- }
- fe->prec = val;
- fe->flags |= BF_FLAG_RADPNT_PREC;
- has_prec = TRUE;
- }
- if (!has_prec) {
- JS_ThrowTypeError(ctx, "precision must be present");
- return -1;
- }
- return 0;
-}
-
-
-static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
-{
- bfdec_t *a, *b, r_s, *r = &r_s;
- JSValue op1, op2, res;
- BigDecimalEnv fe_s, *fe = &fe_s;
- int op_count, ret;
-
- if (magic == MATH_OP_SQRT ||
- magic == MATH_OP_ROUND)
- op_count = 1;
- else
- op_count = 2;
-
- op1 = JS_ToNumeric(ctx, argv[0]);
- if (JS_IsException(op1))
- return op1;
- a = JS_ToBigDecimal(ctx, op1);
- if (!a) {
- JS_FreeValue(ctx, op1);
- return JS_EXCEPTION;
- }
- if (op_count >= 2) {
- op2 = JS_ToNumeric(ctx, argv[1]);
- if (JS_IsException(op2)) {
- JS_FreeValue(ctx, op1);
- return op2;
- }
- b = JS_ToBigDecimal(ctx, op2);
- if (!b)
- goto fail;
- } else {
- op2 = JS_UNDEFINED;
- b = NULL;
- }
- fe->flags = BF_RNDZ;
- fe->prec = BF_PREC_INF;
- if (op_count < argc) {
- if (js_bigdecimal_get_env(ctx, fe, argv[op_count]))
- goto fail;
- }
-
- res = JS_NewBigDecimal(ctx);
- if (JS_IsException(res)) {
- fail:
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- return JS_EXCEPTION;
- }
- r = JS_GetBigDecimal(res);
- switch (magic) {
- case MATH_OP_ADD:
- ret = bfdec_add(r, a, b, fe->prec, fe->flags);
- break;
- case MATH_OP_SUB:
- ret = bfdec_sub(r, a, b, fe->prec, fe->flags);
- break;
- case MATH_OP_MUL:
- ret = bfdec_mul(r, a, b, fe->prec, fe->flags);
- break;
- case MATH_OP_DIV:
- ret = bfdec_div(r, a, b, fe->prec, fe->flags);
- break;
- case MATH_OP_FMOD:
- ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
- break;
- case MATH_OP_SQRT:
- ret = bfdec_sqrt(r, a, fe->prec, fe->flags);
- break;
- case MATH_OP_ROUND:
- ret = bfdec_set(r, a);
- if (!(ret & BF_ST_MEM_ERROR))
- ret = bfdec_round(r, fe->prec, fe->flags);
- break;
- default:
- abort();
- }
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP |
- BF_ST_OVERFLOW;
- if (ret != 0) {
- JS_FreeValue(ctx, res);
- return throw_bf_exception(ctx, ret);
+ if (argc == 0 || JS_IsUndefined(argv[0])) {
+ base = 10;
} else {
- return res;
- }
-}
-
-static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
-{
- JSValue val, ret;
- int64_t f;
- int rnd_mode;
-
- val = js_thisBigDecimalValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (JS_ToInt64Sat(ctx, &f, argv[0]))
- goto fail;
- if (f < 0 || f > BF_PREC_MAX) {
- JS_ThrowRangeError(ctx, "invalid number of digits");
- goto fail;
- }
- rnd_mode = BF_RNDNA;
- if (argc > 1) {
- rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
- if (rnd_mode < 0)
+ base = js_get_radix(ctx, argv[0]);
+ if (base < 0)
goto fail;
}
- ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
+ ret = js_bigint_to_string1(ctx, val, base);
JS_FreeValue(ctx, val);
return ret;
fail:
return JS_EXCEPTION;
}
-static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
+static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
- JSValue val, ret;
- int64_t f;
- int rnd_mode;
-
- val = js_thisBigDecimalValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (JS_ToInt64Sat(ctx, &f, argv[0]))
- goto fail;
- if (JS_IsUndefined(argv[0])) {
- ret = js_bigdecimal_to_string1(ctx, val, 0,
- BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
- } else {
- if (f < 0 || f > BF_PREC_MAX) {
- JS_ThrowRangeError(ctx, "invalid number of digits");
- goto fail;
- }
- rnd_mode = BF_RNDNA;
- if (argc > 1) {
- rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
- if (rnd_mode < 0)
- goto fail;
- }
- ret = js_bigdecimal_to_string1(ctx, val, f + 1,
- rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
- }
- JS_FreeValue(ctx, val);
- return ret;
- fail:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
+ return js_thisBigIntValue(ctx, this_val);
}
-static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
+static JSValue js_bigint_asUintN(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv, int asIntN)
{
- JSValue val, ret;
- int64_t p;
- int rnd_mode;
-
- val = js_thisBigDecimalValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (JS_IsUndefined(argv[0])) {
- return JS_ToStringFree(ctx, val);
- }
- if (JS_ToInt64Sat(ctx, &p, argv[0]))
- goto fail;
- if (p < 1 || p > BF_PREC_MAX) {
- JS_ThrowRangeError(ctx, "invalid number of digits");
- goto fail;
- }
- rnd_mode = BF_RNDNA;
- if (argc > 1) {
- rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
- if (rnd_mode < 0)
- goto fail;
+ uint64_t bits;
+ JSValue res, a;
+
+ if (JS_ToIndex(ctx, &bits, argv[0]))
+ return JS_EXCEPTION;
+ a = JS_ToBigInt(ctx, argv[1]);
+ if (JS_IsException(a))
+ return JS_EXCEPTION;
+ if (bits == 0) {
+ JS_FreeValue(ctx, a);
+ res = __JS_NewShortBigInt(ctx, 0);
+ } else if (JS_VALUE_GET_TAG(a) == JS_TAG_SHORT_BIG_INT) {
+ /* fast case */
+ if (bits >= JS_SHORT_BIG_INT_BITS) {
+ res = a;
+ } else {
+ uint64_t v;
+ int shift;
+ shift = 64 - bits;
+ v = JS_VALUE_GET_SHORT_BIG_INT(a);
+ v = v << shift;
+ if (asIntN)
+ v = (int64_t)v >> shift;
+ else
+ v = v >> shift;
+ res = __JS_NewShortBigInt(ctx, v);
+ }
+ } else {
+ JSBigInt *r, *p = JS_VALUE_GET_PTR(a);
+ if (bits >= p->len * JS_LIMB_BITS) {
+ res = a;
+ } else {
+ int len, shift, i;
+ js_limb_t v;
+ len = (bits + JS_LIMB_BITS - 1) / JS_LIMB_BITS;
+ r = js_bigint_new(ctx, len);
+ if (!r) {
+ JS_FreeValue(ctx, a);
+ return JS_EXCEPTION;
+ }
+ r->len = len;
+ for(i = 0; i < len - 1; i++)
+ r->tab[i] = p->tab[i];
+ shift = (-bits) & (JS_LIMB_BITS - 1);
+ /* 0 <= shift <= JS_LIMB_BITS - 1 */
+ v = p->tab[len - 1] << shift;
+ if (asIntN)
+ v = (js_slimb_t)v >> shift;
+ else
+ v = v >> shift;
+ r->tab[len - 1] = v;
+ r = js_bigint_normalize(ctx, r);
+ JS_FreeValue(ctx, a);
+ res = JS_CompactBigInt(ctx, r);
+ }
}
- ret = js_bigdecimal_to_string1(ctx, val, p,
- rnd_mode | BF_FTOA_FORMAT_FIXED);
- JS_FreeValue(ctx, val);
- return ret;
- fail:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
+ return res;
}
-static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = {
- JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ),
- JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ),
- JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ),
- JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ),
- JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ),
+static const JSCFunctionListEntry js_bigint_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),
+ JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),
};
-static const JSCFunctionListEntry js_bigdecimal_funcs[] = {
- JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ),
- JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ),
- JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ),
- JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ),
- JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ),
- JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ),
- JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ),
+static const JSCFunctionListEntry js_bigint_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_bigint_toString ),
+ JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),
};
-void JS_AddIntrinsicBigDecimal(JSContext *ctx)
+void JS_AddIntrinsicBigInt(JSContext *ctx)
{
- JSRuntime *rt = ctx->rt;
JSValueConst obj1;
- rt->bigdecimal_ops.to_string = js_bigdecimal_to_string;
- rt->bigdecimal_ops.from_string = js_string_to_bigdecimal;
- rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal;
- rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal;
- rt->bigdecimal_ops.compare = js_compare_bigdecimal;
-
- ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx);
- JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL],
- js_bigdecimal_proto_funcs,
- countof(js_bigdecimal_proto_funcs));
- obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal",
- js_bigdecimal_constructor, 1,
- ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
- JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs,
- countof(js_bigdecimal_funcs));
-}
-
-void JS_EnableBignumExt(JSContext *ctx, BOOL enable)
-{
- ctx->bignum_ext = enable;
+ ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
+ js_bigint_proto_funcs,
+ countof(js_bigint_proto_funcs));
+ obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1,
+ ctx->class_proto[JS_CLASS_BIG_INT]);
+ JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,
+ countof(js_bigint_funcs));
}
-#endif /* CONFIG_BIGNUM */
-
static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = {
"EvalError", "RangeError", "ReferenceError",
"SyntaxError", "TypeError", "URIError",
v64 = d;
is_int = (v64 == d);
}
- } else if (tag == JS_TAG_BIG_INT) {
- JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]);
-
+ } else if (tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT) {
+ JSBigIntBuf buf1;
+ JSBigInt *p1;
+ int sz = (64 / JS_LIMB_BITS);
+ if (tag == JS_TAG_SHORT_BIG_INT)
+ p1 = js_bigint_set_short(&buf1, argv[0]);
+ else
+ p1 = JS_VALUE_GET_PTR(argv[0]);
+
if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) {
- if (bf_get_int64(&v64, &p1->num, 0) != 0)
- goto done;
+ if (p1->len > sz)
+ goto done; /* does not fit an int64 : cannot be found */
} else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) {
- if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0)
+ if (js_bigint_sign(p1))
+ goto done; /* v < 0 */
+ if (p1->len <= sz) {
+ /* OK */
+ } else if (p1->len == sz + 1 && p1->tab[sz] == 0) {
+ /* 2^63 <= v <= 2^64-1 */
+ } else {
goto done;
+ }
} else {
goto done;
}
+ if (JS_ToBigInt64(ctx, &v64, argv[0]))
+ goto exception;
d = 0;
is_bigint = 1;
} else {
}
break;
case JS_CLASS_BIG_INT64_ARRAY:
- if (is_bigint || (is_math_mode(ctx) && is_int &&
- v64 >= -MAX_SAFE_INTEGER &&
- v64 <= MAX_SAFE_INTEGER)) {
+ if (is_bigint) {
goto scan64;
}
break;
case JS_CLASS_BIG_UINT64_ARRAY:
- if (is_bigint || (is_math_mode(ctx) && is_int &&
- v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) {
+ if (is_bigint) {
const uint64_t *pv;
uint64_t v;
scan64:
return JS_EXCEPTION;
if (size_log2 == 3) {
int64_t v64;
- ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2]));
+ ret = JS_ToBigIntFree(ctx, JS_DupValue(ctx, argv[2]));
if (JS_IsException(ret))
return ret;
if (JS_ToBigInt64(ctx, &v64, ret)) {