xrpld
Loading...
Searching...
No Matches
STNumber.cpp
1#include <xrpl/protocol/STNumber.h>
2
3#include <xrpl/basics/Number.h>
4#include <xrpl/basics/contract.h>
5#include <xrpl/beast/utility/instrumentation.h>
6#include <xrpl/json/json_value.h>
7#include <xrpl/protocol/Asset.h>
8#include <xrpl/protocol/Rules.h> // IWYU pragma: keep
9#include <xrpl/protocol/SField.h>
10#include <xrpl/protocol/STAmount.h>
11#include <xrpl/protocol/STBase.h>
12#include <xrpl/protocol/STTakesAsset.h>
13#include <xrpl/protocol/Serializer.h>
14
15#include <boost/lexical_cast.hpp>
16#include <boost/regex/v5/regbase.hpp>
17#include <boost/regex/v5/regex.hpp>
18#include <boost/regex/v5/regex_match.hpp>
19
20#include <cstddef>
21#include <cstdint>
22#include <limits>
23#include <ostream>
24#include <stdexcept>
25#include <string>
26#include <utility>
27
28namespace xrpl {
29
31{
32}
33
35{
36 // We must call these methods in separate statements
37 // to guarantee their order of execution.
38 auto mantissa = sit.geti64();
39 auto exponent = sit.geti32();
40 value_ = Number{mantissa, exponent};
41}
42
45{
46 return STI_NUMBER;
47}
48
51{
52 return to_string(value_);
53}
54
55void
57{
59
60 XRPL_ASSERT_PARTS(
61 getFName().shouldMeta(SField::kSmdNeedsAsset),
62 "STNumber::associateAsset",
63 "field needs asset");
64
66}
67
68void
70{
71 XRPL_ASSERT(getFName().isBinary(), "xrpl::STNumber::add : field is binary");
72 XRPL_ASSERT(getFName().fieldType == getSType(), "xrpl::STNumber::add : field type match");
73
74 auto value = value_;
75 auto const mantissa = value.mantissa();
76 auto const exponent = value.exponent();
77
78 SField const& field = getFName();
80 {
81 // asset is defined in the STTakesAsset base class
82 if (asset_)
83 {
84 // The number should be rounded to the asset's precision, but round
85 // it here if it has an asset assigned.
87 XRPL_ASSERT_PARTS(value_ == value, "xrpl::STNumber::add", "value is already rounded");
88 }
89 else
90 {
91 // There are circumstances where an already-rounded Number is
92 // serialized without being touched by a transactor, and thus
93 // without an asset. We can't know if it's rounded, because it could
94 // represent _anything_, particularly when serializing user-provided
95 // Json. Regardless, the only time we should be serializing an
96 // STNumber is when the scale is large.
97 XRPL_ASSERT_PARTS(
99 "xrpl::STNumber::add",
100 "STNumber only used with large mantissa scale");
101 }
102 }
103
104 XRPL_ASSERT_PARTS(
107 "xrpl::STNumber::add",
108 "mantissa in valid range");
109 s.add64(mantissa);
110 s.add32(exponent);
111}
112
113Number const&
115{
116 return value_;
117}
118
119void
121{
122 value_ = v;
123}
124
125STBase*
126STNumber::copy(std::size_t n, void* buf) const
127{
128 return emplace(n, buf, *this);
129}
130
131STBase*
133{
134 return emplace(n, buf, std::move(*this));
135}
136
137bool
139{
140 XRPL_ASSERT(
141 t.getSType() == this->getSType(), "xrpl::STNumber::isEquivalent : field type match");
142 auto const& v = dynamic_cast<STNumber const&>(t);
143 return value_ == v;
144}
145
146bool
148{
149 return value_ == Number();
150}
151
153operator<<(std::ostream& out, STNumber const& rhs)
154{
155 return out << rhs.getText();
156}
157
158NumberParts
160{
161 static boost::regex const kReNumber(
162 "^" // the beginning of the string
163 "([-+]?)" // (optional) + or - character
164 "(0|[1-9][0-9]*)" // a number (no leading zeroes, unless 0)
165 "(\\.([0-9]+))?" // (optional) period followed by any number
166 "([eE]([+-]?)([0-9]+))?" // (optional) E, optional + or -, any number
167 "$",
168 boost::regex_constants::optimize);
169
170 boost::smatch match;
171
172 if (!boost::regex_match(number, match, kReNumber))
173 Throw<std::runtime_error>("'" + number + "' is not a number");
174
175 // Match fields:
176 // 0 = whole input
177 // 1 = sign
178 // 2 = integer portion
179 // 3 = whole fraction (with '.')
180 // 4 = fraction (without '.')
181 // 5 = whole exponent (with 'e')
182 // 6 = exponent sign
183 // 7 = exponent number
184
185 bool const negative = (match[1].matched && (match[1] == "-"));
186
187 std::uint64_t mantissa = 0;
188 int exponent = 0;
189
190 if (!match[4].matched) // integer only
191 {
192 mantissa = boost::lexical_cast<std::uint64_t>(std::string(match[2]));
193 exponent = 0;
194 }
195 else
196 {
197 // integer and fraction
198 mantissa = boost::lexical_cast<std::uint64_t>(match[2] + match[4]);
199 exponent = -(match[4].length());
200 }
201
202 if (match[5].matched)
203 {
204 // we have an exponent
205 if (match[6].matched && (match[6] == "-"))
206 {
207 exponent -= boost::lexical_cast<int>(std::string(match[7]));
208 }
209 else
210 {
211 exponent += boost::lexical_cast<int>(std::string(match[7]));
212 }
213 }
214
215 return {.mantissa = mantissa, .exponent = exponent, .negative = negative};
216}
217
218STNumber
219numberFromJson(SField const& field, json::Value const& value)
220{
221 NumberParts parts;
222
223 if (value.isInt())
224 {
225 if (value.asInt() >= 0)
226 {
227 parts.mantissa = value.asInt();
228 }
229 else
230 {
231 parts.mantissa = value.asAbsUInt();
232 parts.negative = true;
233 }
234 }
235 else if (value.isUInt())
236 {
237 parts.mantissa = value.asUInt();
238 }
239 else if (value.isString())
240 {
241 parts = partsFromString(value.asString());
242
243 XRPL_ASSERT_PARTS(
244 !getCurrentTransactionRules(), "xrpld::numberFromJson", "Not in a Transactor context");
245
246 // Number mantissas are much bigger than the allowable parsed values, so
247 // it can't be out of range.
248 static_assert(
249 // NOLINTNEXTLINE(misc-redundant-expression)
251 std::numeric_limits<decltype(parts.mantissa)>::max());
252 }
253 else
254 {
255 Throw<std::runtime_error>("not a number");
256 }
257
258 Number const num{parts.negative, parts.mantissa, parts.exponent, Number::Normalized{}};
259
260 // Canonicalize "parts" and "num" with each other by getting rid of trailing 0s until either the
261 // exponents match, or there are no more 0s. If the two results don't match exactly, then the
262 // value has been rounded one way or another, and should not be used, because it may lead to an
263 // unexpected result. canonicalizeParts is not to be confused with Number::canonicalize, because
264 // they have completely different goals.
265 auto canonicalizeParts = [](NumberParts p, int otherExponent) {
266 if (p.mantissa == 0)
267 return NumberParts{};
268
269 while (p.exponent < otherExponent && p.mantissa % 10 == 0)
270 {
271 p.mantissa /= 10;
272 ++p.exponent;
273 }
274
275 return p;
276 };
277
278 auto const numberMantissa = num.mantissa();
279 auto const numberExponent = num.exponent();
280
281 auto const canonicalParts = canonicalizeParts(parts, numberExponent);
282
283 auto const canonicalNum = canonicalizeParts(
285 .mantissa = Number::externalToInternal(numberMantissa),
286 .exponent = numberExponent,
287 .negative = numberMantissa < 0,
288 },
289 canonicalParts.exponent);
290
291 if (canonicalParts.mantissa != canonicalNum.mantissa ||
292 canonicalParts.exponent != canonicalNum.exponent ||
293 canonicalParts.negative != canonicalNum.negative)
294 {
295 Throw<std::runtime_error>("number cannot be represented");
296 }
297
298 return STNumber{field, num};
299}
300
301} // namespace xrpl
Represents a JSON value.
Definition json_value.h:117
Number is a floating point type that can represent a wide range of values.
Definition Number.h:351
constexpr rep mantissa() const noexcept
Returns the mantissa of the external view of the Number.
Definition Number.h:692
static MantissaRange::MantissaScale getMantissaScale()
Returns which mantissa scale is currently in use for normalization.
static internalrep externalToInternal(rep mantissa)
constexpr int exponent() const noexcept
Returns the exponent of the external view of the Number.
Definition Number.h:714
Identifies fields.
Definition SField.h:132
static constexpr auto kSmdNeedsAsset
Definition SField.h:143
bool shouldMeta(int c) const
Definition SField.h:265
A type which can be exported to a well known binary format.
Definition STBase.h:129
SField const & getFName() const
Definition STBase.cpp:120
static STBase * emplace(std::size_t n, void *buf, T &&val)
Definition STBase.h:226
virtual SerializedTypeID getSType() const
Definition STBase.cpp:54
A serializable number.
Definition STNumber.h:42
void add(Serializer &s) const override
Definition STNumber.cpp:69
bool isDefault() const override
Definition STNumber.cpp:147
void associateAsset(Asset const &a) override
Definition STNumber.cpp:56
void setValue(Number const &v)
Definition STNumber.cpp:120
STNumber()=default
std::string getText() const override
Definition STNumber.cpp:50
bool isEquivalent(STBase const &t) const override
Definition STNumber.cpp:138
STBase * move(std::size_t n, void *buf) override
Definition STNumber.cpp:132
SerializedTypeID getSType() const override
Definition STNumber.cpp:44
STBase * copy(std::size_t n, void *buf) const override
Definition STNumber.cpp:126
Number value_
Definition STNumber.h:44
Number const & value() const
Definition STNumber.cpp:114
Intermediate class for any STBase-derived class to store an Asset.
virtual void associateAsset(Asset const &a)
std::optional< Asset > asset_
T max(T... args)
T min(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
NumberParts partsFromString(std::string const &number)
Definition STNumber.cpp:159
std::optional< Rules > const & getCurrentTransactionRules()
Definition Rules.cpp:30
std::ostream & operator<<(std::ostream &out, BaseUInt< Bits, Tag > const &u)
Definition base_uint.h:666
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
SerializedTypeID
Definition SField.h:94
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:735
STNumber numberFromJson(SField const &field, json::Value const &value)
Definition STNumber.cpp:219
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:52
std::uint64_t mantissa
Definition STNumber.h:97