From 6b759fb0921c964f72d84d6eff4a4a115b4d64dc Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 7 Jun 2018 12:41:05 +1200 Subject: [PATCH] ConvertBits() - convert from one power-of-2 number base to another. Function extracted from upstream: PR bitcoin/bitcoin#11167 Commit c091b99379b97cb314c9fa123beabdbc324cf7a4 --- src/Makefile.test.include | 1 + src/test/convertbits_tests.cpp | 52 ++++++++++++++++++++++++++++++++++ src/utilstrencodings.h | 24 ++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 src/test/convertbits_tests.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 6aa2b1cbe..bbedd99ae 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -61,6 +61,7 @@ BITCOIN_TESTS =\ test/Checkpoints_tests.cpp \ test/coins_tests.cpp \ test/compress_tests.cpp \ + test/convertbits_tests.cpp \ test/crypto_tests.cpp \ test/DoS_tests.cpp \ test/equihash_tests.cpp \ diff --git a/src/test/convertbits_tests.cpp b/src/test/convertbits_tests.cpp new file mode 100644 index 000000000..3b288dba0 --- /dev/null +++ b/src/test/convertbits_tests.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2018 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include + +#include + +BOOST_FIXTURE_TEST_SUITE(convertbits_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(convertbits_deterministic) +{ + for (size_t i = 0; i < 256; i++) { + std::vector input(32, i); + std::vector data; + std::vector output; + ConvertBits<8, 5, true>(data, input.begin(), input.end()); + ConvertBits<5, 8, false>(output, data.begin(), data.end()); + BOOST_CHECK_EQUAL(data.size(), 52); + BOOST_CHECK_EQUAL(output.size(), 32); + BOOST_CHECK(input == output); + } + + for (size_t i = 0; i < 256; i++) { + std::vector input(43, i); + std::vector data; + std::vector output; + ConvertBits<8, 5, true>(data, input.begin(), input.end()); + ConvertBits<5, 8, false>(output, data.begin(), data.end()); + BOOST_CHECK_EQUAL(data.size(), 69); + BOOST_CHECK_EQUAL(output.size(), 43); + BOOST_CHECK(input == output); + } +} + +BOOST_AUTO_TEST_CASE(convertbits_random) +{ + for (size_t i = 0; i < 1000; i++) { + auto input = libzcash::random_uint256(); + std::vector data; + std::vector output; + ConvertBits<8, 5, true>(data, input.begin(), input.end()); + ConvertBits<5, 8, false>(output, data.begin(), data.end()); + BOOST_CHECK_EQUAL(data.size(), 52); + BOOST_CHECK_EQUAL(output.size(), 32); + BOOST_CHECK(input == uint256(output)); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 2375d0c4f..a5d0e3c8a 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -133,4 +133,28 @@ bool TimingResistantEqual(const T& a, const T& b) */ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); +/** Convert from one power-of-2 number base to another. */ +template +bool ConvertBits(O& out, I it, I end) { + size_t acc = 0; + size_t bits = 0; + constexpr size_t maxv = (1 << tobits) - 1; + constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1; + while (it != end) { + acc = ((acc << frombits) | *it) & max_acc; + bits += frombits; + while (bits >= tobits) { + bits -= tobits; + out.push_back((acc >> bits) & maxv); + } + ++it; + } + if (pad) { + if (bits) out.push_back((acc << (tobits - bits)) & maxv); + } else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { + return false; + } + return true; +} + #endif // BITCOIN_UTILSTRENCODINGS_H