1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef builtin_intl_DecimalNumber_h
8 #define builtin_intl_DecimalNumber_h
9 
10 #include "mozilla/Attributes.h"
11 #include "mozilla/Maybe.h"
12 #include "mozilla/Span.h"
13 #include "mozilla/Variant.h"
14 
15 #include <stddef.h>
16 #include <stdint.h>
17 
18 #include "jstypes.h"
19 
20 #include "js/TypeDecls.h"
21 
22 class JSLinearString;
23 
24 namespace JS {
25 class JS_PUBLIC_API AutoCheckCannotGC;
26 }
27 
28 namespace js::intl {
29 
30 /**
31  * Representation of a decimal number in normalized form.
32  *
33  * Examples of normalized forms:
34  * - "123" is normalized to "0.123e3".
35  * - "0.01e-4" is normalized to "0.1e-5".
36  * - "12.3" is normalized to "0.123e2".
37  *
38  * Note: Internally we leave the decimal point where it lies to avoid copying
39  * the string, but otherwise ignore it once we calculate the normalized
40  * exponent.
41  */
42 class MOZ_STACK_CLASS DecimalNumber final {
43   using Latin1String = mozilla::Span<const JS::Latin1Char>;
44   using TwoByteString = mozilla::Span<const char16_t>;
45 
46   mozilla::Variant<Latin1String, TwoByteString> string_;
47 
charAt(size_t i)48   char charAt(size_t i) const {
49     if (string_.is<Latin1String>()) {
50       return static_cast<char>(string_.as<Latin1String>()[i]);
51     }
52     return static_cast<char>(string_.as<TwoByteString>()[i]);
53   }
54 
55   // Decimal exponent. Valid range is (INT32_MIN, INT_MAX32].
56   int32_t exponent_ = 0;
57 
58   // Start and end position of the significand.
59   size_t significandStart_ = 0;
60   size_t significandEnd_ = 0;
61 
62   // Flag if the number is zero.
63   bool zero_ = false;
64 
65   // Flag for negative numbers.
66   bool negative_ = false;
67 
68   // Error flag when the exponent is too large.
69   bool exponentTooLarge_ = false;
70 
71   template <typename CharT>
DecimalNumber(mozilla::Span<const CharT> string)72   explicit DecimalNumber(mozilla::Span<const CharT> string) : string_(string) {}
73 
74  public:
75   /** Return true if this decimal is zero. */
isZero()76   bool isZero() const { return zero_; }
77 
78   /** Return true if this decimal is negative. */
isNegative()79   bool isNegative() const { return negative_; }
80 
81   /** Return true if the exponent is too large. */
exponentTooLarge()82   bool exponentTooLarge() const { return exponentTooLarge_; }
83 
84   /** Return the exponent of this decimal. */
exponent()85   int32_t exponent() const { return exponent_; }
86 
87   // Exposed for testing.
significandStart()88   size_t significandStart() const { return significandStart_; }
significandEnd()89   size_t significandEnd() const { return significandEnd_; }
90 
91   /**
92    * Compare this decimal to another decimal. Returns a negative value if this
93    * decimal is smaller; zero if this decimal is equal; or a positive value if
94    * this decimal is larger than the input.
95    */
96   int32_t compareTo(const DecimalNumber& other) const;
97 
98   /**
99    * Create a decimal number from the input. Returns |mozilla::Nothing| if the
100    * input can't be parsed.
101    */
102   template <typename CharT>
103   static mozilla::Maybe<DecimalNumber> from(mozilla::Span<const CharT> chars);
104 
105   /**
106    * Create a decimal number from the input. Returns |mozilla::Nothing| if the
107    * input can't be parsed.
108    */
109   static mozilla::Maybe<DecimalNumber> from(JSLinearString* str,
110                                             JS::AutoCheckCannotGC& nogc);
111 };
112 }  // namespace js::intl
113 
114 #endif /* builtin_intl_DecimalNumber_h */
115