/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef vm_BigIntType_h
#define vm_BigIntType_h

#include "mozilla/Assertions.h"
#include "mozilla/Maybe.h"
#include "mozilla/OperatorNewExtensions.h"
#include "mozilla/Range.h"
#include "mozilla/Span.h"

#include "jstypes.h"

#include "gc/Cell.h"
#include "gc/GCEnum.h"
#include "gc/StoreBuffer.h"
#include "js/Result.h"
#include "js/RootingAPI.h"
#include "js/TraceKind.h"
#include "js/TypeDecls.h"

namespace js {

namespace gc {
class CellAllocator;
class TenuringTracer;
}  // namespace gc

namespace jit {
class MacroAssembler;
}  // namespace jit

}  // namespace js

namespace js {

class JS_PUBLIC_API GenericPrinter;
class JSONPrinter;

}  // namespace js

namespace JS {

class JS_PUBLIC_API BigInt;

class BigInt final : public js::gc::CellWithLengthAndFlags {
  friend class js::gc::CellAllocator;

  BigInt() = default;

 public:
  using Digit = uintptr_t;

 private:
  // The low CellFlagBitsReservedForGC flag bits are reserved.
  static constexpr uintptr_t SignBit =
      js::Bit(js::gc::CellFlagBitsReservedForGC);

  static constexpr size_t InlineDigitsLength =
      (js::gc::MinCellSize - sizeof(CellWithLengthAndFlags)) / sizeof(Digit);

 public:
  // The number of digits and the flags are stored in the cell header.
  size_t digitLength() const { return headerLengthField(); }

 private:
  // The digit storage starts with the least significant digit (little-endian
  // digit order).  Byte order within a digit is of course native endian.
  union {
    Digit* heapDigits_;
    Digit inlineDigits_[InlineDigitsLength];
  };

  void setLengthAndFlags(uint32_t len, uint32_t flags) {
    setHeaderLengthAndFlags(len, flags);
  }

 public:
  static const JS::TraceKind TraceKind = JS::TraceKind::BigInt;

  void fixupAfterMovingGC() {}

  js::gc::AllocKind getAllocKind() const { return js::gc::AllocKind::BIGINT; }

  // Offset for direct access from JIT code.
  static constexpr size_t offsetOfDigitLength() {
    return offsetOfHeaderLength();
  }

  bool hasInlineDigits() const { return digitLength() <= InlineDigitsLength; }
  bool hasHeapDigits() const { return !hasInlineDigits(); }

  using Digits = mozilla::Span<Digit>;
  Digits digits() {
    return Digits(hasInlineDigits() ? inlineDigits_ : heapDigits_,
                  digitLength());
  }
  using ConstDigits = mozilla::Span<const Digit>;
  ConstDigits digits() const {
    return ConstDigits(hasInlineDigits() ? inlineDigits_ : heapDigits_,
                       digitLength());
  }
  Digit digit(size_t idx) const { return digits()[idx]; }
  void setDigit(size_t idx, Digit digit) { digits()[idx] = digit; }

  bool isZero() const { return digitLength() == 0; }
  bool isNegative() const { return headerFlagsField() & SignBit; }

  int32_t sign() const { return isZero() ? 0 : isNegative() ? -1 : 1; }

  void initializeDigitsToZero();

  void traceChildren(JSTracer* trc);

  static MOZ_ALWAYS_INLINE void postWriteBarrier(void* cellp, BigInt* prev,
                                                 BigInt* next) {
    js::gc::PostWriteBarrierImpl<BigInt>(cellp, prev, next);
  }

  void finalize(JS::GCContext* gcx);
  js::HashNumber hash() const;
  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
  size_t sizeOfExcludingThisInNursery(mozilla::MallocSizeOf mallocSizeOf) const;

  static BigInt* createUninitialized(JSContext* cx, size_t digitLength,
                                     bool isNegative,
                                     js::gc::Heap heap = js::gc::Heap::Default);
  static BigInt* createFromDouble(JSContext* cx, double d);
  static BigInt* createFromUint64(JSContext* cx, uint64_t n,
                                  js::gc::Heap heap = js::gc::Heap::Default);
  static BigInt* createFromInt64(JSContext* cx, int64_t n,
                                 js::gc::Heap heap = js::gc::Heap::Default);
  static BigInt* createFromIntPtr(JSContext* cx, intptr_t n);
  static BigInt* createFromDigit(JSContext* cx, Digit d, bool isNegative,
                                 js::gc::Heap heap = js::gc::Heap::Default);
  static BigInt* createFromNonZeroRawUint64(JSContext* cx, uint64_t n,
                                            bool isNegative);
  // FIXME: Cache these values.
  static BigInt* zero(JSContext* cx, js::gc::Heap heap = js::gc::Heap::Default);
  static BigInt* one(JSContext* cx);
  static BigInt* negativeOne(JSContext* cx);

