diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index 8f65c0ae3a..79fcd1cd70 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -1914,6 +1914,10 @@
True
True
+
+ True
+ True
+
True
True
@@ -1956,6 +1960,8 @@
+
+
@@ -1994,6 +2000,10 @@
True
True
+
+ True
+ True
+
True
True
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index fcd61ab9ff..8e33c75f13 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -2652,6 +2652,9 @@
ripple\basics\impl
+
+ ripple\basics\impl
+
ripple\basics\impl
@@ -2688,6 +2691,9 @@
ripple\basics
+
+ ripple\basics
+
ripple\basics
@@ -2733,6 +2739,9 @@
ripple\basics\tests
+
+ ripple\basics\tests
+
ripple\basics\tests
diff --git a/src/ripple/app/misc/impl/TxQ.cpp b/src/ripple/app/misc/impl/TxQ.cpp
index 44c82a7f42..49dea107fe 100644
--- a/src/ripple/app/misc/impl/TxQ.cpp
+++ b/src/ripple/app/misc/impl/TxQ.cpp
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include
#include
@@ -62,9 +63,10 @@ getFeeLevelPaid(
// TODO: getRequiredFeeLevel(ttREFERENCE)?
auto referenceFee =
ripple::getRequiredFeeLevel(ttACCOUNT_SET);
- return mulDivNoThrow(tx[sfFee].xrp().drops(),
+ // Don't care about the overflow flag
+ return mulDiv(tx[sfFee].xrp().drops(),
baseRefLevel * referenceFee,
- refTxnCostDrops * requiredFee);
+ refTxnCostDrops * requiredFee).second;
}
//////////////////////////////////////////////////////////////////////////
@@ -169,8 +171,9 @@ FeeMetrics::scaleFeeLevel(OpenView const& view) const
if (current > target)
{
// Compute escalated fee level
- fee = mulDivNoThrow(fee, current * current *
- multiplier, target * target);
+ // Don't care about the overflow flag
+ fee = mulDiv(fee, current * current *
+ multiplier, target * target).second;
}
return fee;
@@ -368,9 +371,10 @@ TxQ::apply(Application& app, OpenView& view,
{
// Is the current transaction's fee higher than
// the queued transaction's fee?
- auto requiredRetryLevel = mulDivNoThrow(
+ // Don't care about the overflow flag
+ auto requiredRetryLevel = mulDiv(
existingCandidate->feeLevel,
- setup_.retrySequencePercent, 100);
+ setup_.retrySequencePercent, 100).second;
JLOG(j_.trace) << "Found transaction in queue for account " <<
account << " with sequence number " << sequence <<
" new txn fee level is " << feeLevelPaid <<
@@ -693,18 +697,19 @@ TxQ::doRPC(Application& app) const
auto const baseFee = view->fees().base;
auto& drops = ret[jss::drops] = Json::Value();
- drops[jss::base_fee] = to_string(mulDivNoThrow(
+ // Don't care about the overflow flags
+ drops[jss::base_fee] = to_string(mulDiv(
metrics.referenceFeeLevel, baseFee,
- metrics.referenceFeeLevel));
- drops[jss::minimum_fee] = to_string(mulDivNoThrow(
+ metrics.referenceFeeLevel).second);
+ drops[jss::minimum_fee] = to_string(mulDiv(
metrics.minFeeLevel, baseFee,
- metrics.referenceFeeLevel));
- drops[jss::median_fee] = to_string(mulDivNoThrow(
+ metrics.referenceFeeLevel).second);
+ drops[jss::median_fee] = to_string(mulDiv(
metrics.medFeeLevel, baseFee,
- metrics.referenceFeeLevel));
- drops[jss::open_ledger_fee] = to_string(mulDivNoThrow(
+ metrics.referenceFeeLevel).second);
+ drops[jss::open_ledger_fee] = to_string(mulDiv(
metrics.expFeeLevel, baseFee,
- metrics.referenceFeeLevel));
+ metrics.referenceFeeLevel).second);
return ret;
}
@@ -714,8 +719,9 @@ TxQ::openLedgerFee(OpenView const& view) const
{
auto metrics = getMetrics(view);
- return mulDivNoThrow(metrics.expFeeLevel,
- view.fees().base, metrics.referenceFeeLevel) + 1;
+ // Don't care about the overflow flag
+ return mulDiv(metrics.expFeeLevel,
+ view.fees().base, metrics.referenceFeeLevel).second + 1;
}
//////////////////////////////////////////////////////////////////////////
diff --git a/src/ripple/basics/impl/mulDiv.cpp b/src/ripple/basics/impl/mulDiv.cpp
new file mode 100644
index 0000000000..83e768e754
--- /dev/null
+++ b/src/ripple/basics/impl/mulDiv.cpp
@@ -0,0 +1,65 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012-2015 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ripple
+{
+
+// compute (value)*(mul)/(div) - avoid overflow but keep precision
+std::pair
+mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
+{
+ if ((value == 0 || mul == 0) && div != 0)
+ return{ true, 0 };
+ lowestTerms(value, div);
+ lowestTerms(mul, div);
+
+ if (value < mul)
+ std::swap(value, mul);
+ constexpr std::uint64_t max =
+ std::numeric_limits::max();
+ const auto limit = max / mul;
+ if (value > limit)
+ {
+ value /= div;
+ if (value > limit)
+ return{ false, max };
+ return{ true, value * mul };
+ }
+ return{ true, value * mul / div };
+}
+
+// compute (value)*(mul)/(div) - avoid overflow but keep precision
+std::uint64_t
+mulDivThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
+{
+ auto const result = mulDiv(value, mul, div);
+ if(!result.first)
+ Throw("mulDiv");
+ return result.second;
+}
+
+
+} // ripple
diff --git a/src/ripple/basics/mulDiv.h b/src/ripple/basics/mulDiv.h
new file mode 100644
index 0000000000..cd7d41529f
--- /dev/null
+++ b/src/ripple/basics/mulDiv.h
@@ -0,0 +1,80 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012-2015 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_BASICS_MULDIV_H_INCLUDED
+#define RIPPLE_BASICS_MULDIV_H_INCLUDED
+
+#include
+#include
+#include
+
+namespace ripple
+{
+
+/** Return value*mul/div accurately.
+ Computes the result of the multiplication and division in
+ a single step, avoiding overflow and retaining precision.
+ Throws:
+ None
+ Returns:
+ `std::pair`:
+ `first` is `false` if the calculation overflows,
+ `true` if the calculation is safe.
+ `second` is the result of the calculation if
+ `first` is `false`, max value of `uint64_t`
+ if `true`.
+*/
+std::pair
+mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
+
+/** Return value*mul/div accurately.
+ Computes the result of the multiplication and division in
+ a single step, avoiding overflow and retaining precision.
+ Throws:
+ std::overflow_error
+*/
+std::uint64_t
+mulDivThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
+
+template ::value &&
+ std::is_unsigned::value &&
+ sizeof(T1) <= sizeof(std::uint64_t) >,
+ class = std::enable_if_t <
+ std::is_integral::value &&
+ std::is_unsigned::value &&
+ sizeof(T2) <= sizeof(std::uint64_t) >
+>
+void lowestTerms(T1& a, T2& b)
+{
+ std::uint64_t x = a, y = b;
+ while (y != 0)
+ {
+ auto t = x % y;
+ x = y;
+ y = t;
+ }
+ a /= x;
+ b /= x;
+}
+
+} // ripple
+
+#endif
diff --git a/src/ripple/basics/tests/mulDiv.test.cpp b/src/ripple/basics/tests/mulDiv.test.cpp
new file mode 100644
index 0000000000..2658556bd5
--- /dev/null
+++ b/src/ripple/basics/tests/mulDiv.test.cpp
@@ -0,0 +1,74 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012-2016 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include
+#include
+#include
+
+namespace ripple {
+namespace test {
+
+struct mulDiv_test : beast::unit_test::suite
+{
+ void run()
+ {
+ const auto max = std::numeric_limits::max();
+ const std::uint64_t max32 = std::numeric_limits::max();
+
+ auto result = mulDiv(85, 20, 5);
+ expect(result.first && result.second == 340);
+ result = mulDiv(20, 85, 5);
+ expect(result.first && result.second == 340);
+
+ result = mulDiv(0, max - 1, max - 3);
+ expect(result.first && result.second == 0);
+ result = mulDiv(max - 1, 0, max - 3);
+ expect(result.first && result.second == 0);
+
+ result = mulDiv(max, 2, max / 2);
+ expect(result.first && result.second == 4);
+ result = mulDiv(max, 1000, max / 1000);
+ expect(result.first && result.second == 1000000);
+ result = mulDiv(max, 1000, max / 1001);
+ expect(result.first && result.second == 1001000);
+ // 2^64 / 5 = 3689348814741910323, but we lose some precision
+ // starting in the 10th digit to avoid the overflow.
+ result = mulDiv(max32 + 1, max32 + 1, 5);
+ expect(result.first && result.second == 3689348813882916864);
+
+ // Overflow
+ result = mulDiv(max - 1, max - 2, 5);
+ expect(!result.first && result.second == max);
+
+ try
+ {
+ mulDivThrow(max - 1, max - 2, 5);
+ fail();
+ }
+ catch (std::overflow_error const& e)
+ {
+ expect(e.what() == std::string("mulDiv"));
+ }
+ }
+};
+
+BEAST_DEFINE_TESTSUITE(mulDiv, ripple_basics, ripple);
+
+} // namespace test
+} // namespace ripple
diff --git a/src/ripple/core/impl/LoadFeeTrack.cpp b/src/ripple/core/impl/LoadFeeTrack.cpp
index c59fb32fc0..b742dd7075 100644
--- a/src/ripple/core/impl/LoadFeeTrack.cpp
+++ b/src/ripple/core/impl/LoadFeeTrack.cpp
@@ -19,6 +19,7 @@
#include
#include
+#include
#include
#include
#include
@@ -31,7 +32,7 @@ std::uint64_t
LoadFeeTrack::scaleFeeBase (std::uint64_t fee, std::uint64_t baseFee,
std::uint32_t referenceFeeUnits) const
{
- return mulDiv (fee, baseFee, referenceFeeUnits);
+ return mulDivThrow (fee, baseFee, referenceFeeUnits);
}
// Scale using load as well as base rate
@@ -116,7 +117,7 @@ LoadFeeTrack::getJson (std::uint64_t baseFee,
// load_fee = The cost to send a "reference" transaction now,
// in millionths of a Ripple
j[jss::load_fee] = Json::Value::UInt (
- mulDiv (baseFee, std::max (mLocalTxnLoadFee,
+ mulDivThrow(baseFee, std::max(mLocalTxnLoadFee,
mRemoteTxnLoadFee), lftNormalFee));
}
diff --git a/src/ripple/protocol/STAmount.h b/src/ripple/protocol/STAmount.h
index 69fd45fca9..7328a7e26e 100644
--- a/src/ripple/protocol/STAmount.h
+++ b/src/ripple/protocol/STAmount.h
@@ -418,35 +418,6 @@ inline bool isXRP(STAmount const& amount)
return isXRP (amount.issue().currency);
}
-/**
- A utility function to compute (value)*(mul)/(div) while avoiding
- overflow but keeping precision.
-*/
-std::uint64_t
-mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
-
-/**
- A utility function to compute (value)*(mul)/(div) while avoiding
- overflow but keeping precision. Will return the max uint64_t
- value if mulDiv would overflow anyway.
-*/
-std::uint64_t
-mulDivNoThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
-
-template
-void lowestTerms(T1& a, T2& b)
-{
- std::uint64_t x = a, y = b;
- while (y != 0)
- {
- auto t = x % y;
- x = y;
- y = t;
- }
- a /= x;
- b /= x;
-}
-
} // ripple
#endif
diff --git a/src/ripple/protocol/impl/STAmount.cpp b/src/ripple/protocol/impl/STAmount.cpp
index 9b7ab22642..9ea925374c 100644
--- a/src/ripple/protocol/impl/STAmount.cpp
+++ b/src/ripple/protocol/impl/STAmount.cpp
@@ -1275,40 +1275,6 @@ divRound (STAmount const& num, STAmount const& den,
return result;
}
-// compute (value)*(mul)/(div) - avoid overflow but keep precision
-std::uint64_t
-mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
-{
- lowestTerms(value, div);
- lowestTerms(mul, div);
-
- if (value < mul)
- std::swap(value, mul);
- const auto max = std::numeric_limits::max();
- if (value > max / mul)
- {
- value /= div;
- if (value > max / mul)
- Throw ("mulDiv");
- return value * mul;
- }
- return value * mul / div;
-}
-
-// compute (value)*(mul)/(div) - avoid overflow but keep precision
-std::uint64_t
-mulDivNoThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
-{
- try
- {
- return mulDiv(value, mul, div);
- }
- catch (std::overflow_error)
- {
- return std::numeric_limits::max();
- }
-}
-
NetClock::time_point
STAmountCalcSwitchovers::enableUnderflowFixCloseTime ()
{
diff --git a/src/ripple/rpc/impl/TransactionSign.cpp b/src/ripple/rpc/impl/TransactionSign.cpp
index 6e5ca9075c..18317392df 100644
--- a/src/ripple/rpc/impl/TransactionSign.cpp
+++ b/src/ripple/rpc/impl/TransactionSign.cpp
@@ -25,6 +25,7 @@
#include
#include // Validity::Valid
#include
+#include
#include
#include
#include
diff --git a/src/ripple/unity/basics.cpp b/src/ripple/unity/basics.cpp
index 93098ed23c..8ba351a5a5 100644
--- a/src/ripple/unity/basics.cpp
+++ b/src/ripple/unity/basics.cpp
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -36,6 +37,7 @@
#include
#include
+#include
#include
#include
#include