mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
1803 lines
48 KiB
C
1803 lines
48 KiB
C
/* Copyright JS Foundation and other contributors, http://js.foundation
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
|
||
#include "ecma-big-uint.h"
|
||
#include "ecma-helpers.h"
|
||
#include "jmem.h"
|
||
#include "lit-char-helpers.h"
|
||
|
||
#if ENABLED (JERRY_BUILTIN_BIGINT)
|
||
|
||
JERRY_STATIC_ASSERT (sizeof (ecma_bigint_two_digits_t) == 2 * sizeof (ecma_bigint_digit_t),
|
||
ecma_big_int_two_digits_must_be_twice_as_long_as_ecma_big_int_digit);
|
||
|
||
JERRY_STATIC_ASSERT ((1 << ECMA_BIGINT_DIGIT_SHIFT) == (8 * sizeof (ecma_bigint_digit_t)),
|
||
ecma_bigint_digit_shift_is_incorrect);
|
||
|
||
JERRY_STATIC_ASSERT ((ECMA_BIG_UINT_BITWISE_DECREASE_LEFT << 1) == ECMA_BIG_UINT_BITWISE_DECREASE_RIGHT,
|
||
ecma_big_uint_bitwise_left_and_right_sub_option_bits_must_follow_each_other);
|
||
|
||
/**
|
||
* Create a new BigInt value
|
||
*
|
||
* @return new BigInt value, NULL on error
|
||
*/
|
||
ecma_extended_primitive_t *
|
||
ecma_bigint_create (uint32_t size) /**< size of the new BigInt value */
|
||
{
|
||
JERRY_ASSERT (size > 0);
|
||
JERRY_ASSERT ((size % sizeof (ecma_bigint_digit_t)) == 0);
|
||
|
||
if (JERRY_UNLIKELY (size > ECMA_BIGINT_MAX_SIZE))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
ecma_extended_primitive_t *value_p;
|
||
|
||
size_t mem_size = ECMA_BIGINT_GET_BYTE_SIZE (size) + sizeof (ecma_extended_primitive_t);
|
||
value_p = (ecma_extended_primitive_t *) jmem_heap_alloc_block_null_on_error (mem_size);
|
||
|
||
if (JERRY_UNLIKELY (value_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
value_p->refs_and_type = ECMA_EXTENDED_PRIMITIVE_REF_ONE | ECMA_TYPE_BIGINT;
|
||
value_p->u.bigint_sign_and_size = size;
|
||
return value_p;
|
||
} /* ecma_bigint_create */
|
||
|
||
/**
|
||
* Extend a BigUInt value with a new data prefix value
|
||
*
|
||
* @return new BigUInt value, NULL on error
|
||
*/
|
||
ecma_extended_primitive_t *
|
||
ecma_big_uint_extend (ecma_extended_primitive_t *value_p, /**< BigUInt value */
|
||
ecma_bigint_digit_t digit) /**< new digit */
|
||
{
|
||
uint32_t old_size = ECMA_BIGINT_GET_SIZE (value_p);
|
||
|
||
if (ECMA_BIGINT_SIZE_IS_ODD (old_size))
|
||
{
|
||
value_p->u.bigint_sign_and_size += (uint32_t) sizeof (ecma_bigint_digit_t);
|
||
*ECMA_BIGINT_GET_DIGITS (value_p, old_size) = digit;
|
||
return value_p;
|
||
}
|
||
|
||
ecma_extended_primitive_t *result_p = ecma_bigint_create (old_size + (uint32_t) sizeof (ecma_bigint_digit_t));
|
||
|
||
if (JERRY_UNLIKELY (result_p == NULL))
|
||
{
|
||
ecma_deref_bigint (value_p);
|
||
return NULL;
|
||
}
|
||
|
||
memcpy (result_p + 1, value_p + 1, old_size);
|
||
ecma_deref_bigint (value_p);
|
||
|
||
*ECMA_BIGINT_GET_DIGITS (result_p, old_size) = digit;
|
||
return result_p;
|
||
} /* ecma_big_uint_extend */
|
||
|
||
/**
|
||
* Count the number of leading zero bits of a digit
|
||
*
|
||
* return number of leading zero bits
|
||
*/
|
||
ecma_bigint_digit_t
|
||
ecma_big_uint_count_leading_zero (ecma_bigint_digit_t digit) /**< digit value */
|
||
{
|
||
ecma_bigint_digit_t shift = 4 * sizeof (ecma_bigint_digit_t);
|
||
ecma_bigint_digit_t result = 8 * sizeof (ecma_bigint_digit_t);
|
||
|
||
do
|
||
{
|
||
ecma_bigint_digit_t value = digit >> shift;
|
||
if (value > 0)
|
||
{
|
||
digit = value;
|
||
result -= shift;
|
||
}
|
||
shift >>= 1;
|
||
}
|
||
while (shift > 0);
|
||
|
||
return result - digit;
|
||
} /* ecma_big_uint_count_leading_zero */
|
||
|
||
/**
|
||
* Helper function which discards the leading zero digits of a BigUInt value
|
||
*
|
||
* @return new BigUInt value, NULL on error
|
||
*/
|
||
static ecma_extended_primitive_t *
|
||
ecma_big_uint_normalize_result (ecma_extended_primitive_t *value_p, /**< BigUInt value */
|
||
ecma_bigint_digit_t *last_digit_p) /**< points to the end of BigUInt */
|
||
{
|
||
JERRY_ASSERT (last_digit_p[-1] == 0);
|
||
|
||
ecma_bigint_digit_t *first_digit_p = ECMA_BIGINT_GET_DIGITS (value_p, 0);
|
||
|
||
/* The following code is tricky. The value stored in first_digit_p[-1] is the size
|
||
* of the BigUInt value, and it cannot be zero. Hence the loop below will terminate. */
|
||
JERRY_ASSERT (first_digit_p[-1] != 0);
|
||
|
||
do
|
||
{
|
||
--last_digit_p;
|
||
}
|
||
while (last_digit_p[-1] == 0);
|
||
|
||
JERRY_ASSERT (last_digit_p >= first_digit_p);
|
||
|
||
if (first_digit_p == last_digit_p)
|
||
{
|
||
ecma_deref_bigint (value_p);
|
||
return ECMA_BIGINT_POINTER_TO_ZERO;
|
||
}
|
||
|
||
uint32_t new_size = (uint32_t) ((uint8_t *) last_digit_p - (uint8_t *) first_digit_p);
|
||
|
||
if (ECMA_BIGINT_SIZE_IS_ODD (new_size)
|
||
&& ((new_size + sizeof (ecma_bigint_digit_t)) == ECMA_BIGINT_GET_SIZE (value_p)))
|
||
{
|
||
value_p->u.bigint_sign_and_size -= (uint32_t) sizeof (ecma_bigint_digit_t);
|
||
return value_p;
|
||
}
|
||
|
||
ecma_extended_primitive_t *result_p = ecma_bigint_create (new_size);
|
||
|
||
if (JERRY_UNLIKELY (result_p == NULL))
|
||
{
|
||
ecma_deref_bigint (value_p);
|
||
return NULL;
|
||
}
|
||
|
||
memcpy (ECMA_BIGINT_GET_DIGITS (result_p, 0), ECMA_BIGINT_GET_DIGITS (value_p, 0), new_size);
|
||
ecma_deref_bigint (value_p);
|
||
|
||
return result_p;
|
||
} /* ecma_big_uint_normalize_result */
|
||
|
||
/**
|
||
* Helper function which increases the result by 1 and extends or shrinks the BigUInt when necessary
|
||
*
|
||
* @return new BigUInt value, NULL on error
|
||
*/
|
||
static ecma_extended_primitive_t *
|
||
ecma_big_uint_increase_result (ecma_extended_primitive_t *value_p) /**< BigUInt value */
|
||
{
|
||
uint32_t size = ECMA_BIGINT_GET_SIZE (value_p);
|
||
|
||
JERRY_ASSERT (size > 0);
|
||
|
||
ecma_bigint_digit_t *first_digit_p = ECMA_BIGINT_GET_DIGITS (value_p, 0);
|
||
ecma_bigint_digit_t *last_digit_p = ECMA_BIGINT_GET_DIGITS (value_p, size);
|
||
|
||
while (*first_digit_p == ~((ecma_bigint_digit_t) 0))
|
||
{
|
||
*first_digit_p++ = 0;
|
||
|
||
if (first_digit_p == last_digit_p)
|
||
{
|
||
return ecma_big_uint_extend (value_p, 1);
|
||
}
|
||
}
|
||
|
||
(*first_digit_p)++;
|
||
|
||
if (last_digit_p[-1] != 0)
|
||
{
|
||
return value_p;
|
||
}
|
||
|
||
return ecma_big_uint_normalize_result (value_p, last_digit_p);
|
||
} /* ecma_big_uint_increase_result */
|
||
|
||
/**
|
||
* Compare two BigUInt numbers
|
||
*
|
||
* return -1, if left value < right value, 0 if they are equal, and 1 otherwise
|
||
*/
|
||
int
|
||
ecma_big_uint_compare (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */
|
||
ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */
|
||
{
|
||
uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p);
|
||
uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p);
|
||
|
||
JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0);
|
||
JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0);
|
||
|
||
if (left_size > right_size)
|
||
{
|
||
return 1;
|
||
}
|
||
|
||
if (left_size < right_size)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
ecma_bigint_digit_t *start_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0);
|
||
ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size);
|
||
ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, left_size);
|
||
|
||
do
|
||
{
|
||
ecma_bigint_digit_t left_value = *(--left_p);
|
||
ecma_bigint_digit_t right_value = *(--right_p);
|
||
|
||
if (left_value < right_value)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
if (left_value > right_value)
|
||
{
|
||
return 1;
|
||
}
|
||
}
|
||
while (left_p > start_p);
|
||
|
||
return 0;
|
||
} /* ecma_big_uint_compare */
|
||
|
||
/**
|
||
* In-place multiply and addition operation with digit
|
||
*
|
||
* return updated value on success, NULL if no memory is available
|
||
*/
|
||
ecma_extended_primitive_t *
|
||
ecma_big_uint_mul_digit (ecma_extended_primitive_t *value_p, /**< BigUInt value */
|
||
ecma_bigint_digit_t mul, /**< multiply value */
|
||
ecma_bigint_digit_t add) /**< addition value */
|
||
{
|
||
JERRY_ASSERT (mul > 1);
|
||
JERRY_ASSERT (add < mul);
|
||
|
||
if (JERRY_UNLIKELY (value_p == NULL))
|
||
{
|
||
JERRY_ASSERT (add > 0);
|
||
|
||
value_p = ecma_bigint_create (sizeof (ecma_bigint_digit_t));
|
||
|
||
if (JERRY_UNLIKELY (value_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
*ECMA_BIGINT_GET_DIGITS (value_p, 0) = add;
|
||
return value_p;
|
||
}
|
||
|
||
uint32_t size = ECMA_BIGINT_GET_SIZE (value_p);
|
||
|
||
JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0);
|
||
|
||
ecma_bigint_digit_t *current_p = ECMA_BIGINT_GET_DIGITS (value_p, 0);
|
||
ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (value_p, size);
|
||
ecma_bigint_digit_t carry = add;
|
||
|
||
do
|
||
{
|
||
ecma_bigint_two_digits_t multiply_result = ((ecma_bigint_two_digits_t) *current_p) * mul;
|
||
ecma_bigint_digit_t multiply_result_low, new_carry;
|
||
|
||
multiply_result_low = (ecma_bigint_digit_t) multiply_result;
|
||
new_carry = (ecma_bigint_digit_t) (multiply_result >> (8 * sizeof (ecma_bigint_digit_t)));
|
||
|
||
multiply_result_low += carry;
|
||
if (multiply_result_low < carry)
|
||
{
|
||
new_carry++;
|
||
}
|
||
|
||
*current_p++ = multiply_result_low;
|
||
carry = new_carry;
|
||
}
|
||
while (current_p < end_p);
|
||
|
||
if (carry == 0)
|
||
{
|
||
return value_p;
|
||
}
|
||
|
||
return ecma_big_uint_extend (value_p, carry);
|
||
} /* ecma_big_uint_mul_digit */
|
||
|
||
/**
|
||
* Convert a BigUInt to a human readable number
|
||
*
|
||
* return char sequence on success, NULL otherwise
|
||
*/
|
||
lit_utf8_byte_t *
|
||
ecma_big_uint_to_string (ecma_extended_primitive_t *value_p, /**< BigUInt value */
|
||
uint32_t radix, /**< radix number between 2 and 36 */
|
||
uint32_t *char_start_p, /**< [out] start offset of numbers */
|
||
uint32_t *char_size_p) /**< [out] size of the output buffer */
|
||
{
|
||
uint32_t size = ECMA_BIGINT_GET_SIZE (value_p);
|
||
|
||
JERRY_ASSERT (radix >= 2 && radix <= 36);
|
||
JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0);
|
||
|
||
uint32_t max_size = size * 8;
|
||
|
||
if (radix < 16)
|
||
{
|
||
if (radix >= 8)
|
||
{
|
||
/* Most frequent case. */
|
||
max_size = (max_size + 2) / 3;
|
||
}
|
||
else if (radix >= 4)
|
||
{
|
||
max_size = (max_size + 1) >> 1;
|
||
}
|
||
}
|
||
else if (radix < 32)
|
||
{
|
||
max_size = (max_size + 3) >> 2;
|
||
}
|
||
else
|
||
{
|
||
max_size = (max_size + 4) / 5;
|
||
}
|
||
|
||
/* This space can be used to store a sign. */
|
||
max_size += (uint32_t) (2 * sizeof (ecma_bigint_digit_t) - 1);
|
||
max_size &= ~(uint32_t) (sizeof (ecma_bigint_digit_t) - 1);
|
||
*char_size_p = max_size;
|
||
|
||
lit_utf8_byte_t *result_p = (lit_utf8_byte_t *) jmem_heap_alloc_block_null_on_error (max_size);
|
||
|
||
if (JERRY_UNLIKELY (result_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
memcpy (result_p, value_p + 1, size);
|
||
|
||
ecma_bigint_digit_t *start_p = (ecma_bigint_digit_t *) (result_p + size);
|
||
ecma_bigint_digit_t *end_p = (ecma_bigint_digit_t *) result_p;
|
||
lit_utf8_byte_t *string_p = result_p + max_size;
|
||
|
||
do
|
||
{
|
||
ecma_bigint_digit_t *current_p = (ecma_bigint_digit_t *) start_p;
|
||
ecma_bigint_digit_t remainder = 0;
|
||
|
||
if (sizeof (uintptr_t) == sizeof (ecma_bigint_two_digits_t))
|
||
{
|
||
do
|
||
{
|
||
ecma_bigint_two_digits_t result = *(--current_p) | ECMA_BIGINT_HIGH_DIGIT (remainder);
|
||
|
||
*current_p = (ecma_bigint_digit_t) (result / radix);
|
||
remainder = (ecma_bigint_digit_t) (result % radix);
|
||
}
|
||
while (current_p > end_p);
|
||
}
|
||
else
|
||
{
|
||
if (ECMA_BIGINT_SIZE_IS_ODD ((uintptr_t) current_p - (uintptr_t) end_p))
|
||
{
|
||
ecma_bigint_digit_t result = *(--current_p);
|
||
*current_p = result / radix;
|
||
remainder = result % radix;
|
||
}
|
||
|
||
while (current_p > end_p)
|
||
{
|
||
/* The following algorithm splits the 64 bit input into three numbers, extend
|
||
* them with remainder, divide them by radix, and updates the three bit ranges
|
||
* corresponding to the three numbers. */
|
||
|
||
const uint32_t extract_bits_low = 10;
|
||
const uint32_t extract_bits_low_mask = (uint32_t) ((1 << extract_bits_low) - 1);
|
||
const uint32_t extract_bits_high = (uint32_t) ((sizeof (ecma_bigint_digit_t) * 8) - extract_bits_low);
|
||
const uint32_t extract_bits_high_mask = (uint32_t) ((1 << extract_bits_high) - 1);
|
||
|
||
ecma_bigint_digit_t result_high = current_p[-1];
|
||
ecma_bigint_digit_t result_mid = (result_high & extract_bits_low_mask) << extract_bits_low;
|
||
|
||
result_high = (result_high >> extract_bits_low) | (remainder << extract_bits_high);
|
||
result_mid |= (result_high % radix) << (extract_bits_low * 2);
|
||
result_high = (result_high / radix) << extract_bits_low;
|
||
|
||
ecma_bigint_digit_t result_low = current_p[-2];
|
||
result_mid |= result_low >> extract_bits_high;
|
||
result_low = (result_low & extract_bits_high_mask) | ((result_mid % radix) << extract_bits_high);
|
||
|
||
result_mid = result_mid / radix;
|
||
|
||
current_p[-1] = result_high | (result_mid >> extract_bits_low);
|
||
current_p[-2] = (result_low / radix) | (result_mid << extract_bits_high);
|
||
|
||
remainder = result_low % radix;
|
||
current_p -= 2;
|
||
}
|
||
}
|
||
|
||
*(--string_p) = (lit_utf8_byte_t) ((remainder < 10) ? (remainder + LIT_CHAR_0)
|
||
: (remainder + (LIT_CHAR_LOWERCASE_A - 10)));
|
||
JERRY_ASSERT (string_p >= (lit_utf8_byte_t *) start_p);
|
||
|
||
if (start_p[-1] == 0)
|
||
{
|
||
start_p--;
|
||
}
|
||
}
|
||
while (start_p > end_p);
|
||
|
||
*char_start_p = (uint32_t) (string_p - result_p);
|
||
return result_p;
|
||
} /* ecma_big_uint_to_string */
|
||
|
||
/**
|
||
* Increase the value of a BigUInt value by 1
|
||
*
|
||
* return new BigUInt value, NULL on error
|
||
*/
|
||
ecma_extended_primitive_t *
|
||
ecma_big_uint_increase (ecma_extended_primitive_t *value_p) /**< BigUInt value */
|
||
{
|
||
uint32_t size = ECMA_BIGINT_GET_SIZE (value_p);
|
||
|
||
JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0);
|
||
|
||
ecma_bigint_digit_t *digits_p = ECMA_BIGINT_GET_DIGITS (value_p, 0);
|
||
ecma_bigint_digit_t *digits_end_p = ECMA_BIGINT_GET_DIGITS (value_p, size);
|
||
|
||
if (JERRY_UNLIKELY (digits_p[0] == ~((ecma_bigint_digit_t) 0) && digits_end_p[-1] == ~((ecma_bigint_digit_t) 0)))
|
||
{
|
||
do
|
||
{
|
||
digits_p++;
|
||
}
|
||
while (digits_p < digits_end_p && digits_p[0] == ~((ecma_bigint_digit_t) 0));
|
||
|
||
if (digits_p == digits_end_p)
|
||
{
|
||
ecma_extended_primitive_t *result_value_p;
|
||
result_value_p = ecma_bigint_create ((uint32_t) (size + sizeof (ecma_bigint_digit_t)));
|
||
|
||
if (JERRY_UNLIKELY (result_value_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
memset (ECMA_BIGINT_GET_DIGITS (result_value_p, 0), 0, size);
|
||
*ECMA_BIGINT_GET_DIGITS (result_value_p, size) = 1;
|
||
return result_value_p;
|
||
}
|
||
|
||
digits_p = ECMA_BIGINT_GET_DIGITS (value_p, 0);
|
||
}
|
||
|
||
ecma_extended_primitive_t *result_value_p = ecma_bigint_create (size);
|
||
|
||
if (JERRY_UNLIKELY (result_value_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0);
|
||
|
||
while (digits_p[0] == ~((ecma_bigint_digit_t) 0))
|
||
{
|
||
digits_p++;
|
||
*result_p++ = 0;
|
||
}
|
||
|
||
*result_p++ = (*digits_p++) + 1;
|
||
|
||
if (digits_p < digits_end_p)
|
||
{
|
||
memcpy (result_p, digits_p, (size_t) ((uint8_t *) digits_end_p - (uint8_t *) digits_p));
|
||
}
|
||
return result_value_p;
|
||
} /* ecma_big_uint_increase */
|
||
|
||
/**
|
||
* Decrease the value of a BigUInt value by 1
|
||
*
|
||
* return new BigUInt value, NULL on error
|
||
*/
|
||
ecma_extended_primitive_t *
|
||
ecma_big_uint_decrease (ecma_extended_primitive_t *value_p) /**< BigUInt value */
|
||
{
|
||
uint32_t size = ECMA_BIGINT_GET_SIZE (value_p);
|
||
|
||
JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0);
|
||
|
||
ecma_bigint_digit_t *digits_p = ECMA_BIGINT_GET_DIGITS (value_p, 0);
|
||
ecma_bigint_digit_t *digits_end_p = ECMA_BIGINT_GET_DIGITS (value_p, size);
|
||
|
||
JERRY_ASSERT (size > sizeof (ecma_bigint_digit_t) || *digits_p > 1);
|
||
|
||
if (JERRY_UNLIKELY (digits_p[0] == 0 && digits_end_p[-1] == 1))
|
||
{
|
||
do
|
||
{
|
||
digits_p++;
|
||
JERRY_ASSERT (digits_p < digits_end_p);
|
||
}
|
||
while (digits_p[0] == 0);
|
||
|
||
if (digits_p + 1 == digits_end_p)
|
||
{
|
||
size -= (uint32_t) sizeof (ecma_bigint_digit_t);
|
||
ecma_extended_primitive_t *result_value_p = ecma_bigint_create (size);
|
||
|
||
if (JERRY_UNLIKELY (result_value_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
memset (ECMA_BIGINT_GET_DIGITS (result_value_p, 0), 0xff, size);
|
||
return result_value_p;
|
||
}
|
||
|
||
digits_p = ECMA_BIGINT_GET_DIGITS (value_p, 0);
|
||
}
|
||
|
||
ecma_extended_primitive_t *result_value_p = ecma_bigint_create (size);
|
||
|
||
if (JERRY_UNLIKELY (result_value_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0);
|
||
|
||
while (digits_p[0] == 0)
|
||
{
|
||
digits_p++;
|
||
*result_p++ = ~((ecma_bigint_digit_t) 0);
|
||
}
|
||
|
||
*result_p++ = (*digits_p++) - 1;
|
||
|
||
if (digits_p < digits_end_p)
|
||
{
|
||
memcpy (result_p, digits_p, (size_t) ((uint8_t *) digits_end_p - (uint8_t *) digits_p));
|
||
}
|
||
return result_value_p;
|
||
} /* ecma_big_uint_decrease */
|
||
|
||
/**
|
||
* Add right BigUInt value to the left BigUInt value
|
||
*
|
||
* return new BigUInt value, NULL on error
|
||
*/
|
||
ecma_extended_primitive_t *
|
||
ecma_big_uint_add (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */
|
||
ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */
|
||
{
|
||
uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p);
|
||
uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p);
|
||
|
||
JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0);
|
||
JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0);
|
||
|
||
if (left_size < right_size)
|
||
{
|
||
/* Swap values. */
|
||
ecma_extended_primitive_t *tmp_value_p = left_value_p;
|
||
left_value_p = right_value_p;
|
||
right_value_p = tmp_value_p;
|
||
|
||
uint32_t tmp_size = left_size;
|
||
left_size = right_size;
|
||
right_size = tmp_size;
|
||
}
|
||
|
||
ecma_extended_primitive_t *result_p = ecma_bigint_create (left_size);
|
||
|
||
if (JERRY_UNLIKELY (result_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
ecma_bigint_digit_t *current_p = ECMA_BIGINT_GET_DIGITS (result_p, 0);
|
||
ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (result_p, right_size);
|
||
ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0);
|
||
ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, 0);
|
||
ecma_bigint_digit_t carry = 0;
|
||
|
||
left_size -= right_size;
|
||
|
||
do
|
||
{
|
||
ecma_bigint_digit_t left = *left_p++;
|
||
|
||
if (carry == 0 || left != ~(ecma_bigint_digit_t) 0)
|
||
{
|
||
left += carry;
|
||
carry = 0;
|
||
}
|
||
else
|
||
{
|
||
left = 0;
|
||
carry = 1;
|
||
}
|
||
|
||
ecma_bigint_digit_t right = *right_p++;
|
||
left += right;
|
||
|
||
if (left < right)
|
||
{
|
||
JERRY_ASSERT (carry == 0);
|
||
carry = 1;
|
||
}
|
||
|
||
*current_p++ = left;
|
||
}
|
||
while (current_p < end_p);
|
||
|
||
end_p = (ecma_bigint_digit_t *) (((uint8_t *) end_p) + left_size);
|
||
|
||
if (carry != 0)
|
||
{
|
||
while (true)
|
||
{
|
||
if (JERRY_UNLIKELY (current_p == end_p))
|
||
{
|
||
return ecma_big_uint_extend (result_p, 1);
|
||
}
|
||
|
||
ecma_bigint_digit_t value = *left_p++;
|
||
|
||
if (value != ~(ecma_bigint_digit_t) 0)
|
||
{
|
||
*current_p++ = value + 1;
|
||
break;
|
||
}
|
||
|
||
*current_p++ = 0;
|
||
}
|
||
}
|
||
|
||
if (current_p < end_p)
|
||
{
|
||
memcpy (current_p, left_p, (size_t) ((uint8_t *) end_p - (uint8_t *) current_p));
|
||
}
|
||
|
||
return result_p;
|
||
} /* ecma_big_uint_add */
|
||
|
||
/**
|
||
* Substract right BigUInt value from the left BigUInt value
|
||
*
|
||
* return new BigUInt value, NULL on error
|
||
*/
|
||
ecma_extended_primitive_t *
|
||
ecma_big_uint_sub (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */
|
||
ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */
|
||
{
|
||
uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p);
|
||
uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p);
|
||
|
||
JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0);
|
||
JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0);
|
||
JERRY_ASSERT (left_size >= right_size);
|
||
|
||
ecma_extended_primitive_t *result_p = ecma_bigint_create (left_size);
|
||
|
||
if (JERRY_UNLIKELY (result_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
ecma_bigint_digit_t *current_p = ECMA_BIGINT_GET_DIGITS (result_p, 0);
|
||
ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (result_p, right_size);
|
||
ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0);
|
||
ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, 0);
|
||
ecma_bigint_digit_t carry = 0;
|
||
|
||
left_size -= right_size;
|
||
|
||
do
|
||
{
|
||
ecma_bigint_digit_t left = *left_p++;
|
||
ecma_bigint_digit_t right = *right_p++;
|
||
|
||
if (carry == 0 || left != 0)
|
||
{
|
||
left -= carry;
|
||
carry = left < right;
|
||
}
|
||
else
|
||
{
|
||
left = ~(ecma_bigint_digit_t) 0;
|
||
carry = 1;
|
||
}
|
||
|
||
*current_p++ = left - right;
|
||
}
|
||
while (current_p < end_p);
|
||
|
||
end_p = (ecma_bigint_digit_t *) (((uint8_t *) end_p) + left_size);
|
||
|
||
if (carry != 0)
|
||
{
|
||
while (true)
|
||
{
|
||
JERRY_ASSERT (current_p < end_p);
|
||
|
||
ecma_bigint_digit_t value = *left_p++;
|
||
|
||
if (value != 0)
|
||
{
|
||
*current_p++ = value - 1;
|
||
break;
|
||
}
|
||
|
||
*current_p++ = ~(ecma_bigint_digit_t) 0;
|
||
}
|
||
}
|
||
|
||
if (current_p < end_p)
|
||
{
|
||
memcpy (current_p, left_p, (size_t) ((uint8_t *) end_p - (uint8_t *) current_p));
|
||
return result_p;
|
||
}
|
||
|
||
if (current_p[-1] != 0)
|
||
{
|
||
return result_p;
|
||
}
|
||
|
||
return ecma_big_uint_normalize_result (result_p, current_p);
|
||
} /* ecma_big_uint_sub */
|
||
|
||
/**
|
||
* Multiply two BigUInt values
|
||
*
|
||
* return new BigUInt value, NULL on error
|
||
*/
|
||
ecma_extended_primitive_t *
|
||
ecma_big_uint_mul (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */
|
||
ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */
|
||
{
|
||
uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p);
|
||
uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p);
|
||
|
||
JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0);
|
||
JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0);
|
||
|
||
if (left_size < right_size)
|
||
{
|
||
/* Swap values. */
|
||
ecma_extended_primitive_t *tmp_value_p = left_value_p;
|
||
left_value_p = right_value_p;
|
||
right_value_p = tmp_value_p;
|
||
|
||
uint32_t tmp_size = left_size;
|
||
left_size = right_size;
|
||
right_size = tmp_size;
|
||
}
|
||
|
||
uint32_t result_size = left_size + right_size - (uint32_t) sizeof (ecma_bigint_digit_t);
|
||
|
||
ecma_extended_primitive_t *result_p = ecma_bigint_create (result_size);
|
||
|
||
if (JERRY_UNLIKELY (result_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
memset (ECMA_BIGINT_GET_DIGITS (result_p, 0), 0, result_size);
|
||
|
||
/* Lower amount of space is allocated by default. This value provides extra space if needed. */
|
||
ecma_bigint_digit_t extra_space[1] = { 0 };
|
||
|
||
ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, 0);
|
||
ecma_bigint_digit_t *right_end_p = ECMA_BIGINT_GET_DIGITS (right_value_p, right_size);
|
||
ecma_bigint_digit_t *left_start_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0);
|
||
ecma_bigint_digit_t *left_end_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size);
|
||
|
||
ecma_bigint_digit_t *result_start_p = ECMA_BIGINT_GET_DIGITS (result_p, 0);
|
||
ecma_bigint_digit_t *result_end_p = ECMA_BIGINT_GET_DIGITS (result_p, result_size);
|
||
|
||
do
|
||
{
|
||
ecma_bigint_two_digits_t right = *right_p++;
|
||
|
||
if (right == 0)
|
||
{
|
||
result_start_p++;
|
||
continue;
|
||
}
|
||
|
||
ecma_bigint_digit_t *left_p = left_start_p;
|
||
ecma_bigint_digit_t *destination_p = result_start_p;
|
||
ecma_bigint_digit_t carry = 0;
|
||
|
||
do
|
||
{
|
||
JERRY_ASSERT (destination_p != (ecma_bigint_digit_t *) (extra_space + 1));
|
||
|
||
ecma_bigint_two_digits_t multiply_result;
|
||
ecma_bigint_digit_t multiply_result_low, new_carry;
|
||
ecma_bigint_digit_t value = *destination_p;
|
||
|
||
multiply_result = ((ecma_bigint_two_digits_t) (*left_p++)) * ((ecma_bigint_two_digits_t) right);
|
||
multiply_result_low = (ecma_bigint_digit_t) multiply_result;
|
||
value += multiply_result_low;
|
||
new_carry = (ecma_bigint_digit_t) (multiply_result >> (8 * sizeof (ecma_bigint_digit_t)));
|
||
|
||
/* The new_carry can never overflow because:
|
||
* a) If left or right is less than 0xff..ff, new_carry will be less than or equal to
|
||
* 0xff...fd, and increasing it by maximum of two (carries) cannot overflow.
|
||
* b) If left and right are both equal to 0xff..ff, multiply_result_low will be 1,
|
||
* and computing value + carry + 1 can only increase new_carry at most once. */
|
||
|
||
if (value < multiply_result_low)
|
||
{
|
||
JERRY_ASSERT (new_carry < ~(ecma_bigint_digit_t) 0);
|
||
new_carry++;
|
||
}
|
||
|
||
value += carry;
|
||
|
||
if (value < carry)
|
||
{
|
||
JERRY_ASSERT (new_carry < ~(ecma_bigint_digit_t) 0);
|
||
new_carry++;
|
||
}
|
||
|
||
carry = new_carry;
|
||
*destination_p++ = value;
|
||
|
||
if (destination_p == result_end_p)
|
||
{
|
||
destination_p = (ecma_bigint_digit_t *) extra_space;
|
||
}
|
||
}
|
||
while (left_p < left_end_p);
|
||
|
||
while (carry > 0)
|
||
{
|
||
JERRY_ASSERT (destination_p != (ecma_bigint_digit_t *) (extra_space + 1));
|
||
|
||
ecma_bigint_digit_t value = *destination_p;
|
||
|
||
value += carry;
|
||
carry = (value < carry);
|
||
|
||
*destination_p++ = value;
|
||
|
||
if (destination_p == result_end_p)
|
||
{
|
||
destination_p = (ecma_bigint_digit_t *) extra_space;
|
||
}
|
||
}
|
||
|
||
result_start_p++;
|
||
}
|
||
while (right_p < right_end_p);
|
||
|
||
if (extra_space[0] == 0)
|
||
{
|
||
return result_p;
|
||
}
|
||
|
||
return ecma_big_uint_extend (result_p, extra_space[0]);
|
||
} /* ecma_big_uint_mul */
|
||
|
||
/**
|
||
* Divide left BigUInt value with right digit value
|
||
*
|
||
* return new BigUInt value, NULL on error
|
||
*/
|
||
static ecma_extended_primitive_t *
|
||
ecma_big_uint_div_digit (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */
|
||
ecma_bigint_digit_t divisor_digit, /**< divisor value */
|
||
bool is_mod) /**< true if return with remainder */
|
||
{
|
||
uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p);
|
||
|
||
JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0);
|
||
JERRY_ASSERT (divisor_digit > 0);
|
||
|
||
ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size - sizeof (ecma_bigint_digit_t));
|
||
ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0);
|
||
|
||
ecma_bigint_digit_t last_digit = *left_p;
|
||
ecma_bigint_digit_t remainder = last_digit % divisor_digit;
|
||
|
||
last_digit = last_digit / divisor_digit;
|
||
|
||
ecma_bigint_digit_t result_size = 0;
|
||
ecma_extended_primitive_t *result_p = NULL;
|
||
ecma_bigint_digit_t *current_p = NULL;
|
||
|
||
if (!is_mod)
|
||
{
|
||
result_size = left_size;
|
||
|
||
if (last_digit == 0)
|
||
{
|
||
result_size -= (uint32_t) sizeof (ecma_bigint_digit_t);
|
||
}
|
||
|
||
result_p = ecma_bigint_create (result_size);
|
||
|
||
if (JERRY_UNLIKELY (result_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
current_p = ECMA_BIGINT_GET_DIGITS (result_p, result_size);
|
||
|
||
if (last_digit != 0)
|
||
{
|
||
*(--current_p) = last_digit;
|
||
}
|
||
}
|
||
|
||
while (left_p > end_p)
|
||
{
|
||
const uint32_t shift = 1 << ECMA_BIGINT_DIGIT_SHIFT;
|
||
|
||
ecma_bigint_two_digits_t result = *(--left_p) | (((ecma_bigint_two_digits_t) remainder) << shift);
|
||
|
||
if (!is_mod)
|
||
{
|
||
*(--current_p) = (ecma_bigint_digit_t) (result / divisor_digit);
|
||
}
|
||
|
||
remainder = (ecma_bigint_digit_t) (result % divisor_digit);
|
||
}
|
||
|
||
if (!is_mod)
|
||
{
|
||
JERRY_ASSERT (current_p == ECMA_BIGINT_GET_DIGITS (result_p, 0));
|
||
return result_p;
|
||
}
|
||
|
||
if (remainder == 0)
|
||
{
|
||
return ECMA_BIGINT_POINTER_TO_ZERO;
|
||
}
|
||
|
||
result_p = ecma_bigint_create (sizeof (ecma_bigint_digit_t));
|
||
|
||
if (JERRY_UNLIKELY (result_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
*ECMA_BIGINT_GET_DIGITS (result_p, 0) = remainder;
|
||
return result_p;
|
||
} /* ecma_big_uint_div_digit */
|
||
|
||
/**
|
||
* Shift left a BigUInt value by a digit value
|
||
*
|
||
* return newly allocated buffer, NULL on error
|
||
*/
|
||
static ecma_bigint_digit_t *
|
||
ecma_big_uint_div_shift_left (ecma_extended_primitive_t *value_p, /**< BigUInt value */
|
||
ecma_bigint_digit_t shift_left, /**< left shift */
|
||
bool extend) /**< extend the result with an extra digit */
|
||
{
|
||
uint32_t size = ECMA_BIGINT_GET_SIZE (value_p);
|
||
|
||
JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0);
|
||
|
||
ecma_bigint_digit_t *source_p = ECMA_BIGINT_GET_DIGITS (value_p, 0);
|
||
ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (value_p, size);
|
||
|
||
if (extend)
|
||
{
|
||
size += (uint32_t) sizeof (ecma_bigint_digit_t);
|
||
}
|
||
|
||
ecma_bigint_digit_t *result_p = (ecma_bigint_digit_t *) jmem_heap_alloc_block_null_on_error (size);
|
||
|
||
if (JERRY_UNLIKELY (result_p == NULL))
|
||
{
|
||
return result_p;
|
||
}
|
||
|
||
if (shift_left == 0)
|
||
{
|
||
JERRY_ASSERT (extend);
|
||
|
||
size -= (uint32_t) sizeof (ecma_bigint_digit_t);
|
||
*(ecma_bigint_digit_t *) (((uint8_t *) result_p) + size) = 0;
|
||
|
||
memcpy (result_p, source_p, size);
|
||
return result_p;
|
||
}
|
||
|
||
ecma_bigint_digit_t *destination_p = result_p;
|
||
ecma_bigint_digit_t carry = 0;
|
||
uint32_t shift_right = (1 << ECMA_BIGINT_DIGIT_SHIFT) - shift_left;
|
||
|
||
do
|
||
{
|
||
ecma_bigint_digit_t value = *source_p++;
|
||
|
||
*destination_p++ = (value << shift_left) | carry;
|
||
carry = value >> shift_right;
|
||
}
|
||
while (source_p < end_p);
|
||
|
||
if (extend)
|
||
{
|
||
*destination_p++ = carry;
|
||
}
|
||
|
||
return result_p;
|
||
} /* ecma_big_uint_div_shift_left */
|
||
|
||
/**
|
||
* Divide left BigUInt value with right BigUInt value
|
||
*
|
||
* return new BigUInt value, NULL on error
|
||
*/
|
||
ecma_extended_primitive_t *
|
||
ecma_big_uint_div_mod (ecma_extended_primitive_t *dividend_value_p, /**< divider BigUInt value */
|
||
ecma_extended_primitive_t *divisor_value_p, /**< divisor BigUInt value */
|
||
bool is_mod) /**< true if return with remainder instead of quotient */
|
||
{
|
||
/* This algorithm is based on Donald Knuth’s "Algorithm D" */
|
||
uint32_t divisor_size = ECMA_BIGINT_GET_SIZE (divisor_value_p);
|
||
|
||
JERRY_ASSERT (divisor_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (divisor_value_p, divisor_size) != 0);
|
||
|
||
/* The divisor must have at least two digits, so the single digit case is handled separately. */
|
||
if (divisor_size == sizeof (ecma_bigint_digit_t))
|
||
{
|
||
return ecma_big_uint_div_digit (dividend_value_p, *ECMA_BIGINT_GET_DIGITS (divisor_value_p, 0), is_mod);
|
||
}
|
||
|
||
/* D1. [Normalize] */
|
||
ecma_bigint_digit_t divisor_high = ECMA_BIGINT_GET_LAST_DIGIT (divisor_value_p, divisor_size);
|
||
ecma_bigint_digit_t shift_left = ecma_big_uint_count_leading_zero (divisor_high);
|
||
ecma_bigint_digit_t *buffer_p = ecma_big_uint_div_shift_left (dividend_value_p, shift_left, true);
|
||
|
||
if (JERRY_UNLIKELY (buffer_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
uint32_t dividend_size = ECMA_BIGINT_GET_SIZE (dividend_value_p);
|
||
ecma_extended_primitive_t *result_p = NULL;
|
||
ecma_bigint_digit_t *divisor_p;
|
||
|
||
JERRY_ASSERT (dividend_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (dividend_value_p, dividend_size) != 0);
|
||
JERRY_ASSERT (dividend_size >= divisor_size);
|
||
|
||
if (shift_left > 0)
|
||
{
|
||
divisor_p = ecma_big_uint_div_shift_left (divisor_value_p, shift_left, false);
|
||
|
||
if (JERRY_UNLIKELY (divisor_p == NULL))
|
||
{
|
||
goto error;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
divisor_p = ECMA_BIGINT_GET_DIGITS (divisor_value_p, 0);
|
||
}
|
||
|
||
ecma_bigint_digit_t *dividend_end_p = (ecma_bigint_digit_t *) (((uint8_t *) buffer_p) + dividend_size);
|
||
ecma_bigint_digit_t *dividend_p = (ecma_bigint_digit_t *) (((uint8_t *) dividend_end_p) - divisor_size);
|
||
ecma_bigint_digit_t *divisor_end_p = (ecma_bigint_digit_t *) (((uint8_t *) divisor_p) + divisor_size);
|
||
ecma_bigint_digit_t divisor_low = divisor_end_p[-2];
|
||
|
||
divisor_high = divisor_end_p[-1];
|
||
JERRY_ASSERT ((divisor_high & (((ecma_bigint_digit_t) 1) << (8 * sizeof (ecma_bigint_digit_t) - 1))) != 0);
|
||
|
||
do
|
||
{
|
||
/* D3. [Calculate Q′] */
|
||
ecma_bigint_digit_t result_div;
|
||
|
||
/* This do-while(false) statement allows local declarations and early exit. */
|
||
do
|
||
{
|
||
ecma_bigint_digit_t result_mod;
|
||
|
||
if (dividend_end_p[0] < divisor_high)
|
||
{
|
||
ecma_bigint_two_digits_t dividend = dividend_end_p[-1] | ECMA_BIGINT_HIGH_DIGIT (dividend_end_p[0]);
|
||
result_div = (ecma_bigint_digit_t) (dividend / divisor_high);
|
||
result_mod = (ecma_bigint_digit_t) (dividend % divisor_high);
|
||
}
|
||
else
|
||
{
|
||
JERRY_ASSERT (dividend_end_p[0] == divisor_high && dividend_end_p[-1] < divisor_high);
|
||
|
||
result_div = ~((ecma_bigint_digit_t) 0);
|
||
result_mod = dividend_end_p[-1] + divisor_high;
|
||
|
||
if (result_mod < divisor_high)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
ecma_bigint_two_digits_t low_digits = ((ecma_bigint_two_digits_t) result_div) * divisor_low;
|
||
|
||
while (low_digits > (ECMA_BIGINT_HIGH_DIGIT (result_mod) | divisor_low))
|
||
{
|
||
result_div--;
|
||
result_mod += divisor_high;
|
||
|
||
/* If result_mod becomes a two digit long number, the condition of the loop must be true,
|
||
* so the loop can be aborted. This loop stops after maximum of two iterations, since
|
||
* the highest bit of divisor_high is set. */
|
||
if (result_mod < divisor_high)
|
||
{
|
||
break;
|
||
}
|
||
|
||
/* Subtraction is faster than recomputing result_div * divisor_low. */
|
||
low_digits -= divisor_low;
|
||
}
|
||
}
|
||
while (false);
|
||
|
||
/* D4. [Multiply and subtract] */
|
||
ecma_bigint_digit_t *destination_p = dividend_p;
|
||
ecma_bigint_digit_t *source_p = divisor_p;
|
||
ecma_bigint_digit_t carry = 0;
|
||
|
||
do
|
||
{
|
||
ecma_bigint_two_digits_t multiply_result = ((ecma_bigint_two_digits_t) (*source_p++)) * result_div;
|
||
ecma_bigint_digit_t multiply_result_low, new_carry;
|
||
ecma_bigint_digit_t value = *destination_p;
|
||
|
||
/* The new carry never overflows. See the comment in ecma_big_uint_mul. */
|
||
new_carry = (ecma_bigint_digit_t) (multiply_result >> (8 * sizeof (ecma_bigint_digit_t)));
|
||
multiply_result_low = (ecma_bigint_digit_t) multiply_result;
|
||
|
||
if (value < multiply_result_low)
|
||
{
|
||
new_carry++;
|
||
}
|
||
|
||
value -= multiply_result_low;
|
||
|
||
if (value < carry)
|
||
{
|
||
new_carry++;
|
||
}
|
||
|
||
*destination_p++ = value - carry;
|
||
carry = new_carry;
|
||
}
|
||
while (source_p < divisor_end_p);
|
||
|
||
bool negative_result = *destination_p < carry;
|
||
*destination_p -= carry;
|
||
|
||
if (negative_result)
|
||
{
|
||
/* D6. [Add back] */
|
||
result_div--;
|
||
|
||
destination_p = dividend_p;
|
||
source_p = divisor_p;
|
||
carry = 0;
|
||
|
||
do
|
||
{
|
||
ecma_bigint_digit_t left = *destination_p;
|
||
|
||
if (carry == 0 || left != ~(ecma_bigint_digit_t) 0)
|
||
{
|
||
left += carry;
|
||
carry = 0;
|
||
}
|
||
else
|
||
{
|
||
left = 0;
|
||
carry = 1;
|
||
}
|
||
|
||
ecma_bigint_digit_t right = *source_p++;
|
||
left += right;
|
||
|
||
if (left < right)
|
||
{
|
||
JERRY_ASSERT (carry == 0);
|
||
carry = 1;
|
||
}
|
||
|
||
*destination_p++ = left;
|
||
}
|
||
while (source_p < divisor_end_p);
|
||
}
|
||
|
||
*dividend_end_p = result_div;
|
||
|
||
dividend_p--;
|
||
dividend_end_p--;
|
||
}
|
||
while (dividend_p >= buffer_p);
|
||
|
||
ecma_bigint_digit_t *source_p;
|
||
ecma_bigint_digit_t *source_end_p;
|
||
|
||
if (is_mod)
|
||
{
|
||
source_p = buffer_p;
|
||
source_end_p = dividend_end_p;
|
||
|
||
while (source_end_p > source_p && *source_end_p == 0)
|
||
{
|
||
source_end_p--;
|
||
}
|
||
|
||
if ((*source_end_p >> shift_left) != 0)
|
||
{
|
||
source_end_p++;
|
||
/* This is required to reset carry below. */
|
||
*source_end_p = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
source_p = dividend_end_p + 1;
|
||
source_end_p = (ecma_bigint_digit_t *) (((uint8_t *) buffer_p) + dividend_size);
|
||
|
||
if (*source_end_p != 0)
|
||
{
|
||
source_end_p++;
|
||
}
|
||
}
|
||
|
||
result_p = ECMA_BIGINT_POINTER_TO_ZERO;
|
||
|
||
if (source_p < source_end_p)
|
||
{
|
||
result_p = ecma_bigint_create ((uint32_t) ((uint8_t *) source_end_p - (uint8_t *) source_p));
|
||
|
||
if (result_p != NULL)
|
||
{
|
||
ecma_bigint_digit_t *destination_p = ECMA_BIGINT_GET_DIGITS (result_p, 0);
|
||
|
||
if (is_mod && shift_left > 0)
|
||
{
|
||
ecma_bigint_digit_t shift_right = shift_left;
|
||
|
||
shift_left = (ecma_bigint_digit_t) (8 * (sizeof (ecma_bigint_digit_t)) - shift_left);
|
||
destination_p += source_end_p - source_p;
|
||
|
||
ecma_bigint_digit_t carry = *source_end_p << shift_left;
|
||
|
||
do
|
||
{
|
||
ecma_bigint_digit_t value = *(--source_end_p);
|
||
|
||
*(--destination_p) = (value >> shift_right) | carry;
|
||
carry = value << shift_left;
|
||
}
|
||
while (source_end_p > source_p);
|
||
}
|
||
else
|
||
{
|
||
memcpy (destination_p, source_p, (size_t) ((uint8_t *) source_end_p - (uint8_t *) source_p));
|
||
}
|
||
}
|
||
}
|
||
|
||
error:
|
||
jmem_heap_free_block (buffer_p, dividend_size + sizeof (ecma_bigint_digit_t));
|
||
|
||
if (shift_left > 0 && divisor_p != NULL)
|
||
{
|
||
jmem_heap_free_block (divisor_p, divisor_size);
|
||
}
|
||
|
||
return result_p;
|
||
} /* ecma_big_uint_div_mod */
|
||
|
||
/**
|
||
* Shift left BigUInt values by an uint32 value
|
||
*
|
||
* return new BigUInt value, NULL on error
|
||
*/
|
||
ecma_extended_primitive_t *
|
||
ecma_big_uint_shift_left (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */
|
||
uint32_t right_value) /**< shift value */
|
||
{
|
||
JERRY_ASSERT (right_value > 0);
|
||
|
||
uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p);
|
||
JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0);
|
||
|
||
uint32_t zero_size = (right_value >> ECMA_BIGINT_DIGIT_SHIFT) * (uint32_t) sizeof (ecma_bigint_digit_t);
|
||
uint32_t result_size = left_size + zero_size;
|
||
|
||
uint32_t shift_left = right_value & ((1 << ECMA_BIGINT_DIGIT_SHIFT) - 1);
|
||
uint32_t shift_right = (1 << ECMA_BIGINT_DIGIT_SHIFT) - shift_left;
|
||
|
||
if (shift_left > 0 && (ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) >> shift_right) != 0)
|
||
{
|
||
result_size += (uint32_t) sizeof (ecma_bigint_digit_t);
|
||
}
|
||
|
||
if (result_size > ECMA_BIGINT_MAX_SIZE)
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
ecma_extended_primitive_t *result_value_p = ecma_bigint_create (result_size);
|
||
|
||
if (JERRY_UNLIKELY (result_value_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0);
|
||
ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0);
|
||
|
||
if (zero_size > 0)
|
||
{
|
||
memset (result_p, 0, zero_size);
|
||
result_p = (ecma_bigint_digit_t *) (((uint8_t *) result_p) + zero_size);
|
||
}
|
||
|
||
if (shift_left == 0)
|
||
{
|
||
/* Shift by full digits. */
|
||
memcpy (result_p, left_p, left_size);
|
||
return result_value_p;
|
||
}
|
||
|
||
ecma_bigint_digit_t *left_end_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size);
|
||
ecma_bigint_digit_t carry = 0;
|
||
|
||
do
|
||
{
|
||
ecma_bigint_digit_t value = *left_p++;
|
||
|
||
*result_p++ = (value << shift_left) | carry;
|
||
carry = value >> shift_right;
|
||
}
|
||
while (left_p < left_end_p);
|
||
|
||
if (carry > 0)
|
||
{
|
||
*result_p = carry;
|
||
}
|
||
|
||
return result_value_p;
|
||
} /* ecma_big_uint_shift_left */
|
||
|
||
/**
|
||
* Shift right BigUInt values by an uint32 value
|
||
*
|
||
* @return new BigUInt value, NULL on error
|
||
*/
|
||
ecma_extended_primitive_t *
|
||
ecma_big_uint_shift_right (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */
|
||
uint32_t right_value, /**< shift value */
|
||
bool increase_result) /**< increase result */
|
||
{
|
||
JERRY_ASSERT (right_value > 0);
|
||
|
||
uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p);
|
||
JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0);
|
||
|
||
uint32_t crop_size = (right_value >> ECMA_BIGINT_DIGIT_SHIFT) * (uint32_t) sizeof (ecma_bigint_digit_t);
|
||
|
||
uint32_t shift_right = right_value & ((1 << ECMA_BIGINT_DIGIT_SHIFT) - 1);
|
||
uint32_t shift_left = (1 << ECMA_BIGINT_DIGIT_SHIFT) - shift_right;
|
||
ecma_bigint_digit_t carry = 0;
|
||
|
||
if (shift_right > 0
|
||
&& (ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) >> shift_right) == 0)
|
||
{
|
||
carry = ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) << shift_left;
|
||
left_size -= (uint32_t) sizeof (ecma_bigint_digit_t);
|
||
}
|
||
|
||
if (left_size <= crop_size)
|
||
{
|
||
if (JERRY_LIKELY (!increase_result))
|
||
{
|
||
return ECMA_BIGINT_POINTER_TO_ZERO;
|
||
}
|
||
|
||
ecma_extended_primitive_t *result_value_p = ecma_bigint_create (sizeof (ecma_bigint_digit_t));
|
||
if (result_value_p != NULL)
|
||
{
|
||
*ECMA_BIGINT_GET_DIGITS (result_value_p, 0) = 1;
|
||
}
|
||
return result_value_p;
|
||
}
|
||
|
||
if (JERRY_UNLIKELY (increase_result)
|
||
&& (shift_right == 0
|
||
|| (*ECMA_BIGINT_GET_DIGITS (left_value_p, crop_size) << shift_left) == 0))
|
||
{
|
||
ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0);
|
||
ecma_bigint_digit_t *left_end_p = ECMA_BIGINT_GET_DIGITS (left_value_p, crop_size);
|
||
|
||
while (left_p < left_end_p)
|
||
{
|
||
if (*left_p != 0)
|
||
{
|
||
break;
|
||
}
|
||
left_p++;
|
||
}
|
||
|
||
if (left_p == left_end_p)
|
||
{
|
||
increase_result = false;
|
||
}
|
||
}
|
||
|
||
uint32_t size = left_size - crop_size;
|
||
ecma_extended_primitive_t *result_value_p = ecma_bigint_create (size);
|
||
|
||
if (JERRY_UNLIKELY (result_value_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
if (shift_right == 0)
|
||
{
|
||
memcpy (ECMA_BIGINT_GET_DIGITS (result_value_p, 0), ECMA_BIGINT_GET_DIGITS (left_value_p, crop_size), size);
|
||
|
||
if (JERRY_LIKELY (!increase_result))
|
||
{
|
||
return result_value_p;
|
||
}
|
||
return ecma_big_uint_increase_result (result_value_p);
|
||
}
|
||
|
||
ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size);
|
||
ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, size);
|
||
ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0);
|
||
|
||
do
|
||
{
|
||
ecma_bigint_digit_t value = *(--left_p);
|
||
|
||
*(--result_p) = (value >> shift_right) | carry;
|
||
carry = value << shift_left;
|
||
}
|
||
while (result_p > end_p);
|
||
|
||
if (JERRY_LIKELY (!increase_result))
|
||
{
|
||
return result_value_p;
|
||
}
|
||
return ecma_big_uint_increase_result (result_value_p);
|
||
} /* ecma_big_uint_shift_right */
|
||
|
||
#if ENABLED (JERRY_ESNEXT)
|
||
|
||
/**
|
||
* Compute the left value raised to the power of right value
|
||
*
|
||
* return new BigUInt value, NULL on error
|
||
*/
|
||
ecma_extended_primitive_t *
|
||
ecma_big_uint_pow (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */
|
||
uint32_t right_value) /**< power value */
|
||
{
|
||
ecma_extended_primitive_t *result_p = ECMA_BIGINT_NUMBER_IS_ODD (right_value) ? left_value_p : NULL;
|
||
ecma_extended_primitive_t *square_p = left_value_p;
|
||
|
||
JERRY_ASSERT (right_value >= 2);
|
||
|
||
while (true)
|
||
{
|
||
ecma_extended_primitive_t *new_square_p = ecma_big_uint_mul (square_p, square_p);
|
||
|
||
if (JERRY_UNLIKELY (new_square_p == NULL))
|
||
{
|
||
if (result_p != NULL && result_p != left_value_p)
|
||
{
|
||
ecma_deref_bigint (result_p);
|
||
}
|
||
result_p = NULL;
|
||
break;
|
||
}
|
||
|
||
if (square_p != left_value_p)
|
||
{
|
||
ecma_deref_bigint (square_p);
|
||
}
|
||
|
||
square_p = new_square_p;
|
||
right_value >>= 1;
|
||
|
||
if (ECMA_BIGINT_NUMBER_IS_ODD (right_value))
|
||
{
|
||
if (result_p != NULL)
|
||
{
|
||
ecma_extended_primitive_t *new_result_p = ecma_big_uint_mul (square_p, result_p);
|
||
|
||
if (result_p != left_value_p)
|
||
{
|
||
ecma_deref_bigint (result_p);
|
||
}
|
||
|
||
result_p = new_result_p;
|
||
}
|
||
else
|
||
{
|
||
ecma_ref_extended_primitive (square_p);
|
||
result_p = square_p;
|
||
}
|
||
|
||
if (JERRY_UNLIKELY (result_p == NULL) || right_value == 1)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (square_p != left_value_p)
|
||
{
|
||
ecma_deref_bigint (square_p);
|
||
}
|
||
|
||
return result_p;
|
||
} /* ecma_big_uint_pow */
|
||
|
||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||
|
||
/**
|
||
* Perform bitwise operations on two BigUInt numbers
|
||
*
|
||
* return new BigUInt value, NULL on error
|
||
*/
|
||
ecma_extended_primitive_t *
|
||
ecma_big_uint_bitwise_op (uint32_t operation_and_options, /**< bitwise operation type and options */
|
||
ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */
|
||
ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */
|
||
{
|
||
uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p);
|
||
uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p);
|
||
|
||
JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0);
|
||
JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0);
|
||
|
||
uint32_t operation_type = ECMA_BIGINT_BITWISE_GET_OPERATION_TYPE (operation_and_options);
|
||
|
||
switch (operation_type)
|
||
{
|
||
case ECMA_BIG_UINT_BITWISE_AND:
|
||
{
|
||
if (left_size > right_size)
|
||
{
|
||
left_size = right_size;
|
||
break;
|
||
}
|
||
/* FALLTHRU */
|
||
}
|
||
case ECMA_BIG_UINT_BITWISE_AND_NOT:
|
||
{
|
||
if (right_size > left_size)
|
||
{
|
||
right_size = left_size;
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
JERRY_ASSERT (operation_type == ECMA_BIG_UINT_BITWISE_OR
|
||
|| operation_type == ECMA_BIG_UINT_BITWISE_XOR);
|
||
|
||
if (right_size <= left_size)
|
||
{
|
||
break;
|
||
}
|
||
|
||
/* Swap values. */
|
||
ecma_extended_primitive_t *tmp_value_p = left_value_p;
|
||
left_value_p = right_value_p;
|
||
right_value_p = tmp_value_p;
|
||
|
||
uint32_t tmp_size = left_size;
|
||
left_size = right_size;
|
||
right_size = tmp_size;
|
||
|
||
uint32_t decrease_opts = (operation_and_options & ECMA_BIG_UINT_BITWISE_DECREASE_BOTH);
|
||
|
||
/* When exactly one bit is set, invert both bits. */
|
||
if (decrease_opts >= ECMA_BIG_UINT_BITWISE_DECREASE_LEFT
|
||
&& decrease_opts <= ECMA_BIG_UINT_BITWISE_DECREASE_RIGHT)
|
||
{
|
||
operation_and_options ^= ECMA_BIG_UINT_BITWISE_DECREASE_BOTH;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
ecma_extended_primitive_t *result_value_p = ecma_bigint_create (left_size);
|
||
|
||
if (JERRY_UNLIKELY (result_value_p == NULL))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0);
|
||
ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, 0);
|
||
ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0);
|
||
ecma_bigint_digit_t *result_end_p = ECMA_BIGINT_GET_DIGITS (result_value_p, right_size);
|
||
|
||
if (!(operation_and_options & ECMA_BIG_UINT_BITWISE_DECREASE_BOTH))
|
||
{
|
||
JERRY_ASSERT (!(operation_and_options & ECMA_BIG_UINT_BITWISE_INCREASE_RESULT));
|
||
|
||
if (operation_type == ECMA_BIG_UINT_BITWISE_AND)
|
||
{
|
||
do
|
||
{
|
||
*result_p++ = *left_p++ & *right_p++;
|
||
}
|
||
while (result_p < result_end_p);
|
||
|
||
if (result_p[-1] == 0)
|
||
{
|
||
return ecma_big_uint_normalize_result (result_value_p, result_p);
|
||
}
|
||
return result_value_p;
|
||
}
|
||
|
||
if (operation_type == ECMA_BIG_UINT_BITWISE_OR)
|
||
{
|
||
do
|
||
{
|
||
*result_p++ = *left_p++ | *right_p++;
|
||
}
|
||
while (result_p < result_end_p);
|
||
|
||
if (left_size > right_size)
|
||
{
|
||
memcpy (result_p, left_p, left_size - right_size);
|
||
}
|
||
return result_value_p;
|
||
}
|
||
|
||
JERRY_ASSERT (operation_type == ECMA_BIG_UINT_BITWISE_XOR);
|
||
|
||
do
|
||
{
|
||
*result_p++ = *left_p++ ^ *right_p++;
|
||
}
|
||
while (result_p < result_end_p);
|
||
|
||
if (left_size > right_size)
|
||
{
|
||
memcpy (result_p, left_p, left_size - right_size);
|
||
return result_value_p;
|
||
}
|
||
|
||
if (result_p[-1] == 0)
|
||
{
|
||
return ecma_big_uint_normalize_result (result_value_p, result_p);
|
||
}
|
||
return result_value_p;
|
||
}
|
||
|
||
uint32_t left_carry = 0, right_carry = 0;
|
||
|
||
if (operation_and_options & ECMA_BIG_UINT_BITWISE_DECREASE_LEFT)
|
||
{
|
||
left_carry = 1;
|
||
}
|
||
|
||
if (operation_and_options & ECMA_BIG_UINT_BITWISE_DECREASE_RIGHT)
|
||
{
|
||
right_carry = 1;
|
||
}
|
||
|
||
do
|
||
{
|
||
ecma_bigint_digit_t left = (*left_p++) - left_carry;
|
||
|
||
if (left != ~((ecma_bigint_digit_t) 0))
|
||
{
|
||
left_carry = 0;
|
||
}
|
||
|
||
ecma_bigint_digit_t right = (*right_p++) - right_carry;
|
||
|
||
if (right != ~((ecma_bigint_digit_t) 0))
|
||
{
|
||
right_carry = 0;
|
||
}
|
||
|
||
switch (operation_type)
|
||
{
|
||
case ECMA_BIG_UINT_BITWISE_AND:
|
||
{
|
||
*result_p++ = left & right;
|
||
break;
|
||
}
|
||
case ECMA_BIG_UINT_BITWISE_OR:
|
||
{
|
||
*result_p++ = left | right;
|
||
break;
|
||
}
|
||
case ECMA_BIG_UINT_BITWISE_XOR:
|
||
{
|
||
*result_p++ = left ^ right;
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
JERRY_ASSERT (operation_type == ECMA_BIG_UINT_BITWISE_AND_NOT);
|
||
*result_p++ = left & ~right;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
while (result_p < result_end_p);
|
||
|
||
if (operation_type != ECMA_BIG_UINT_BITWISE_AND)
|
||
{
|
||
result_end_p = ECMA_BIGINT_GET_DIGITS (result_value_p, left_size);
|
||
|
||
if (left_carry > 0)
|
||
{
|
||
while (*left_p == 0)
|
||
{
|
||
*result_p++ = ~((ecma_bigint_digit_t) 0);
|
||
left_p++;
|
||
|
||
JERRY_ASSERT (result_p < result_end_p);
|
||
}
|
||
|
||
*result_p++ = *left_p++ - 1;
|
||
}
|
||
|
||
if (result_p < result_end_p)
|
||
{
|
||
memcpy (result_p, left_p, (size_t) ((uint8_t *) result_end_p - (uint8_t *) result_p));
|
||
|
||
if (operation_and_options & ECMA_BIG_UINT_BITWISE_INCREASE_RESULT)
|
||
{
|
||
return ecma_big_uint_increase_result (result_value_p);
|
||
}
|
||
return result_value_p;
|
||
}
|
||
}
|
||
|
||
if (operation_and_options & ECMA_BIG_UINT_BITWISE_INCREASE_RESULT)
|
||
{
|
||
return ecma_big_uint_increase_result (result_value_p);
|
||
}
|
||
|
||
if (result_p[-1] == 0)
|
||
{
|
||
return ecma_big_uint_normalize_result (result_value_p, result_p);
|
||
}
|
||
return result_value_p;
|
||
} /* ecma_big_uint_bitwise_op */
|
||
|
||
#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */
|