  static BigInt* copy(JSContext* cx, Handle<BigInt*> x,
                      js::gc::Heap heap = js::gc::Heap::Default);
  static BigInt* add(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
  static BigInt* sub(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
  static BigInt* mul(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
  static BigInt* div(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
  static BigInt* mod(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
  static BigInt* pow(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
  static BigInt* neg(JSContext* cx, Handle<BigInt*> x);
  static BigInt* inc(JSContext* cx, Handle<BigInt*> x);
  static BigInt* dec(JSContext* cx, Handle<BigInt*> x);
  static BigInt* lsh(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
  static BigInt* rsh(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
  static BigInt* bitAnd(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
  static BigInt* bitXor(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
  static BigInt* bitOr(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
  static BigInt* bitNot(JSContext* cx, Handle<BigInt*> x);

  static bool divmod(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y,
                     MutableHandle<BigInt*> quotient,
                     MutableHandle<BigInt*> remainder);

  static bool powIntPtr(intptr_t x, intptr_t y, intptr_t* result);

  static int64_t toInt64(const BigInt* x);
  static uint64_t toUint64(const BigInt* x);

  // Return true if the BigInt is without loss of precision representable as an
  // int32 and store the int32 value in the output. Otherwise return false and
  // leave the value of the output parameter unspecified.
  static bool isInt32(const BigInt* x, int32_t* result);

  // Return true if the BigInt is without loss of precision representable as an
  // int64 and store the int64 value in the output. Otherwise return false and
  // leave the value of the output parameter unspecified.
  static bool isInt64(const BigInt* x, int64_t* result);

  // Return true if the BigInt is without loss of precision representable as an
  // uint64 and store the uint64 value in the output. Otherwise return false and
  // leave the value of the output parameter unspecified.
  static bool isUint64(const BigInt* x, uint64_t* result);

  // Return true if the BigInt is without loss of precision representable as an
  // intptr_t and store the intptr_t value in the output. Otherwise return false
  // and leave the value of the output parameter unspecified.
  static bool isIntPtr(const BigInt* x, intptr_t* result);

  // Return true if the BigInt is without loss of precision representable as a
  // JS Number (double) and store the double value in the output. Otherwise
  // return false and leave the value of the output parameter unspecified.
  static bool isNumber(const BigInt* x, double* result);

  static BigInt* asIntN(JSContext* cx, Handle<BigInt*> x, uint64_t bits);
  static BigInt* asUintN(JSContext* cx, Handle<BigInt*> x, uint64_t bits);

  // Type-checking versions of arithmetic operations. These methods
  // must be called with at least one BigInt operand. Binary
  // operations will throw a TypeError if one of the operands is not a
  // BigInt value.
  static bool addValue(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
                       MutableHandle<Value> res);
  static bool subValue(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
                       MutableHandle<Value> res);
  static bool mulValue(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
                       MutableHandle<Value> res);
  static bool divValue(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
                       MutableHandle<Value> res);
  static bool modValue(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
                       MutableHandle<Value> res);
  static bool powValue(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
                       MutableHandle<Value> res);
  static bool negValue(JSContext* cx, Handle<Value> operand,
                       MutableHandle<Value> res);
  static bool incValue(JSContext* cx, Handle<Value> operand,
                       MutableHandle<Value> res);
  static bool decValue(JSContext* cx, Handle<Value> operand,
                       MutableHandle<Value> res);
  static bool lshValue(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
                       MutableHandle<Value> res);
  static bool rshValue(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
                       MutableHandle<Value> res);
  static bool bitAndValue(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
                          MutableHandle<Value> res);
  static bool bitXorValue(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
                          MutableHandle<Value> res);
  static bool bitOrValue(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
                         MutableHandle<Value> res);
  static bool bitNotValue(JSContext* cx, Handle<Value> operand,
                          MutableHandle<Value> res);

  static double numberValue(const BigInt* x);

  template <js::AllowGC allowGC>
  static JSLinearString* toString(JSContext* cx, Handle<BigInt*> x,
                                  uint8_t radix);
  template <typename CharT>
  static BigInt* parseLiteral(JSContext* cx, mozilla::Range<const CharT> chars,
                              bool* haveParseError,
                              js::gc::Heap heap = js::gc::Heap::Default);
  template <typename CharT>
  static BigInt* parseLiteralDigits(JSContext* cx,
                                    mozilla::Range<const CharT> chars,
                                    unsigned radix, bool isNegative,
                                    bool* haveParseError,
                                    js::gc::Heap heap = js::gc::Heap::Default);

  static int8_t compare(const BigInt* lhs, const BigInt* rhs);
  static bool equal(const BigInt* lhs, const BigInt* rhs);
  static bool equal(const BigInt* lhs, double rhs);
  static JS::Result<bool> equal(JSContext* cx, Handle<BigInt*> lhs,
                                HandleString rhs);

  static bool lessThan(const BigInt* x, const BigInt* y);
  // These methods return Nothing when the non-BigInt operand is NaN
  // or a string that can't be interpreted as a BigInt.
  static mozilla::Maybe<bool> lessThan(const BigInt* lhs, double rhs);
  static mozilla::Maybe<bool> lessThan(double lhs, const BigInt* rhs);
  static bool lessThan(JSContext* cx, Handle<BigInt*> lhs, HandleString rhs,
                       mozilla::Maybe<bool>& res);
  static bool lessThan(JSContext* cx, HandleString lhs, Handle<BigInt*> rhs,
                       mozilla::Maybe<bool>& res);
  static bool lessThan(JSContext* cx, HandleValue lhs, HandleValue rhs,
                       mozilla::Maybe<bool>& res);

#if defined(DEBUG) || defined(JS_JITSPEW)
  void dump() const;  // Debugger-friendly stderr dump.
  void dump(js::GenericPrinter& out) const;
  void dump(js::JSONPrinter& json) const;

  void dumpFields(js::JSONPrinter& json) const;
  void dumpStringContent(js::GenericPrinter& out) const;
  void dumpLiteral(js::GenericPrinter& out) const;
#endif

 public:
  static constexpr size_t DigitBits = sizeof(Digit) * CHAR_BIT;

 private:
  static constexpr size_t HalfDigitBits = DigitBits / 2;
  static constexpr Digit HalfDigitMask = (1ull << HalfDigitBits) - 1;

  static_assert(DigitBits == 32 || DigitBits == 64,
                "Unexpected BigInt Digit size");

  // Limit the size of bigint values to 1 million bits, to prevent excessive
  // memory usage.  This limit may be raised in the future if needed.  Note
  // however that there are many parts of the implementation that rely on being
  // able to count and index bits using a 32-bit signed ints, so until those
  // sites are fixed, the practical limit is 0x7fffffff bits.
  static constexpr size_t MaxBitLength = 1024 * 1024;
  static constexpr size_t MaxDigitLength = MaxBitLength / DigitBits;

  // BigInts can be serialized to strings of radix between 2 and 36.  For a
  // given bigint, radix 2 will take the most characters (one per bit).
  // Ensure that the max bigint size is small enough so that we can fit the
  // corresponding character count into a size_t, with space for a possible
  // sign prefix.
  static_assert(MaxBitLength <= std::numeric_limits<size_t>::max() - 1,
                "BigInt max length must be small enough to be serialized as a "
                "binary string");

  static size_t calculateMaximumCharactersRequired(HandleBigInt x,
                                                   unsigned radix);
  [[nodiscard]] static bool calculateMaximumDigitsRequired(JSContext* cx,
                                                           uint8_t radix,
                                                           size_t charCount,
                                                           size_t* result);

  static bool absoluteDivWithDigitDivisor(
      JSContext* cx, Handle<BigInt*> x, Digit divisor,
      const mozilla::Maybe<MutableHandle<BigInt*>>& quotient, Digit* remainder,
      bool quotientNegative);
  static void internalMultiplyAdd(const BigInt* source, Digit factor,
                                  Digit summand, unsigned, BigInt* result);
  static void multiplyAccumulate(const BigInt* multiplicand, Digit multiplier,
                                 BigInt* accumulator,
                                 unsigned accumulatorIndex);
  static bool absoluteDivWithBigIntDivisor(
      JSContext* cx, Handle<BigInt*> dividend, Handle<BigInt*> divisor,
      const mozilla::Maybe<MutableHandle<BigInt*>>& quotient,
      const mozilla::Maybe<MutableHandle<BigInt*>>& remainder,
      bool quotientNegative);

  enum class LeftShiftMode { SameSizeResult, AlwaysAddOneDigit };

  static BigInt* absoluteLeftShiftAlwaysCopy(JSContext* cx, Handle<BigInt*> x,
                                             unsigned shift, LeftShiftMode);
  static bool productGreaterThan(Digit factor1, Digit factor2, Digit high,
                                 Digit low);
  static BigInt* lshByAbsolute(JSContext* cx, HandleBigInt x, HandleBigInt y);
  static BigInt* rshByAbsolute(JSContext* cx, HandleBigInt x, HandleBigInt y);
  static BigInt* rshByMaximum(JSContext* cx, bool isNegative);
  static BigInt* truncateAndSubFromPowerOfTwo(JSContext* cx, HandleBigInt x,
                                              uint64_t bits,
                                              bool resultNegative);

  Digit absoluteInplaceAdd(const BigInt* summand, unsigned startIndex);
  Digit absoluteInplaceSub(const BigInt* subtrahend, unsigned startIndex);
  void inplaceRightShiftLowZeroBits(unsigned shift);
  void inplaceMultiplyAdd(Digit multiplier, Digit part);

  // The result of an SymmetricTrim bitwise op has as many digits as the
  // smaller operand.  A SymmetricFill bitwise op result has as many digits as
  // the larger operand, with high digits (if any) copied from the larger
  // operand.  AsymmetricFill is like SymmetricFill, except the result has as
  // many digits as the first operand; this kind is used for the and-not
  // operation.
  enum class BitwiseOpKind { SymmetricTrim, SymmetricFill, AsymmetricFill };

  template <BitwiseOpKind kind, typename BitwiseOp>
  static BigInt* absoluteBitwiseOp(JSContext* cx, Handle<BigInt*> x,
                                   Handle<BigInt*> y, BitwiseOp&& op);

  // Return `|x| & |y|`.
  static BigInt* absoluteAnd(JSContext* cx, Handle<BigInt*> x,
                             Handle<BigInt*> y);

  // Return `|x| | |y|`.
  static BigInt* absoluteOr(JSContext* cx, Handle<BigInt*> x,
                            Handle<BigInt*> y);

  // Return `|x| & ~|y|`.
  static BigInt* absoluteAndNot(JSContext* cx, Handle<BigInt*> x,
                                Handle<BigInt*> y);

  // Return `|x| ^ |y|`.
  static BigInt* absoluteXor(JSContext* cx, Handle<BigInt*> x,
                             Handle<BigInt*> y);

  // Return `(|x| + 1) * (resultNegative ? -1 : +1)`.
  static BigInt* absoluteAddOne(JSContext* cx, Handle<BigInt*> x,
                                bool resultNegative);

  // Return `(|x| - 1) * (resultNegative ? -1 : +1)`, with the precondition that
  // |x| != 0.
  static BigInt* absoluteSubOne(JSContext* cx, Handle<BigInt*> x,
                                bool resultNegative = false);

  // Return `a + b`, incrementing `*carry` if the addition overflows.
  static inline Digit digitAdd(Digit a, Digit b, Digit* carry) {
    Digit result = a + b;
    *carry += static_cast<Digit>(result < a);
    return result;
  }

  // Return `left - right`, incrementing `*borrow` if the addition overflows.
  static inline Digit digitSub(Digit left, Digit right, Digit* borrow) {
    Digit result = left - right;
    *borrow += static_cast<Digit>(result > left);
    return result;
  }

  // Compute `a * b`, returning the low half of the result and putting the
  // high half in `*high`.
  static Digit digitMul(Digit a, Digit b, Digit* high);

  // Divide `(high << DigitBits) + low` by `divisor`, returning the quotient
  // and storing the remainder in `*remainder`, with the precondition that
  // `high < divisor` so that the result fits in a Digit.
  static Digit digitDiv(Digit high, Digit low, Digit divisor, Digit* remainder);

  // Return `(|x| + |y|) * (resultNegative ? -1 : +1)`.
  static BigInt* absoluteAdd(JSContext* cx, Handle<BigInt*> x,
                             Handle<BigInt*> y, bool resultNegative);

  // Return `(|x| - |y|) * (resultNegative ? -1 : +1)`, with the precondition
  // that |x| >= |y|.
  static BigInt* absoluteSub(JSContext* cx, Handle<BigInt*> x,
                             Handle<BigInt*> y, bool resultNegative);

 public:
  // If `|x| < |y|` return -1; if `|x| == |y|` return 0; otherwise return 1.
  static int8_t absoluteCompare(const BigInt* lhs, const BigInt* rhs);

 private:
  static int8_t compare(const BigInt* lhs, double rhs);

  template <js::AllowGC allowGC>
  static JSLinearString* toStringBasePowerOfTwo(JSContext* cx, Handle<BigInt*>,
                                                unsigned radix);
  template <js::AllowGC allowGC>
  static JSLinearString* toStringSingleDigit(JSContext* cx, Digit digit,
                                             bool isNegative, unsigned radix);
  static JSLinearString* toStringGeneric(JSContext* cx, Handle<BigInt*>,
                                         unsigned radix);

  friend struct ::JSStructuredCloneReader;  // So it can call the following:
  static BigInt* destructivelyTrimHighZeroDigits(JSContext* cx, BigInt* x);

  bool absFitsInUint64() const { return digitLength() <= 64 / DigitBits; }

  uint64_t uint64FromAbsNonZero() const {
    MOZ_ASSERT(!isZero());

    uint64_t val = digit(0);
    if (DigitBits == 32 && digitLength() > 1) {
      val |= static_cast<uint64_t>(digit(1)) << 32;
    }
    return val;
  }

  friend struct ::JSStructuredCloneReader;
  friend struct ::JSStructuredCloneWriter;

  BigInt(const BigInt& other) = delete;
  void operator=(const BigInt& other) = delete;

 public:
  static constexpr size_t offsetOfFlags() { return offsetOfHeaderFlags(); }
  static constexpr size_t offsetOfLength() { return offsetOfHeaderLength(); }

  static constexpr size_t signBitMask() { return SignBit; }

 private:
  // To help avoid writing Spectre-unsafe code, we only allow MacroAssembler to
  // call the methods below.
  friend class js::jit::MacroAssembler;

  static size_t offsetOfInlineDigits() {
    return offsetof(BigInt, inlineDigits_);
  }

  static size_t offsetOfHeapDigits() { return offsetof(BigInt, heapDigits_); }

  static constexpr size_t inlineDigitsLength() { return InlineDigitsLength; }

 private:
  friend class js::gc::TenuringTracer;
};

static_assert(
    sizeof(BigInt) >= js::gc::MinCellSize,
    "sizeof(BigInt) must be greater than the minimum allocation size");

static_assert(
    sizeof(BigInt) == js::gc::MinCellSize,
    "sizeof(BigInt) intended to be the same as the minimum allocation size");

}  // namespace JS

namespace js {

template <AllowGC allowGC>
extern JSAtom* BigIntToAtom(JSContext* cx, JS::HandleBigInt bi);

extern JS::BigInt* NumberToBigInt(JSContext* cx, double d);

// Parse a BigInt from a string, using the method specified for StringToBigInt.
// Used by the BigInt constructor among other places.
extern JS::Result<JS::BigInt*> StringToBigInt(JSContext* cx,
                                              JS::Handle<JSString*> str);

// Parse a BigInt from an already-validated numeric literal.  Used by the
// parser.  Can only fail in out-of-memory situations.
extern JS::BigInt* ParseBigIntLiteral(
    JSContext* cx, const mozilla::Range<const char16_t>& chars);

// Parse a BigInt from an already-validated numeric literal. Returns Some if the
// BigInt literal fits into int64. Otherwise returns Nothing.
extern mozilla::Maybe<int64_t> ParseBigInt64Literal(
    mozilla::Range<const char16_t> chars);

extern JS::BigInt* ToBigInt(JSContext* cx, JS::Handle<JS::Value> v);
extern JS::Result<int64_t> ToBigInt64(JSContext* cx, JS::Handle<JS::Value> v);
extern JS::Result<uint64_t> ToBigUint64(JSContext* cx, JS::Handle<JS::Value> v);

}  // namespace js

#endif
