// bdljsn_jsonnumber.h -*-C++-*- #ifndef INCLUDED_BDLJSN_JSONNUMBER #define INCLUDED_BDLJSN_JSONNUMBER #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a value-semantic type representing a JSON number. // //@CLASSES: // bdljsn::JsonNumber: value-semantic type representing a JSON number // //@DESCRIPTION: This component provides a single value-semantic class, // 'bdljsn::JsonNumber', that represents a JSON number. The value of a // 'bdljsn::JsonNumber' object is set at construction using a string // representation of the JSON number (see {JSON Textual Specification}) or from // one of several C++ arithmetic types (see {Supported Conversions}). // // Arithmetic operations are *not* defined for for 'bdljsn::JsonNumber' // objects. For such operations, the value of a 'bdljsn::JsonNumber' object // can be converted to any of those supported types, though the conversion may // not be exact. // // The 'bdlsn::JsonNumber' equality operation returns 'true' if the string // representation of the number (returned by the 'value' accessor method) is // the same, even where the two strings represent the same number (e.g., "10" // and "1e1"). This definition of equality reflects the fact that the JSON // textual representation for the two 'JsonNumber' objects will be different. // The function 'isEqual' is provided for (a more expensive) numeric equality // comparison. // ///JSON Textual Specification ///-------------------------- // JSON numbers are defined by strings that match the grammar given at // https://www.rfc-editor.org/rfc/rfc8259#section-6. The equivalent regular // expression is: //.. // /^-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][-+]?[0-9]+)?\z/ //.. // Note that "\z" matches end-of-string but not a preceding '\n'. // // For example: //.. // 1 // 2.1 // -3 // 4e1 // 5.1e+2 // 6.12e-3 // 7e+04 // -8.1e+005 //.. // Notice that: // //: o Leading zeros are not allowed for the mantissa but are allowed for the //: exponent. //: //: o A decimal point must be followed by at least one digit. //: //: o The grammar does *not* specify any limit on the number of digits in the //: mantissa or the exponent. // //: o One can validly represent JSON numbers that are too large or too small //: for conversion to any of the supported arithmetic types. //: //: o The special values, 'INF' (infinity) and 'NaN' (Not A Number) are //: disallowed. // // The value of 'bdljsn::JsonNumber' object is determined by the input string // and that is *not* canonical. Thus, unequal strings lead to unequal // 'bdljsn::JsonNumber' objects even if their numeric values (e.g., 'asInt') // are equal: //.. // assert(bdljsn::JsonNumber("1") != bdljsn::JsonNumber("1.0"); // assert(bdljsn::JsonNumber("1").asInt() == bdljsn::JsonNumber("1.0") // .asInt(); //.. // ///Supported Conversions ///--------------------- // The value of a 'bdljsn::JsonNumber' object can be converted to an assortment // of useful types: // //: o 'int' //: o 'unsigned int' //: o 'bsls::Types::Int64' //: o 'bsls::Types::Uint64' //: o 'float' //: o 'double' //: o 'bdldfp::Decimal64' // // In addition to named conversion functions (like 'asInt' and 'asDouble') This // component provides explicit conversion operations for floating point types // ('float', 'double', 'Decimal64') on platforms where explicit conversions are // supported. // ///Handling Inexact Conversions: Floating Point Vs Integral Types /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Converting a 'bdlsjn::JsonNumber' to another representation may result in a // value that is not the same as the original 'bdljsn::JsonNumber'. Either the // 'bdljsn::JsonNumber' may represent a numeric value outside of the // representable range of the requested type (i.e., it is too large, or too // small), or the value may not be representable exactly. 'bdljsn::JsonNumber' // conversions will return the closest approximation of the // 'bdljsn::JsonNumber', even when a non-zero status is returned indicating an // inexact conversion. // // All the provided conversions to integral types have signatures that require // a return status, whereas conversion functions are provided for floating // point types that do not return a status. This is because: // //: 1 Floating point representations have specific values to indicate a //: 'bdljsn::JsonNumber' is outside of the representable range //: '(-INF, +INF)'. //: //: 2 Truncating the fractional part of a number to coerce a value to an //: integer is typically an error (the data being processed did not meet the //: programmer's expectation), whereas returning the closest floating point //: approximation to a 'bdljsn::JsonNumber' is very often not an error. // ///Exact 'Decima64' Representations /// - - - - - - - - - - - - - - - - // For users requiring precise conversions to 'bdldfp::Decimal64', the function // 'asDecimal64Exact' returns additional status indicating whether the // conversion is exact. An exact conversion for a 'Decimal64' is one that // preserves all the significant digits resulting in a decimal representation // having the same numerical value as the original JSON text. Note that // 'asDecimal64Exact' has very similar performance to 'asDecimal64' (i.e., // there is not a notable performance penalty to determining this property). // ///Known Issues With 'asDecima64Exact' ///- - - - - - - - - - - - - - - - - - // Currently 'asDecimal64Exact' will return 'bdljsn::JsonNumber::k_NOT_EXACT' // if the input is 0 with an exponent outside of the range ('[-398, 369]'). // For example, '0e-400'. This reflects the behavior of the underlying 3rd // party implementation. Please contact BDE if this is a concern. // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Creating JSON Number Object from User Input /// - - - - - - - - - - - - - - - - - - - - - - - - - - - // The specification of values for JSON numbers often starts with user input // textual representations of those values. As the specifications for valid // representation are complicated and not always intuitive it is prudent to // validate that input using the 'bdljsn::JsonNumber::isValidNumber' function; // otherwise, one might try to create a 'bdljsn::JsonNumber' object from an // invalid specification and that leads to undefined behavior. // // First, as a expedient for this example, we organize in an array input that // might well be entered by some user: //.. // struct { // const char *d_text_p; // const char *d_description_p; // bool d_expected; // } USER_INPUT[] = { // // // VALUE DESCRIPTION EXP // // ---------------------- -------------------------------------- --- // // // Invalid Input (that is valid in other contexts). // // { "1.", "Not uncommon way to write '1'." , 0 } // , { "1,000", "No commas allowed" , 0 } // , { "01", "Leading '0', disallowed by JSON." , 0 } // , { "", "0 per 'atoi', disallowed by JSON." , 0 } // , { "Hello, world!", "0 per 'atoi', disallowed by JSON." , 0 } // , { "NaN", "invalid number" , 0 } // , { "INF", "invalid number" , 0 } // , { "-INF", "invalid number" , 0 } // , { "+INF", "invalid number" , 0 } // // // Valid input (some surprising) // // , { "1234567890", "Integral value" , 1 } // , { "1234567890.123456", "Non-integral value" , 1 } // , { "1234567890.1234567", "Beyond Decimal64 precision" , 1 } // , { "-9223372036854775809", "INT64_MIN, underflow, but valid JSON", 1 } // , { "1.5e27", "INT64_MAX, overflow, but valid JSON", 1 } // , { "999999999999999999999999999999999999999999999999999999999999" // "e" // "999999999999999999999999999999999999999999999999999999999999", // "astronomic value" , 1 } // }; // // const bsl::size_t NUM_USER_INPUT = sizeof USER_INPUT / sizeof *USER_INPUT; //.. // Now, if and only if the input is valid, we use the input to construct a // 'bdljsn::JsonNumber' object and add that object to a vector for later // processing. //.. // bsl::vector<bdljsn::JsonNumber> userInput; // when valid input // // for (bsl::size_t ti = 0; ti < NUM_USER_INPUT; ++ti) { // const char *TEXT = USER_INPUT[ti].d_text_p; // const char *DESC = USER_INPUT[ti].d_description_p; (void) DESC; // const bool EXP = USER_INPUT[ti].d_expected; // // const bool isValid = bdljsn::JsonNumber::isValidNumber(TEXT); // assert(EXP == isValid); // // if (isValid) { // userInput.push_back(bdljsn::JsonNumber(TEXT)); // } // } //.. // Finally, we confirm that the vector has the expected number of elements: //.. // assert(6 == userInput.size()); //.. // ///Example 2: Using 'bdljsn::JsonNumber' Objects ///- - - - - - - - - - - - - - - - - - - - - - - // We saw in {Example 1} that 'bdljsn::JsonNumber' objects can validly hold // values numeric values that cannot be converted to any of the supported types // (e.g., the "astronomic value") for arithmetic operations. Applications that // accept arbitrary 'bdljsn::JsonNumber' objects should be prepared to // categorize the contained value and adapt their handling accordingly. In // practice, applications may have some assurances of the contents of received // 'bdljsn::JsonNumber' objects. Here, we intentionally avoid such assumptions // to explore the wide range of variations that can arise. // // Legend, in the output below: // //: o "OK": //: o Means "OKay to use". In some cases, the numeric value of the //: arithmetic type is an approximation of JSON number and the application //: may have to allow for that difference. //: //: o "NG": //: o Means "No Good" (do not use). The JSON number is outside of the valid //: range of the arithmetic type. // // First, we set up a framework (in this case, a 'for' loop) for examining our // input, the same 'userInput' vector created in {Example 1}: //.. // for (bsl::size_t i = 0; i < userInput.size(); ++i) { // const bdljsn::JsonNumber obj = userInput[i]; //.. // Then, we categorize the value as integral or not: //.. // if (obj.isIntegral()) { // bsl::cout << "Integral: "; //.. // If integral, we check if the value is a usable range. Let us assume that // 'bslsl::Type::Int64' is as large a number as we can accept. // // Then, we convert the JSON number to that type and check for overflow and // underflow: //.. // bsls::Types::Int64 value; // int rc = obj.asInt64(&value); // switch (rc) { // case 0: { // bsl::cout << value << " : OK to USE" << bsl::endl; // } break; // case bdljsn::JsonNumber::k_OVERFLOW: { // bsl::cout << obj.value() << ": NG too large" << bsl::endl; // } break; // case bdljsn::JsonNumber::k_UNDERFLOW: { // bsl::cout << obj.value() << ": NG too small" << bsl::endl; // } break; // case bdljsn::JsonNumber::k_NOT_INTEGRAL: { // assert(!"reached"); // } break; // } //.. // Next, if the value is not integral, we try to handle it as a floating point // value -- a 'bdldfp::Decimal64' in this example -- and further categorize it // as exact/inexact, too large/small. //.. // } else { // bsl::cout << "Not-Integral: "; // // bdldfp::Decimal64 value; // int rc = obj.asDecimal64Exact(&value); // switch (rc) { // case 0: { // bsl::cout << value << " : exact: OK to USE"; // } break; // case bdljsn::JsonNumber::k_INEXACT: { // bsl::cout << value << ": inexact: USE approximation"; // } break; // case bdljsn::JsonNumber::k_NOT_INTEGRAL: { // assert(!"reached"); // } break; // } // // const bdldfp::Decimal64 INF = // bsl::numeric_limits<bdldfp::Decimal64>::infinity(); // // if ( INF == value) { // bsl::cout << ": NG too large" << bsl::endl; // } else if (-INF == value) { // bsl::cout << ": NG too small" << bsl::endl; // } else { // bsl::cout << bsl::endl; // } // } // } //.. // Finally, we observe for particular input: //.. // Integral: 1234567890 : OK to USE // Not-Integral: 1234567890.123456 : exact: OK to USE // Not-Integral: 1234567890.123457: inexact: USE approximation // Integral: -9223372036854775809: NG too small // Integral: 1.5e27: NG too large // Integral: 999999999999999999999999999999999999999999999999999999999999e9999 // 99999999999999999999999999999999999999999999999999999999: NG too large //.. #include <bdlscm_version.h> #include <bdljsn_numberutil.h> #include <bdlb_float.h> #include <bdldfp_decimal.h> #include <bdldfp_decimalutil.h> #include <bslalg_swaputil.h> #include <bslmf_assert.h> #include <bslmf_isbitwisemoveable.h> #include <bslmf_isintegral.h> #include <bslmf_nestedtraitdeclaration.h> #include <bsls_assert.h> #include <bsls_keyword.h> // 'BSLS_KEYWORD_NOEXCEPT' #include <bsls_types.h> // 'bsls::Types::Int64', 'bsls::Types::Uint64' #include <bsl_iosfwd.h> #include <bsl_string.h> namespace BloombergLP { namespace bdljsn { // ================ // class JsonNumber // ================ class JsonNumber { // This class defines a value-semantic class that represents a JSON number. // Objects of this class have a value determined at construction and does // not change except by assignment from or swap with another 'JsonNumber' // object. The value can be specified by supplying a string that conforms // to the {JSON Textual Specification} or from one of the {Supported // Types}. The value of a JSON object can be converted to any of those // types; however, some of those conversions can be inexact. // PRIVATE TYPES typedef NumberUtil Nu; // DATA bsl::string d_value; // FRIENDS friend void swap(JsonNumber& , JsonNumber& ); public: // CONSTANTS enum { // special integer conversion status values k_OVERFLOW = Nu::k_OVERFLOW, // above the representable range k_UNDERFLOW = Nu::k_UNDERFLOW, // below the representable range k_NOT_INTEGRAL = Nu::k_NOT_INTEGRAL, // the number is not an integer // special exact Decimal64 conversion status values k_INEXACT = Nu::k_INEXACT }; // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(JsonNumber, bslma::UsesBslmaAllocator); BSLMF_NESTED_TRAIT_DECLARATION(JsonNumber, bslmf::IsBitwiseMoveable); // CLASS METHODS static bool isValidNumber(const bsl::string_view& text); // Return 'true' if the specified 'text' complies with the grammar of a // JSON number, and 'false' otherwise. See the {JSON Textual // Specification}. // CREATORS JsonNumber(); explicit JsonNumber(bslma::Allocator *basicAllocator); // Create a 'JsonNumber' having the value "0". Optionally specify a // 'basicAllocator' used to supply memory. If 'basicAllocator' is 0, // the currently installed default allocator is used. explicit JsonNumber(const char *text, bslma::Allocator *basicAllocator = 0); explicit JsonNumber(const bsl::string_view& text, bslma::Allocator *basicAllocator = 0); // Create a 'JsonNumber' having the value of the specified 'text'. // Optionally specify a 'basicAllocator' used to supply memory. If // 'basicAllocator' is 0, the currently installed default allocator is // used. The behavior is undefined unless 'isValidJsonNumber(text)' // is 'true'. See {JSON Textual Specification}. explicit JsonNumber(bslmf::MovableRef<bsl::string> text); // Create a 'JsonNumber' object having the same value and the same // allocator as the specified 'text'. The contents of the 'value' // string becomes unspecified but valid, and its allocator remains // unchanged. The behavior is undefined unless 'isValidNumber(text)' // is 'true'. See {JSON Textual Specification}. explicit JsonNumber(bslmf::MovableRef<bsl::string> text, bslma::Allocator *basicAllocator); // Create a 'JsonNumber' object having the same value as the specified // 'text', using the specified 'basicAllocator' to supply memory. The // allocator of the 'text' string remains unchanged. If the 'text' and // the newly created object have the same allocator then the contents // of 'text' string becomes unspecified but valid, and no exceptions // will be thrown; otherwise the 'text' string is unchanged and an // exception may be thrown. The behavior is undefined unless // 'isValidNumber(text)' is 'true'. See {JSON Textual Specification}. explicit JsonNumber(int value, bslma::Allocator *basicAllocator = 0); explicit JsonNumber(unsigned int value, bslma::Allocator *basicAllocator = 0); explicit JsonNumber(bsls::Types::Int64 value, bslma::Allocator *basicAllocator = 0); explicit JsonNumber(bsls::Types::Uint64 value, bslma::Allocator *basicAllocator = 0); // Create a 'JsonNumber' having the specified 'value'. Optionally // specify a 'basicAllocator' used to supply memory. If // 'basicAllocator' is 0, the currently installed default allocator is // used. explicit JsonNumber(float value, bslma::Allocator *basicAllocator = 0); explicit JsonNumber(double value, bslma::Allocator *basicAllocator = 0); explicit JsonNumber(bdldfp::Decimal64 value, bslma::Allocator *basicAllocator = 0); // Create a 'JsonNumber' having the specified 'value'. Optionally // specify a 'basicAllocator' used to supply memory. If // 'basicAllocator' is 0, the currently installed default allocator is // used. The behavior is undefined if the 'value' is infinite ('INF') // or not-a-number ('NaN'). JsonNumber(const JsonNumber& original, bslma::Allocator *basicAllocator = 0); // Create a 'JsonNumber' object having the same value as the specified // 'original' object. Optionally specify a 'basicAllocator' used to // supply memory. If 'basicAllocator' is 0, the currently installed // default allocator is used. JsonNumber(bslmf::MovableRef<JsonNumber> original) BSLS_KEYWORD_NOEXCEPT; // Create a 'JsonNumber' object having the same value and the same // allocator as the specified 'original' object. The value of // 'original' becomes unspecified but valid, and its allocator remains // unchanged. JsonNumber(bslmf::MovableRef<JsonNumber> original, bslma::Allocator *basicAllocator); // Create a 'JsonNumber' object having the same value as the specified // 'original' object, using the specified 'basicAllocator' to supply // memory. The allocator of 'original' remains unchanged. If // 'original' and the newly created object have the same allocator then // the value of 'original' becomes unspecified but valid, and no // exceptions will be thrown; otherwise 'original' is unchanged (and an // exception may be thrown). //! ~JsonNumber() = default; // Destroy this object. // MANIPULATORS JsonNumber& operator=(const JsonNumber& rhs); // Assign to this object the value of the specified 'rhs' object, and // return a non-'const' reference to this object. JsonNumber& operator=(bslmf::MovableRef<JsonNumber> rhs); // Assign to this object the value of the specified 'rhs' object, and // return a non-'const' reference to this object. The allocators of // this object and 'rhs' both remain unchanged. If 'rhs' and this // object have the same allocator then the value of 'rhs' becomes // unspecified but valid, and no exceptions will be thrown; otherwise // 'rhs' is unchanged (and an exception may be thrown). JsonNumber& operator=(int rhs); JsonNumber& operator=(unsigned int rhs); JsonNumber& operator=(bsls::Types::Int64 rhs); JsonNumber& operator=(bsls::Types::Uint64 rhs); // Assign to this object the value of the specified 'rhs', and return a // non-'const' reference to this object. JsonNumber& operator=(float rhs); JsonNumber& operator=(double rhs); JsonNumber& operator=(bdldfp::Decimal64 rhs); // Assign to this object the value of the specified 'rhs', and return a // non-'const' reference to this object. The behavior is undefined if // 'rhs' is infinite ('INF') or not-a-number ('NaN'). void swap(JsonNumber& other); // Efficiently exchange the value of this object with the value of the // specified 'other' object. This method provides the no-throw // exception-safety guarantee. The behavior is undefined unless this // object was created with the same allocator as 'other'. // ACCESSORS bool isEqual(const JsonNumber& other) const; // Return 'true' if this number and the specified 'other' number have // the same numeric value, and 'false' otherwise. Note that this is in // contrast to 'operator==', which returns whether the text returned by // 'value' is the same. So, for example, // 'JsonNumber("10") == JsonNumber("1e1")' is 'false', but // 'JsonNumber("10").isEqual(JsonNumber("1e1"))' is 'true'. bool isIntegral() const; // Return 'true' if the value of this 'JsonNumber' is an (exact) // integral value, or 'false' otherwise. Note that this function may // return 'true' even this number cannot be represented in a // fundamental integral type. const bsl::string& value() const; // Return the textual representation of this 'JsonNumber'. // BDE_VERIFY pragma: push // BDE_VERIFY pragma: -FABC01 // not in alphabetic order // Integer Accessors int asInt (int *result) const; int asInt64 (bsls::Types::Int64 *result) const; int asUint (unsigned int *result) const; int asUint64(bsls::Types::Uint64 *result) const; // Load into the specified 'result' the integer value of this number. // Return 0 on success, 'k_OVERFLOW' if 'value' is larger than can be // represented by 'result', 'k_UNDERFLOW' if 'value' is smaller than // can be represented by 'result', and 'k_NOT_INTEGRAL' if 'value' is // not an integral number (i.e., there is a fractional part). For // underflow, 'result' will be loaded with the minimum representable // value, for overflow, 'result' will be loaded with the maximum // representable value, for non-integral values 'result' will be loaded // with the integer part of 'value' (truncating the fractional part). // If the result is not an integer and also either overflows or // underflows, it is treated as an overflow or underflow // (respectively). Note that this operation returns an error status // value (unlike similar floating point conversions) because typically // it is an error if a conversion to an integer results in an in-exact // value. float asFloat() const; double asDouble() const; bdldfp::Decimal64 asDecimal64() const; // Return the closest floating point representation to this number. If // this number is outside the representable range, return '+INF' or // '-INF' (as appropriate). Note that values smaller than the smallest // representable non-zero value (a.k.a, 'MIN') are rounded to 'MIN' // (positive or negative, as appropriate) or 0, whichever is the better // approximation. // 'Exact' Accessors int asDecimal64Exact(bdldfp::Decimal64 *result) const; // Load to the specified 'result' the closest floating point // representation to this number, even if a non-zero status is // returned. Return 0 if this number can be represented exactly, and // return 'k_INEXACT' and load 'result' with the closest approximation // if 'value' cannot be represented exactly. If this number is outside // the representable range, load 'result' with '+INF' or '-INF' (as // appropriate). A number can be represented exactly as a 'Decimal64' // if, for the significand and exponent, // 'abs(significand) <= 9,999,999,999,999,999' and // '-398 <= exponent <= 369'. // BDE_VERIFY pragma: pop // 'explicit' (conversion) operators #if defined(BSLS_COMPILERFEATURES_SUPPORT_OPERATOR_EXPLICIT) explicit operator float() const; explicit operator double() const; explicit operator bdldfp::Decimal64() const; // Return the closest floating point representation to this number. If // this number is outside the representable range, return '+INF' or // '-INF' (as appropriate). Note that the values returned by these // operators match those returned by 'asFloat', 'asDouble', and // 'asDecimal64', respectively. #endif // Aspects bslma::Allocator *allocator() const; // Return the allocator used by this object to supply memory. Note // that if no allocator was supplied at construction the default // allocator in effect at construction is used. bsl::ostream& print(bsl::ostream& stream, int level = 0, int spacesPerLevel = 4) const; // Write the value of this object to the specified output 'stream' in a // human-readable format, and return a non-'const' reference to // 'stream'. Optionally specify an initial indentation 'level', whose // absolute value is incremented recursively for nested objects. If // 'level' is specified, optionally specify 'spacesPerLevel', whose // absolute value indicates the number of spaces per indentation level // for this and all of its nested objects. If 'level' is negative, // suppress indentation of the first line. If 'spacesPerLevel' is // negative, format the entire output on one line, suppressing all but // the initial indentation (as governed by 'level'). If 'stream' is // not valid on entry, this operation has no effect. Note that the // format is not fully specified, and can change without notice. }; // FREE OPERATORS bsl::ostream& operator<<(bsl::ostream& stream, const JsonNumber& object); // Write the value of the specified 'object' to the specified output // 'stream' in a single-line format, and return a non-'const' reference to // 'stream'. If 'stream' is not valid on entry, this operation has no // effect. Note that this human-readable format is not fully specified and // can change without notice. Also note that this method has the same // behavior as 'object.print(stream, 0, -1)'. bool operator==(const JsonNumber& lhs, const JsonNumber& rhs); // Return 'true' if the specified 'lhs' and 'rhs' objects have the same // value, and 'false' otherwise. Two 'JsonNumber' objects have the same // value if their 'value' attributes are the same. bool operator!=(const JsonNumber& lhs, const JsonNumber& rhs); // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the // same value, and 'false' otherwise. Two 'JsonNumber' objects do not have // the same value if their 'value' attributes are not the same. // FREE FUNCTIONS template <class HASHALG> void hashAppend(HASHALG& hashAlgorithm, const JsonNumber& object); // Pass the specified 'object' to the specified 'hashAlgorithm'. This // function integrates with the 'bslh' modular hashing system and // effectively provides a 'bsl::hash' specialization for 'JsonNumber'. void swap(JsonNumber& a, JsonNumber& b); // Exchange the values of the specified 'a' and 'b' objects. This function // provides the no-throw exception-safety guarantee if the two objects were // created with the same allocator and the basic guarantee otherwise. // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ----------------- // struct JsonNumber // ----------------- // CLASS METHODS inline bool JsonNumber::isValidNumber(const bsl::string_view& text) { return NumberUtil::isValidNumber(text); } // CREATORS inline JsonNumber::JsonNumber() : d_value(1, '0') { } inline JsonNumber::JsonNumber(bslma::Allocator *basicAllocator) : d_value(1, '0', basicAllocator) { } inline JsonNumber::JsonNumber(const char *text, bslma::Allocator *basicAllocator) : d_value(text, basicAllocator) { BSLS_ASSERT(NumberUtil::isValidNumber(text)); } inline JsonNumber::JsonNumber(const bsl::string_view& text, bslma::Allocator *basicAllocator) : d_value(text, basicAllocator) { BSLS_ASSERT(NumberUtil::isValidNumber(text)); } inline JsonNumber::JsonNumber(int value, bslma::Allocator *basicAllocator) : d_value(basicAllocator) { NumberUtil::stringify(&d_value, static_cast<bsls::Types::Int64>(value)); } inline JsonNumber::JsonNumber(unsigned int value, bslma::Allocator *basicAllocator) : d_value(basicAllocator) { NumberUtil::stringify(&d_value, static_cast<bsls::Types::Uint64>(value)); } inline JsonNumber::JsonNumber(bsls::Types::Int64 value, bslma::Allocator *basicAllocator) : d_value(basicAllocator) { NumberUtil::stringify(&d_value, value); } inline JsonNumber::JsonNumber(bsls::Types::Uint64 value, bslma::Allocator *basicAllocator) : d_value(basicAllocator) { NumberUtil::stringify(&d_value, value); } inline JsonNumber::JsonNumber(float value, bslma::Allocator *basicAllocator) : d_value(basicAllocator) { BSLS_ASSERT(!bdlb::Float::isNan (value)); BSLS_ASSERT(!bdlb::Float::isInfinite(value)); NumberUtil::stringify(&d_value, value); } inline JsonNumber::JsonNumber(double value, bslma::Allocator *basicAllocator) : d_value(basicAllocator) { BSLS_ASSERT(!bdlb::Float::isNan (value)); BSLS_ASSERT(!bdlb::Float::isInfinite(value)); NumberUtil::stringify(&d_value, value); } inline JsonNumber::JsonNumber(bdldfp::Decimal64 value, bslma::Allocator *basicAllocator) : d_value(basicAllocator) { BSLS_ASSERT(!bdldfp::DecimalUtil::isNan(value)); BSLS_ASSERT(!bdldfp::DecimalUtil::isInf(value)); NumberUtil::stringify(&d_value, value); } inline JsonNumber::JsonNumber(bslmf::MovableRef<bsl::string> text) : d_value(bslmf::MovableRefUtil::move(text)) { BSLS_ASSERT(NumberUtil::isValidNumber(d_value)); } inline JsonNumber::JsonNumber(bslmf::MovableRef<bsl::string> text, bslma::Allocator *basicAllocator) : d_value(bslmf::MovableRefUtil::move(text), basicAllocator) { BSLS_ASSERT(NumberUtil::isValidNumber(d_value)); } inline JsonNumber::JsonNumber(const JsonNumber& original, bslma::Allocator *basicAllocator) : d_value(original.d_value, basicAllocator) { } inline JsonNumber::JsonNumber(bslmf::MovableRef<JsonNumber> original) BSLS_KEYWORD_NOEXCEPT : d_value(bslmf::MovableRefUtil::move( bslmf::MovableRefUtil::access(original).d_value)) { } inline JsonNumber::JsonNumber(bslmf::MovableRef<JsonNumber> original, bslma::Allocator *basicAllocator) : d_value(bslmf::MovableRefUtil::move( bslmf::MovableRefUtil::access(original).d_value), basicAllocator) { } // MANIPULATORS inline JsonNumber& JsonNumber::operator=(const JsonNumber& rhs) { d_value = rhs.d_value; return *this; } inline JsonNumber& JsonNumber::operator=(bslmf::MovableRef<JsonNumber> rhs) { d_value = bslmf::MovableRefUtil::move( bslmf::MovableRefUtil::access(rhs).d_value); return *this; } inline JsonNumber& JsonNumber::operator=(int rhs) { NumberUtil::stringify(&d_value, static_cast<bsls::Types::Int64>(rhs)); return *this; } inline JsonNumber& JsonNumber::operator=(unsigned int rhs) { NumberUtil::stringify(&d_value, static_cast<bsls::Types::Uint64>(rhs)); return *this; } inline JsonNumber& JsonNumber::operator=(bsls::Types::Int64 rhs) { NumberUtil::stringify(&d_value, rhs); return *this; } inline JsonNumber& JsonNumber::operator=(bsls::Types::Uint64 rhs) { NumberUtil::stringify(&d_value, rhs); return *this; } inline JsonNumber& JsonNumber::operator=(float rhs) { BSLS_ASSERT(!bdlb::Float::isNan (rhs)); BSLS_ASSERT(!bdlb::Float::isInfinite(rhs)); NumberUtil::stringify(&d_value, rhs); return *this; } inline JsonNumber& JsonNumber::operator=(double rhs) { BSLS_ASSERT(!bdlb::Float::isNan (rhs)); BSLS_ASSERT(!bdlb::Float::isInfinite(rhs)); NumberUtil::stringify(&d_value, rhs); return *this; } inline JsonNumber& JsonNumber::operator=(bdldfp::Decimal64 rhs) { BSLS_ASSERT(!bdldfp::DecimalUtil::isNan(rhs)); BSLS_ASSERT(!bdldfp::DecimalUtil::isInf(rhs)); NumberUtil::stringify(&d_value, rhs); return *this; } inline void JsonNumber::swap(JsonNumber& other) { BSLS_ASSERT(d_value.allocator() == other.allocator()); bslalg::SwapUtil::swap(&d_value, &other.d_value); } // ACCESSORS inline bool JsonNumber::isEqual(const JsonNumber& other) const { return NumberUtil::areEqual(d_value, other.d_value); } inline bool JsonNumber::isIntegral() const { return NumberUtil::isIntegralNumber(d_value); } inline const bsl::string& JsonNumber::value() const { return d_value; } // Integer Accessors // BDE_VERIFY pragma: push // BDE_VERIFY pragma: -FABC01 // not in alphabetic order inline int JsonNumber::asInt(int *result) const { return NumberUtil::asInt(result, d_value); } inline int JsonNumber::asInt64(bsls::Types::Int64 *result) const { return NumberUtil::asInt64(result, d_value); } inline int JsonNumber::asUint(unsigned int *result) const { return NumberUtil::asUint(result, d_value); } inline int JsonNumber::asUint64(bsls::Types::Uint64 *result) const { return NumberUtil::asUint64(result, d_value); } inline float JsonNumber::asFloat() const { return static_cast<float>(asDouble()); } inline double JsonNumber::asDouble() const { return NumberUtil::asDouble(d_value); } inline bdldfp::Decimal64 JsonNumber::asDecimal64() const { return NumberUtil::asDecimal64(d_value); } // 'Exact' Accessors inline int JsonNumber::asDecimal64Exact(bdldfp::Decimal64 *result) const { return NumberUtil::asDecimal64Exact(result, d_value); } // BDE_VERIFY pragma: pop #if defined(BSLS_COMPILERFEATURES_SUPPORT_OPERATOR_EXPLICIT) // 'explicit' (conversion) operators inline JsonNumber::operator float() const { return asFloat(); } inline JsonNumber::operator double() const { return asDouble(); } inline JsonNumber::operator bdldfp::Decimal64() const { return asDecimal64(); } #endif // Aspects inline bslma::Allocator *JsonNumber::allocator() const { return d_value.get_allocator().mechanism(); } } // close package namespace // FREE OPERATORS inline bsl::ostream& bdljsn::operator<<(bsl::ostream& stream, const bdljsn::JsonNumber& object) { return object.print(stream, 0, -1); } inline bool bdljsn::operator==(const bdljsn::JsonNumber& lhs, const bdljsn::JsonNumber& rhs) { return lhs.value() == rhs.value(); } inline bool bdljsn::operator!=(const bdljsn::JsonNumber& lhs, const bdljsn::JsonNumber& rhs) { return lhs.value() != rhs.value(); } // FREE FUNCTIONS template <class HASHALG> inline void bdljsn::hashAppend(HASHALG& hashAlgorithm, const bdljsn::JsonNumber& object) { hashAppend(hashAlgorithm, object.value()); } inline void bdljsn::swap(bdljsn::JsonNumber& a, bdljsn::JsonNumber& b) { bslalg::SwapUtil::swap(&a.d_value, &b.d_value); } } // close enterprise namespace #endif // INCLUDED_BDLJSN_JSONNUMBER // ---------------------------------------------------------------------------- // Copyright 2022 Bloomberg Finance L.P. // // 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. // ----------------------------- END-OF-FILE ----------------------------------