From d0b85b69f36ffae7dcf687300bff1e7dee0fbb26 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Wed, 15 Sep 2021 01:10:51 -0600 Subject: [PATCH] add ParseArbitraryInt() for diversifier index --- src/test/util_tests.cpp | 103 +++++++++++++++++++++++++++++++++++++++ src/utilstrencodings.cpp | 22 +++++++++ src/utilstrencodings.h | 5 ++ 3 files changed, 130 insertions(+) diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 0e75189db..1b0b0d360 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -492,4 +492,107 @@ BOOST_AUTO_TEST_CASE(test_ParseFixedPoint) BOOST_CHECK(!ParseFixedPoint("1.", 8, &amount)); } +BOOST_AUTO_TEST_CASE(test_ParseArbitraryInt) +{ + // Negation not allowed. + BOOST_CHECK(!ParseArbitraryInt("-1")); + + // no legal digits + BOOST_CHECK(!ParseArbitraryInt("")); + BOOST_CHECK(!ParseArbitraryInt(" ")); + + // Hex not allowed (only decimal). + BOOST_CHECK(!ParseArbitraryInt("ab")); + BOOST_CHECK(!ParseArbitraryInt("0xab")); + + // Decimal point not allowed. + BOOST_CHECK(!ParseArbitraryInt("1.")); + + std::optional> v; + + // simple success case + v = ParseArbitraryInt("1"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 1); + BOOST_CHECK_EQUAL((*v)[0], 1); + + // Leading and trailing whitespace (spaces and tabs) is allowed. + v = ParseArbitraryInt(" 1"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 1); + BOOST_CHECK_EQUAL((*v)[0], 1); + v = ParseArbitraryInt(" 1"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 1); + BOOST_CHECK_EQUAL((*v)[0], 1); + v = ParseArbitraryInt(" \t1 "); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 1); + BOOST_CHECK_EQUAL((*v)[0], 1); + + // Leading zeros have no effect, does not mean octal + v = ParseArbitraryInt("010\t"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 1); + BOOST_CHECK_EQUAL((*v)[0], 10); + + v = ParseArbitraryInt(" 255\t"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 1); + BOOST_CHECK_EQUAL((*v)[0], 255); + + v = ParseArbitraryInt("\t 256"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 2); + BOOST_CHECK_EQUAL((*v)[0], 0); + BOOST_CHECK_EQUAL((*v)[1], 1); + + v = ParseArbitraryInt("257 \t"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 2); + BOOST_CHECK_EQUAL((*v)[0], 1); + BOOST_CHECK_EQUAL((*v)[1], 1); + + v = ParseArbitraryInt("65535"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 2); + BOOST_CHECK_EQUAL((*v)[0], 255); + BOOST_CHECK_EQUAL((*v)[1], 255); + + v = ParseArbitraryInt("65536"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 3); + BOOST_CHECK_EQUAL((*v)[0], 0); + BOOST_CHECK_EQUAL((*v)[1], 0); + BOOST_CHECK_EQUAL((*v)[2], 1); + + // This decimal string came from: + // $ echo 16i 102030405060708090A0B0C0D0E0F0F1F2F3F4F5F6F7 p | dc + v = ParseArbitraryInt("6033354224708459019450009057293028077350189222196983"); + BOOST_CHECK(v.has_value()); + BOOST_CHECK_EQUAL(v->size(), 22); + BOOST_CHECK_EQUAL((*v)[0], 0xf7); + BOOST_CHECK_EQUAL((*v)[1], 0xf6); + BOOST_CHECK_EQUAL((*v)[2], 0xf5); + BOOST_CHECK_EQUAL((*v)[3], 0xf4); + BOOST_CHECK_EQUAL((*v)[4], 0xf3); + BOOST_CHECK_EQUAL((*v)[5], 0xf2); + BOOST_CHECK_EQUAL((*v)[6], 0xf1); + BOOST_CHECK_EQUAL((*v)[7], 0xf0); + BOOST_CHECK_EQUAL((*v)[8], 0xe0); + BOOST_CHECK_EQUAL((*v)[9], 0xd0); + BOOST_CHECK_EQUAL((*v)[10], 0xc0); + BOOST_CHECK_EQUAL((*v)[11], 0xb0); + BOOST_CHECK_EQUAL((*v)[12], 0xa0); + BOOST_CHECK_EQUAL((*v)[13], 0x90); + BOOST_CHECK_EQUAL((*v)[14], 0x80); + BOOST_CHECK_EQUAL((*v)[15], 0x70); + BOOST_CHECK_EQUAL((*v)[16], 0x60); + BOOST_CHECK_EQUAL((*v)[17], 0x50); + BOOST_CHECK_EQUAL((*v)[18], 0x40); + BOOST_CHECK_EQUAL((*v)[19], 0x30); + BOOST_CHECK_EQUAL((*v)[20], 0x20); + BOOST_CHECK_EQUAL((*v)[21], 0x10); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index 0b6332a63..5665d4c9d 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -506,3 +506,25 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out) return true; } +/// Parse the decimal string into a little-endian byte vector. +std::optional> ParseArbitraryInt(const std::string& num_string) +{ + std::vector result; + const size_t start = num_string.find_first_not_of(WHITESPACE); + if (start == std::string::npos) return std::nullopt; + const size_t end = num_string.find_last_not_of(WHITESPACE); + assert(end != std::string::npos); + for (char c : num_string.substr(start, end-start+1)) { + if (c < '0' || c > '9') { + return std::nullopt; + } + uint16_t v = c - '0'; + for (auto& r : result) { + v += r * 10; + r = v & 0xFF; // store low byte of the value + v >>= 8; // carry to the next result position + } + if (v > 0) result.push_back(v); + } + return result; +} diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index d756db70c..04073db6f 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -12,6 +12,9 @@ #include #include #include +#include + +constexpr char WHITESPACE[] = " \t"; #define BEGIN(a) ((char*)&(a)) #define END(a) ((char*)&((&(a))[1])) @@ -164,4 +167,6 @@ bool ConvertBits(const O& outfn, I it, I end) { return true; } +std::optional> ParseArbitraryInt(const std::string& s); + #endif // BITCOIN_UTILSTRENCODINGS_H