Files
roytam1 aec5e5666e import changes from `dev' branch of rmottola/Arctic-Fox:
- bug 1083930 - cpu spin during large h2/spdy upload r=hurley (0949388a6a)
- Bug 1241906 - Spdy deadlock on suspended channel r=hurley (f40e9677d5)
- Bug 1247205 - dont loop on http2 softerror r=dragana (2ab3cb69ef)
- Bug 1246778 - dont loop in nshttpconnection during shutdown r=dragana (0677b9d34b)
- Bug 1201037 - only send "HTTP pings" on seemingly idle connections, r=mcmanus (134198bc79)
- Bug 1174899 - discarded spdy data with fin attributed to wrong stream r=bagder (f3b801c734)
- Bug 1236170 - Make Http2Session::UncompressAndDiscard push-aware. r=mcmanus (e71634e853)
- Bug 1240025 - incorrect close state on pushed stream r=hurley (eb2832177a)
- Bug 1227931 - init Http2Stream::mReceivedData in the constructor. r=nwgh (44f1d8e897)
- Bug 241788 - net_FilterURIString should filter \r\n\t from the entire URL r=honzab (734d9b8cae)
- Bug 1259459 - h2 0 length options puts end-stream on headers r=hurley (31ac211a9b)
- Bug 1174899 - fixup log format strings for spdy/h2 r=bagder (967c9ff71e)
- Bug 1211694 - dataLength has been added twice. r=mcmanus (6773981db3)
- cleanup (26517f5de0)
- Bug 1247998 - Let nsHttpChannel::AsyncOpen* throw after nsHttpHandler has been shutdown, r=mcmanus (90bb2364be)
- Bug 1231512 - Allow nsIHttpChannel.redirectTo() work also on an open channel, r=jduell (198fb72816)
- Bug 1242472 - Properly propagate mTopWindowURI through redirects. r=francois/ckerschb (1d27a15770)
- Bug 1133873 - some spdy logs r=hurley (cd95cfed5a)
- bug 1215724 - enable brotli on spdy r=hurley (83cca72fa5)
- Bug 137852 - Add a new working HTTP authentication identity to the begining of the session cache list. r=honzab (f670349771)
- Bug 1245414, part 1 - Delete the mfbt/decimal/LICENSE* files since upstream now just uses inline comments. r=Waldo (15bb211e14)
- Bug 1245414, part 2 - Update mfbt/decimal/update.sh to reflect Blink's switch from svn to git, and the different files we now pull. r=Waldo (4dd0b5916a)
- Bug 1245414, part 3 - Overwrite mfbt/decimal/Decimal.* with vanilla upstream copies. r=Waldo (98f7ba4711)
- Bug 1245414, part 4 - Update mfbt/decimal/zero-serialization.patch. r =Waldo (055e1354a7)
- Bug 1245414, part 5 - Update mfbt/decimal/comparison-with-nan.patch. r=Waldo (583e0f3e76)
- Bug 1245414, part 6 - Update mfbt/decimal/mfbt-abi-markers.patch. r=Waldo (148b1ac08b)
- Bug 1245414, part 7 - Update mfbt/decimal/to-moz-dependencies.patch. r=Waldo (2e2a6a33d7)
- Bug 1245414, part 8 - Remove mfbt/decimal/floor-ceiling.patch now that the issue is fixed upstream. r=Waldo (84fc02c068)
- Bug 1245414, part 9 - Disable mfbt/decimal/fix-wshadow-warnings.patch. r=cpeterson (4476d04c5d)
- Bug 1245414, part 10 - Apply the Mozilla patches via mfbt/decimal/update.sh. r=Waldo (1f95ef5524)
- Bug 1247082 - Suppress rendering of nsBackdropFrame for VR content r=dholbert (0ffeae4267)
- Bug 1206545 - Initialize AccessibleCaretEventHub in nsCanvasFrame. r=roc (687d4997fb)
- Bug 591737 - Add SummaryFrame. r=bz (1b750bfeb8)
- Bug 1165893 - Fix rounding issue in nsDisplaySelectionOverlay::Paint. r=mattwoodro (9994cc983a)
- Bug 1245450 - Only setup AutoSaveRestorePerspectiveIndex for the descendants of the element with perspective. r=roc (fe8a350417)
- Bug 1243282 - Wrap items having clips with a separator. r=mattwoodrow (915737e3d0)
- Bug 1223232 - Use GetUsedBorder() instead of the computed border value when calculating CB size. r=roc (f4c05b30c7)
- Bug 1223232 - Crashtest. (394e112818)
- Bug 1230665 - Make anonymous flex/grid items non-tabbable and non-focusable. r=roc (0d3f70e672)
- Bug 1142295 - Closing descriptor when GECKO_DISPLAY_REFLOW_RULES_FILE is setted. r=erahm (664ae6ba0a)
- minor change (b914bd2602)
- Bug 1237754 part 1 - [css-grid][css-align] Make 'align/justify-content:normal' behave as 'stretch' for Grid containers. r=dholbert (09a9a09629)
- Bug 1237754 part 2 - [css-grid][css-align] Test updates to account for new default behavior for 'align/justify-content'. (5e62e837ff)
- minor of Bug 1141931 part 2 (a12f5b430e)
-  Bu 974309: Fixes the IsEditable() logic for table cells. r=ehsan (2a3caa932f)
- Bug 1238137 - Telemetry pings for main thread keyboard-driven scroll input methods. r=ehsan (e9c07427f9)
- Bug 1238137 - Telemetry pings for main thread scrolling to bring the caret into view after moving it in response to keyboard input. r=ehsan (834bc12b7a)
- Bug 1246405 - Declare mTextRun earlier to avoid alignment spill on 64-bit architectures. r=roc (7ba93b72c9)
- Fixing bug 440486. Work around a Windows XP fax dialog bug. r=rstrong. (a59409acd6)
- Bug 1240911 - Prevent SerializedStructuredCloneBuffer from escaping into the heap. r=amarchesini (2c0b7c474b)
- Bug 1240985 - Hold off processing some messages during timeout (r=dvander) (10f6f6d7a2)
- Bug 1146471 - Release thread asserts for IPC (r=dvander) (f94d0ee09a)
- Bug 1240985 - Fix bug where mAwaitingSyncReply can be overwritten in Send after Cancel (r=dvander) (7b95acdca6)
- Bug 1193861: Log to the process log when launching a sandboxed process on Windows. r=billm (0ad1afd0d0)
- Bug 1233061 - add override declarations for MessagePumpForNonMainUIThreads; r=billm (94b9a5bfe9)
- Bug 1172467: Fix an IPC channel file descriptor leak from Nuwa to the child process. r=khuey (908601ed0e)
- Bug 1240985 - Check WasTransactionCanceled after timeout (and avoid timing out) (r=dvander) (33aade0a92)
- Bug 1237458 - Use MOZ_RELEASE_ASSERT for IPC assertions (r=jld) (cb0f058205)
- Bug 1247429 - Warn instead of error if shmem deallocated before IPDL sends it. r=nical (3c94d99b21)
- Bug 1175999 - Deallocate mach SharedMemory properly. r=blassey. (542649b570)
- Bug 1188186 - Fix leak of FDs in |CreateTransport|. r=bds (a40b9a0c58)
-  Bg 1240607 - Force CreateWindow hooks to be detours. r=jmathies (895d1c21c4)
- Bug 1209464: Fix missing neutered window region in MessageChannel::WaitForInterruptNotify. Regression from bug 1189709; r=jimm (204256880b)
- Bug 1229825 - Make GIF deinterlacer respect the frame rect bounds. r=tn (904f6bd9b7)
- Bug 1242093 - Fix assertion in Downscaler::ClearRow. r=njn (63ffe82e99)
- Bug 1235859 - Add FrameSize to non-skia downscaler. r=edwin (e7474630e0)
- Bug 1237709: During RasterImage error-handling cleanup, set UniquePtr mAnim to null instead of using reset(), to avoid leaking. r=dholbert (b064f9c20d)
- Bug 1235605 - Use CheckedInt in Deinterlacer and make its buffer allocation fallible. r=tn (f6f3858c65)
- cleanup (f02aa9441e)
- Bug 1242778: Add MOZ_COUNT_CTOR & MOZ_COUNT_DTOR calls to track leaks of imagelib's FrameAnimator class. r=tn (b1aa366694)
- Bug 1241728. Add crashtest. (17d80a3387)
- Bug 1241729. Add crashtest. (bd6d7337d7)
- Bug 1241728. Limit the size of images that we will downscale from to 1048576 pixels. r=edwin (ad38a82aad)
- Bug 1218782 - use fallible allocations in Downscaler.cpp; r=seth (b22caa1121)
- Bug 1224979. Check if we compute usable filters for the downscaler, and if not put the downscaler in error state so it's not used. r=edwin (8fb59463ef)
- Bug 1235297 - Annotate intentional switch fallthroughs to suppress -Wimplicit-fallthrough warnings in image/decoders/. r=tn (094c37c0fe)
- Bug 1238558 (part 1) - Add Decoder::BeforeFinishInternal(). r=tnikkel. (c7922054d6)
- Bug 1238558 (part 2) - Add a test. r=tnikkel. (7e09caf47f)
- Bug 1238551 (part 2) - Add a test. r=tn. (f548a2cb97)
- Bug 1238551 (part 1) - Reject BITMAPV3INFOHEADER BMP images. r=tn. (c4c8f95cb3)
- Bug 1240629. Don't buffer image file data that we are never going to look at in the gap between the header and the pixel data for BMP files. r=njn (f580910cd3)
- Bug 1237171 - Improve a case where ICO and BMP files disagree on an image size. r=tn. (615db65802)
- Bug 1220021 (part 1) - Don't treat 0RGB ICO files as transparent. r=seth. (b97298285f)
- Bug 1220021 (part 2) - Add four reftests. r=seth. (b1e7b58a98)
- Bug 1163856 (Part 2) - Fix tests that depended on image load event timing. r=tn (4304c676a0)
- Bug 1207958 - Fix heuristic for choosing which ICO sub-image to render - r=tn (3d4db5a033)
- Bug 987625 - Conditionally define MOZ_PNG_MAX_DIMENSION. r=jrmuizel (859bae490c)
- Bug 75077 - Interpolate interlaced PNG images instead of libpng blocky display. r=seth (bc17b43fa6)
- fix side-effect of 1219405 (6536821e18)
- Bug 1245845, part 1 - Stop Moz2D Path::CopyToBuilder/TransformedCopyToBuilder implicitly converting the Path's FillRule. r=Bas (ecc552f359)
- Bug 1245845, part 2 - Remove code that is now useless from gfxContext::EnsurePath. r=Bas (2430be2837)
- Bug 1237448 - Moz2Dify two functions in gfxSurfaceDrawable. r=roc. (bb768302c5)
- Bug 1231888 (follow-up) - Simplify CurrentSurface(). r=jrmuizel. (303cea98f3)
- Bug 1247380: Only copy the background if we can succesfully get a snapshot. r=jrmuizel (13b64445e9)
- Bug 1228507 - Initialize mBlendOpacity. r=Bas (b301a2c9f4)
- Bug 1238846 (part 2) - Remove gfxContext::mOriginalDT, which is unused. r=mattwoodrow. (a5b0f948b7)
- Bug 1240819 - cleanup dead branches in gfxXlibNativeRender.cpp. r=jrmuizel (57bbec6693)
- Bug 1234950 - When advancing APZ animations, use the next vsync timestamp instead of the current one, since that is what will be composited. r=mstange (421829d459)
- Bug 1021845 - Don't skip checkerboarding layers during compositing, even if the layer's visible region is empty. r=botond (6cf1497019)
- Bug 1230149 - check bigImgIter to see if it's not null. r=jmuizelaar (aeef579f9f)
- Bug 1248325 - Update BufferTextureHost::GetAsSurface() r=nical (39a8b3ca71)
- reapply per misspatch Bug 1200595 - Consolidate the TextureClient's destruction logic (68966e4dc3)
- Bug 1249245 - Add missing header gfxPrefs.h to GrallocTextureClient.cpp. r=cyu (676669eb01)
- Bug 1245057: Refer to |gfx::IntPoint| in |GrallocTextureHostOGL::SetCropRect|, r=sotaro (99e572f3f6)
- Bug 1240867 - Fix non-unified build bustage in OGLShaderProgram.cpp. r=nical (0071f08285)
- Bug 1238015 - Make sure PTexture actors are destroyed after all messages referring to them are sent. r=sotaro (250f99b4a4)
- Bug 1220895 - Add layerviewer for layer tree & display list visualization NPOTB. r=botond (fa211145a1)
- Bug 1213464 - ImageBridgeChild and CompositorChild should delete their Transport. r=billm (a37a0dbdfd)
-  Bug 1234343 (part 1) - Make GfxMemoryImageReporter::sAmount signed. r=Bas. (18f0cb61ec)
- Bug 1234343 (part 2) - Add a missing GfxMemoryImageReporter::DidAlloc() call. r=Bas. (69df7f3674)
- Bug 1245249 - Check actor state before calling Send__delete__(); r=luke (65716a5915)
- Bug 1221418 - A better cleanup method for AsmJSCache::ChildRunnable, r=janv (5c8c023b9d)
- Bug 1235657 - Session storage needs to handle origin attributes correctly - part 1 - createOriginAttributesWithUserContextId, r=huseby (f2df8109ef)
- Bug 1245954 - Console StartTimer/StopTimer and IncrementCounter should run in the owning thread, r=bz (64f73d7759)
- Bug 1245957 - Adding assertions in Console about in which thread is running what, r=bz (291ee70e2d)
- Bug 1248022 - ConsoleEvent.styles can be a sequence of nullable strings, r=bz (b94ec79ac0)
- Bug 1245242 - Normalize to unit vector for DOMMatrix.rotateAxisAngleSelf. r=roc (3a9e684b4d)
- Bug 1236329. Back out the patch for bug 492933 (revision d8012b35413b) because it's not web-compatible in practice. r=smaug (f6540d84c3)
- mTarget can be null in CanvasRenderingContext2D::ClearRect(), return early if so. (13e8a4e26a)
2023-12-26 09:36:39 +08:00

1051 lines
28 KiB
C++

/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "Decimal.h"
#include "moz-decimal-utils.h"
using namespace moz_decimal_utils;
#include <algorithm>
#include <float.h>
namespace blink {
namespace DecimalPrivate {
static int const ExponentMax = 1023;
static int const ExponentMin = -1023;
static int const Precision = 18;
static const uint64_t MaxCoefficient = UINT64_C(0xDE0B6B3A763FFFF); // 999999999999999999 == 18 9's
// This class handles Decimal special values.
class SpecialValueHandler {
STACK_ALLOCATED();
WTF_MAKE_NONCOPYABLE(SpecialValueHandler);
public:
enum HandleResult {
BothFinite,
BothInfinity,
EitherNaN,
LHSIsInfinity,
RHSIsInfinity,
};
SpecialValueHandler(const Decimal& lhs, const Decimal& rhs);
HandleResult handle();
Decimal value() const;
private:
enum Result {
ResultIsLHS,
ResultIsRHS,
ResultIsUnknown,
};
const Decimal& m_lhs;
const Decimal& m_rhs;
Result m_result;
};
SpecialValueHandler::SpecialValueHandler(const Decimal& lhs, const Decimal& rhs)
: m_lhs(lhs), m_rhs(rhs), m_result(ResultIsUnknown)
{
}
SpecialValueHandler::HandleResult SpecialValueHandler::handle()
{
if (m_lhs.isFinite() && m_rhs.isFinite())
return BothFinite;
const Decimal::EncodedData::FormatClass lhsClass = m_lhs.value().formatClass();
const Decimal::EncodedData::FormatClass rhsClass = m_rhs.value().formatClass();
if (lhsClass == Decimal::EncodedData::ClassNaN) {
m_result = ResultIsLHS;
return EitherNaN;
}
if (rhsClass == Decimal::EncodedData::ClassNaN) {
m_result = ResultIsRHS;
return EitherNaN;
}
if (lhsClass == Decimal::EncodedData::ClassInfinity)
return rhsClass == Decimal::EncodedData::ClassInfinity ? BothInfinity : LHSIsInfinity;
if (rhsClass == Decimal::EncodedData::ClassInfinity)
return RHSIsInfinity;
ASSERT_NOT_REACHED();
return BothFinite;
}
Decimal SpecialValueHandler::value() const
{
switch (m_result) {
case ResultIsLHS:
return m_lhs;
case ResultIsRHS:
return m_rhs;
case ResultIsUnknown:
default:
ASSERT_NOT_REACHED();
return m_lhs;
}
}
// This class is used for 128 bit unsigned integer arithmetic.
class UInt128 {
public:
UInt128(uint64_t low, uint64_t high)
: m_high(high), m_low(low)
{
}
UInt128& operator/=(uint32_t);
uint64_t high() const { return m_high; }
uint64_t low() const { return m_low; }
static UInt128 multiply(uint64_t u, uint64_t v) { return UInt128(u * v, multiplyHigh(u, v)); }
private:
static uint32_t highUInt32(uint64_t x) { return static_cast<uint32_t>(x >> 32); }
static uint32_t lowUInt32(uint64_t x) { return static_cast<uint32_t>(x & ((static_cast<uint64_t>(1) << 32) - 1)); }
static uint64_t makeUInt64(uint32_t low, uint32_t high) { return low | (static_cast<uint64_t>(high) << 32); }
static uint64_t multiplyHigh(uint64_t, uint64_t);
uint64_t m_high;
uint64_t m_low;
};
UInt128& UInt128::operator/=(const uint32_t divisor)
{
ASSERT(divisor);
if (!m_high) {
m_low /= divisor;
return *this;
}
uint32_t dividend[4];
dividend[0] = lowUInt32(m_low);
dividend[1] = highUInt32(m_low);
dividend[2] = lowUInt32(m_high);
dividend[3] = highUInt32(m_high);
uint32_t quotient[4];
uint32_t remainder = 0;
for (int i = 3; i >= 0; --i) {
const uint64_t work = makeUInt64(dividend[i], remainder);
remainder = static_cast<uint32_t>(work % divisor);
quotient[i] = static_cast<uint32_t>(work / divisor);
}
m_low = makeUInt64(quotient[0], quotient[1]);
m_high = makeUInt64(quotient[2], quotient[3]);
return *this;
}
// Returns high 64bit of 128bit product.
uint64_t UInt128::multiplyHigh(uint64_t u, uint64_t v)
{
const uint64_t uLow = lowUInt32(u);
const uint64_t uHigh = highUInt32(u);
const uint64_t vLow = lowUInt32(v);
const uint64_t vHigh = highUInt32(v);
const uint64_t partialProduct = uHigh * vLow + highUInt32(uLow * vLow);
return uHigh * vHigh + highUInt32(partialProduct) + highUInt32(uLow * vHigh + lowUInt32(partialProduct));
}
static int countDigits(uint64_t x)
{
int numberOfDigits = 0;
for (uint64_t powerOfTen = 1; x >= powerOfTen; powerOfTen *= 10) {
++numberOfDigits;
if (powerOfTen >= std::numeric_limits<uint64_t>::max() / 10)
break;
}
return numberOfDigits;
}
static uint64_t scaleDown(uint64_t x, int n)
{
ASSERT(n >= 0);
while (n > 0 && x) {
x /= 10;
--n;
}
return x;
}
static uint64_t scaleUp(uint64_t x, int n)
{
ASSERT(n >= 0);
ASSERT(n <= Precision);
uint64_t y = 1;
uint64_t z = 10;
for (;;) {
if (n & 1)
y = y * z;
n >>= 1;
if (!n)
return x * y;
z = z * z;
}
}
} // namespace DecimalPrivate
using namespace DecimalPrivate;
Decimal::EncodedData::EncodedData(Sign sign, FormatClass formatClass)
: m_coefficient(0)
, m_exponent(0)
, m_formatClass(formatClass)
, m_sign(sign)
{
}
Decimal::EncodedData::EncodedData(Sign sign, int exponent, uint64_t coefficient)
: m_formatClass(coefficient ? ClassNormal : ClassZero)
, m_sign(sign)
{
if (exponent >= ExponentMin && exponent <= ExponentMax) {
while (coefficient > MaxCoefficient) {
coefficient /= 10;
++exponent;
}
}
if (exponent > ExponentMax) {
m_coefficient = 0;
m_exponent = 0;
m_formatClass = ClassInfinity;
return;
}
if (exponent < ExponentMin) {
m_coefficient = 0;
m_exponent = 0;
m_formatClass = ClassZero;
return;
}
m_coefficient = coefficient;
m_exponent = static_cast<int16_t>(exponent);
}
bool Decimal::EncodedData::operator==(const EncodedData& another) const
{
return m_sign == another.m_sign
&& m_formatClass == another.m_formatClass
&& m_exponent == another.m_exponent
&& m_coefficient == another.m_coefficient;
}
Decimal::Decimal(int32_t i32)
: m_data(i32 < 0 ? Negative : Positive, 0, i32 < 0 ? static_cast<uint64_t>(-static_cast<int64_t>(i32)) : static_cast<uint64_t>(i32))
{
}
Decimal::Decimal(Sign sign, int exponent, uint64_t coefficient)
: m_data(sign, coefficient ? exponent : 0, coefficient)
{
}
Decimal::Decimal(const EncodedData& data)
: m_data(data)
{
}
Decimal::Decimal(const Decimal& other)
: m_data(other.m_data)
{
}
Decimal& Decimal::operator=(const Decimal& other)
{
m_data = other.m_data;
return *this;
}
Decimal& Decimal::operator+=(const Decimal& other)
{
m_data = (*this + other).m_data;
return *this;
}
Decimal& Decimal::operator-=(const Decimal& other)
{
m_data = (*this - other).m_data;
return *this;
}
Decimal& Decimal::operator*=(const Decimal& other)
{
m_data = (*this * other).m_data;
return *this;
}
Decimal& Decimal::operator/=(const Decimal& other)
{
m_data = (*this / other).m_data;
return *this;
}
Decimal Decimal::operator-() const
{
if (isNaN())
return *this;
Decimal result(*this);
result.m_data.setSign(invertSign(m_data.sign()));
return result;
}
Decimal Decimal::operator+(const Decimal& rhs) const
{
const Decimal& lhs = *this;
const Sign lhsSign = lhs.sign();
const Sign rhsSign = rhs.sign();
SpecialValueHandler handler(lhs, rhs);
switch (handler.handle()) {
case SpecialValueHandler::BothFinite:
break;
case SpecialValueHandler::BothInfinity:
return lhsSign == rhsSign ? lhs : nan();
case SpecialValueHandler::EitherNaN:
return handler.value();
case SpecialValueHandler::LHSIsInfinity:
return lhs;
case SpecialValueHandler::RHSIsInfinity:
return rhs;
}
const AlignedOperands alignedOperands = alignOperands(lhs, rhs);
const uint64_t result = lhsSign == rhsSign
? alignedOperands.lhsCoefficient + alignedOperands.rhsCoefficient
: alignedOperands.lhsCoefficient - alignedOperands.rhsCoefficient;
if (lhsSign == Negative && rhsSign == Positive && !result)
return Decimal(Positive, alignedOperands.exponent, 0);
return static_cast<int64_t>(result) >= 0
? Decimal(lhsSign, alignedOperands.exponent, result)
: Decimal(invertSign(lhsSign), alignedOperands.exponent, -static_cast<int64_t>(result));
}
Decimal Decimal::operator-(const Decimal& rhs) const
{
const Decimal& lhs = *this;
const Sign lhsSign = lhs.sign();
const Sign rhsSign = rhs.sign();
SpecialValueHandler handler(lhs, rhs);
switch (handler.handle()) {
case SpecialValueHandler::BothFinite:
break;
case SpecialValueHandler::BothInfinity:
return lhsSign == rhsSign ? nan() : lhs;
case SpecialValueHandler::EitherNaN:
return handler.value();
case SpecialValueHandler::LHSIsInfinity:
return lhs;
case SpecialValueHandler::RHSIsInfinity:
return infinity(invertSign(rhsSign));
}
const AlignedOperands alignedOperands = alignOperands(lhs, rhs);
const uint64_t result = lhsSign == rhsSign
? alignedOperands.lhsCoefficient - alignedOperands.rhsCoefficient
: alignedOperands.lhsCoefficient + alignedOperands.rhsCoefficient;
if (lhsSign == Negative && rhsSign == Negative && !result)
return Decimal(Positive, alignedOperands.exponent, 0);
return static_cast<int64_t>(result) >= 0
? Decimal(lhsSign, alignedOperands.exponent, result)
: Decimal(invertSign(lhsSign), alignedOperands.exponent, -static_cast<int64_t>(result));
}
Decimal Decimal::operator*(const Decimal& rhs) const
{
const Decimal& lhs = *this;
const Sign lhsSign = lhs.sign();
const Sign rhsSign = rhs.sign();
const Sign resultSign = lhsSign == rhsSign ? Positive : Negative;
SpecialValueHandler handler(lhs, rhs);
switch (handler.handle()) {
case SpecialValueHandler::BothFinite: {
const uint64_t lhsCoefficient = lhs.m_data.coefficient();
const uint64_t rhsCoefficient = rhs.m_data.coefficient();
int resultExponent = lhs.exponent() + rhs.exponent();
UInt128 work(UInt128::multiply(lhsCoefficient, rhsCoefficient));
while (work.high()) {
work /= 10;
++resultExponent;
}
return Decimal(resultSign, resultExponent, work.low());
}
case SpecialValueHandler::BothInfinity:
return infinity(resultSign);
case SpecialValueHandler::EitherNaN:
return handler.value();
case SpecialValueHandler::LHSIsInfinity:
return rhs.isZero() ? nan() : infinity(resultSign);
case SpecialValueHandler::RHSIsInfinity:
return lhs.isZero() ? nan() : infinity(resultSign);
}
ASSERT_NOT_REACHED();
return nan();
}
Decimal Decimal::operator/(const Decimal& rhs) const
{
const Decimal& lhs = *this;
const Sign lhsSign = lhs.sign();
const Sign rhsSign = rhs.sign();
const Sign resultSign = lhsSign == rhsSign ? Positive : Negative;
SpecialValueHandler handler(lhs, rhs);
switch (handler.handle()) {
case SpecialValueHandler::BothFinite:
break;
case SpecialValueHandler::BothInfinity:
return nan();
case SpecialValueHandler::EitherNaN:
return handler.value();
case SpecialValueHandler::LHSIsInfinity:
return infinity(resultSign);
case SpecialValueHandler::RHSIsInfinity:
return zero(resultSign);
}
ASSERT(lhs.isFinite());
ASSERT(rhs.isFinite());
if (rhs.isZero())
return lhs.isZero() ? nan() : infinity(resultSign);
int resultExponent = lhs.exponent() - rhs.exponent();
if (lhs.isZero())
return Decimal(resultSign, resultExponent, 0);
uint64_t remainder = lhs.m_data.coefficient();
const uint64_t divisor = rhs.m_data.coefficient();
uint64_t result = 0;
for (;;) {
while (remainder < divisor && result < MaxCoefficient / 10) {
remainder *= 10;
result *= 10;
--resultExponent;
}
if (remainder < divisor)
break;
uint64_t quotient = remainder / divisor;
if (result > MaxCoefficient - quotient)
break;
result += quotient;
remainder %= divisor;
if (!remainder)
break;
}
if (remainder > divisor / 2)
++result;
return Decimal(resultSign, resultExponent, result);
}
bool Decimal::operator==(const Decimal& rhs) const
{
if (isNaN() || rhs.isNaN())
return false;
return m_data == rhs.m_data || compareTo(rhs).isZero();
}
bool Decimal::operator!=(const Decimal& rhs) const
{
if (isNaN() || rhs.isNaN())
return true;
if (m_data == rhs.m_data)
return false;
const Decimal result = compareTo(rhs);
if (result.isNaN())
return false;
return !result.isZero();
}
bool Decimal::operator<(const Decimal& rhs) const
{
const Decimal result = compareTo(rhs);
if (result.isNaN())
return false;
return !result.isZero() && result.isNegative();
}
bool Decimal::operator<=(const Decimal& rhs) const
{
if (isNaN() || rhs.isNaN())
return false;
if (m_data == rhs.m_data)
return true;
const Decimal result = compareTo(rhs);
if (result.isNaN())
return false;
return result.isZero() || result.isNegative();
}
bool Decimal::operator>(const Decimal& rhs) const
{
const Decimal result = compareTo(rhs);
if (result.isNaN())
return false;
return !result.isZero() && result.isPositive();
}
bool Decimal::operator>=(const Decimal& rhs) const
{
if (isNaN() || rhs.isNaN())
return false;
if (m_data == rhs.m_data)
return true;
const Decimal result = compareTo(rhs);
if (result.isNaN())
return false;
return result.isZero() || !result.isNegative();
}
Decimal Decimal::abs() const
{
Decimal result(*this);
result.m_data.setSign(Positive);
return result;
}
Decimal::AlignedOperands Decimal::alignOperands(const Decimal& lhs, const Decimal& rhs)
{
ASSERT(lhs.isFinite());
ASSERT(rhs.isFinite());
const int lhsExponent = lhs.exponent();
const int rhsExponent = rhs.exponent();
int exponent = std::min(lhsExponent, rhsExponent);
uint64_t lhsCoefficient = lhs.m_data.coefficient();
uint64_t rhsCoefficient = rhs.m_data.coefficient();
if (lhsExponent > rhsExponent) {
const int numberOfLHSDigits = countDigits(lhsCoefficient);
if (numberOfLHSDigits) {
const int lhsShiftAmount = lhsExponent - rhsExponent;
const int overflow = numberOfLHSDigits + lhsShiftAmount - Precision;
if (overflow <= 0) {
lhsCoefficient = scaleUp(lhsCoefficient, lhsShiftAmount);
} else {
lhsCoefficient = scaleUp(lhsCoefficient, lhsShiftAmount - overflow);
rhsCoefficient = scaleDown(rhsCoefficient, overflow);
exponent += overflow;
}
}
} else if (lhsExponent < rhsExponent) {
const int numberOfRHSDigits = countDigits(rhsCoefficient);
if (numberOfRHSDigits) {
const int rhsShiftAmount = rhsExponent - lhsExponent;
const int overflow = numberOfRHSDigits + rhsShiftAmount - Precision;
if (overflow <= 0) {
rhsCoefficient = scaleUp(rhsCoefficient, rhsShiftAmount);
} else {
rhsCoefficient = scaleUp(rhsCoefficient, rhsShiftAmount - overflow);
lhsCoefficient = scaleDown(lhsCoefficient, overflow);
exponent += overflow;
}
}
}
AlignedOperands alignedOperands;
alignedOperands.exponent = exponent;
alignedOperands.lhsCoefficient = lhsCoefficient;
alignedOperands.rhsCoefficient = rhsCoefficient;
return alignedOperands;
}
static bool isMultiplePowersOfTen(uint64_t coefficient, int n)
{
return !coefficient || !(coefficient % scaleUp(1, n));
}
// Round toward positive infinity.
Decimal Decimal::ceil() const
{
if (isSpecial())
return *this;
if (exponent() >= 0)
return *this;
uint64_t result = m_data.coefficient();
const int numberOfDigits = countDigits(result);
const int numberOfDropDigits = -exponent();
if (numberOfDigits <= numberOfDropDigits)
return isPositive() ? Decimal(1) : zero(Positive);
result = scaleDown(result, numberOfDropDigits);
if (isPositive() && !isMultiplePowersOfTen(m_data.coefficient(), numberOfDropDigits))
++result;
return Decimal(sign(), 0, result);
}
Decimal Decimal::compareTo(const Decimal& rhs) const
{
const Decimal result(*this - rhs);
switch (result.m_data.formatClass()) {
case EncodedData::ClassInfinity:
return result.isNegative() ? Decimal(-1) : Decimal(1);
case EncodedData::ClassNaN:
case EncodedData::ClassNormal:
return result;
case EncodedData::ClassZero:
return zero(Positive);
default:
ASSERT_NOT_REACHED();
return nan();
}
}
// Round toward negative infinity.
Decimal Decimal::floor() const
{
if (isSpecial())
return *this;
if (exponent() >= 0)
return *this;
uint64_t result = m_data.coefficient();
const int numberOfDigits = countDigits(result);
const int numberOfDropDigits = -exponent();
if (numberOfDigits < numberOfDropDigits)
return isPositive() ? zero(Positive) : Decimal(-1);
result = scaleDown(result, numberOfDropDigits);
if (isNegative() && !isMultiplePowersOfTen(m_data.coefficient(), numberOfDropDigits))
++result;
return Decimal(sign(), 0, result);
}
Decimal Decimal::fromDouble(double doubleValue)
{
if (std::isfinite(doubleValue))
return fromString(mozToString(doubleValue));
if (std::isinf(doubleValue))
return infinity(doubleValue < 0 ? Negative : Positive);
return nan();
}
Decimal Decimal::fromString(const String& str)
{
int exponent = 0;
Sign exponentSign = Positive;
int numberOfDigits = 0;
int numberOfDigitsAfterDot = 0;
int numberOfExtraDigits = 0;
Sign sign = Positive;
enum {
StateDigit,
StateDot,
StateDotDigit,
StateE,
StateEDigit,
StateESign,
StateSign,
StateStart,
StateZero,
} state = StateStart;
#define HandleCharAndBreak(expected, nextState) \
if (ch == expected) { \
state = nextState; \
break; \
}
#define HandleTwoCharsAndBreak(expected1, expected2, nextState) \
if (ch == expected1 || ch == expected2) { \
state = nextState; \
break; \
}
uint64_t accumulator = 0;
for (unsigned index = 0; index < str.length(); ++index) {
const int ch = str[index];
switch (state) {
case StateDigit:
if (ch >= '0' && ch <= '9') {
if (numberOfDigits < Precision) {
++numberOfDigits;
accumulator *= 10;
accumulator += ch - '0';
} else {
++numberOfExtraDigits;
}
break;
}
HandleCharAndBreak('.', StateDot);
HandleTwoCharsAndBreak('E', 'e', StateE);
return nan();
case StateDot:
case StateDotDigit:
if (ch >= '0' && ch <= '9') {
if (numberOfDigits < Precision) {
++numberOfDigits;
++numberOfDigitsAfterDot;
accumulator *= 10;
accumulator += ch - '0';
}
state = StateDotDigit;
break;
}
HandleTwoCharsAndBreak('E', 'e', StateE);
return nan();
case StateE:
if (ch == '+') {
exponentSign = Positive;
state = StateESign;
break;
}
if (ch == '-') {
exponentSign = Negative;
state = StateESign;
break;
}
if (ch >= '0' && ch <= '9') {
exponent = ch - '0';
state = StateEDigit;
break;
}
return nan();
case StateEDigit:
if (ch >= '0' && ch <= '9') {
exponent *= 10;
exponent += ch - '0';
if (exponent > ExponentMax + Precision) {
if (accumulator)
return exponentSign == Negative ? zero(Positive) : infinity(sign);
return zero(sign);
}
state = StateEDigit;
break;
}
return nan();
case StateESign:
if (ch >= '0' && ch <= '9') {
exponent = ch - '0';
state = StateEDigit;
break;
}
return nan();
case StateSign:
if (ch >= '1' && ch <= '9') {
accumulator = ch - '0';
numberOfDigits = 1;
state = StateDigit;
break;
}
HandleCharAndBreak('0', StateZero);
return nan();
case StateStart:
if (ch >= '1' && ch <= '9') {
accumulator = ch - '0';
numberOfDigits = 1;
state = StateDigit;
break;
}
if (ch == '-') {
sign = Negative;
state = StateSign;
break;
}
if (ch == '+') {
sign = Positive;
state = StateSign;
break;
}
HandleCharAndBreak('0', StateZero);
HandleCharAndBreak('.', StateDot);
return nan();
case StateZero:
if (ch == '0')
break;
if (ch >= '1' && ch <= '9') {
accumulator = ch - '0';
numberOfDigits = 1;
state = StateDigit;
break;
}
HandleCharAndBreak('.', StateDot);
HandleTwoCharsAndBreak('E', 'e', StateE);
return nan();
default:
ASSERT_NOT_REACHED();
return nan();
}
}
if (state == StateZero)
return zero(sign);
if (state == StateDigit || state == StateEDigit || state == StateDotDigit) {
int resultExponent = exponent * (exponentSign == Negative ? -1 : 1) - numberOfDigitsAfterDot + numberOfExtraDigits;
if (resultExponent < ExponentMin)
return zero(Positive);
const int overflow = resultExponent - ExponentMax + 1;
if (overflow > 0) {
if (overflow + numberOfDigits - numberOfDigitsAfterDot > Precision)
return infinity(sign);
accumulator = scaleUp(accumulator, overflow);
resultExponent -= overflow;
}
return Decimal(sign, resultExponent, accumulator);
}
return nan();
}
Decimal Decimal::infinity(const Sign sign)
{
return Decimal(EncodedData(sign, EncodedData::ClassInfinity));
}
Decimal Decimal::nan()
{
return Decimal(EncodedData(Positive, EncodedData::ClassNaN));
}
Decimal Decimal::remainder(const Decimal& rhs) const
{
const Decimal quotient = *this / rhs;
return quotient.isSpecial() ? quotient : *this - (quotient.isNegative() ? quotient.ceil() : quotient.floor()) * rhs;
}
Decimal Decimal::round() const
{
if (isSpecial())
return *this;
if (exponent() >= 0)
return *this;
uint64_t result = m_data.coefficient();
const int numberOfDigits = countDigits(result);
const int numberOfDropDigits = -exponent();
if (numberOfDigits < numberOfDropDigits)
return zero(Positive);
result = scaleDown(result, numberOfDropDigits - 1);
if (result % 10 >= 5)
result += 10;
result /= 10;
return Decimal(sign(), 0, result);
}
double Decimal::toDouble() const
{
if (isFinite()) {
bool valid;
const double doubleValue = mozToDouble(toString(), &valid);
return valid ? doubleValue : std::numeric_limits<double>::quiet_NaN();
}
if (isInfinity())
return isNegative() ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity();
return std::numeric_limits<double>::quiet_NaN();
}
String Decimal::toString() const
{
switch (m_data.formatClass()) {
case EncodedData::ClassInfinity:
return sign() ? "-Infinity" : "Infinity";
case EncodedData::ClassNaN:
return "NaN";
case EncodedData::ClassNormal:
case EncodedData::ClassZero:
break;
default:
ASSERT_NOT_REACHED();
return "";
}
StringBuilder builder;
if (sign())
builder.append('-');
int originalExponent = exponent();
uint64_t coefficient = m_data.coefficient();
if (originalExponent < 0) {
const int maxDigits = DBL_DIG;
uint64_t lastDigit = 0;
while (countDigits(coefficient) > maxDigits) {
lastDigit = coefficient % 10;
coefficient /= 10;
++originalExponent;
}
if (lastDigit >= 5)
++coefficient;
while (originalExponent < 0 && coefficient && !(coefficient % 10)) {
coefficient /= 10;
++originalExponent;
}
}
const String digits = mozToString(coefficient);
int coefficientLength = static_cast<int>(digits.length());
const int adjustedExponent = originalExponent + coefficientLength - 1;
if (originalExponent <= 0 && adjustedExponent >= -6) {
if (!originalExponent) {
builder.append(digits);
return builder.toString();
}
if (adjustedExponent >= 0) {
for (int i = 0; i < coefficientLength; ++i) {
builder.append(digits[i]);
if (i == adjustedExponent)
builder.append('.');
}
return builder.toString();
}
builder.appendLiteral("0.");
for (int i = adjustedExponent + 1; i < 0; ++i)
builder.append('0');
builder.append(digits);
} else {
builder.append(digits[0]);
while (coefficientLength >= 2 && digits[coefficientLength - 1] == '0')
--coefficientLength;
if (coefficientLength >= 2) {
builder.append('.');
for (int i = 1; i < coefficientLength; ++i)
builder.append(digits[i]);
}
if (adjustedExponent) {
builder.append(adjustedExponent < 0 ? "e" : "e+");
builder.appendNumber(adjustedExponent);
}
}
return builder.toString();
}
bool Decimal::toString(char* strBuf, size_t bufLength) const
{
ASSERT(bufLength > 0);
String str = toString();
size_t length = str.copy(strBuf, bufLength);
if (length < bufLength) {
strBuf[length] = '\0';
return true;
}
strBuf[bufLength - 1] = '\0';
return false;
}
Decimal Decimal::zero(Sign sign)
{
return Decimal(EncodedData(sign, EncodedData::ClassZero));
}
} // namespace blink