From df44abc316a054819e8748428b001a0f821c64f6 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Sun, 17 Mar 2019 00:04:00 -0500 Subject: [PATCH] [ox/mc] Add VLI decode --- deps/ox/src/ox/mc/intops.hpp | 49 +++++++++++++++++++++++++++ deps/ox/src/ox/mc/test/CMakeLists.txt | 1 + deps/ox/src/ox/mc/test/tests.cpp | 32 +++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/deps/ox/src/ox/mc/intops.hpp b/deps/ox/src/ox/mc/intops.hpp index 9c068549..1762e8d3 100644 --- a/deps/ox/src/ox/mc/intops.hpp +++ b/deps/ox/src/ox/mc/intops.hpp @@ -96,4 +96,53 @@ template return out; } +/** + * Returns the number of bytes indicated by the bytes indicator of a variable + * length integer. + */ +[[nodiscard]] static constexpr std::size_t countBytes(uint8_t b) noexcept { + std::size_t i = 0; + for (; (b >> i) & 1; i++); + return i + 1; +} + +static_assert(countBytes(0b00000000) == 1); +static_assert(countBytes(0b00000001) == 2); +static_assert(countBytes(0b00000011) == 3); +static_assert(countBytes(0b00000111) == 4); +static_assert(countBytes(0b00001111) == 5); +static_assert(countBytes(0b00011111) == 6); +static_assert(countBytes(0b00111111) == 7); +static_assert(countBytes(0b01111111) == 8); +static_assert(countBytes(0b11111111) == 9); + +template +[[nodiscard]] ValErr decodeInteger(uint8_t buff[9], std::size_t buffLen) noexcept { + const auto bytes = countBytes(buff[0]); + if (bytes == 9) { + return {LittleEndian(*reinterpret_cast(&buff[1])), 0}; + } else if (buffLen >= bytes) { + uint64_t decoded = LittleEndian(*reinterpret_cast(&buff[0])); + decoded >>= bytes; + auto out = static_cast(decoded); + // move sign bit + if constexpr(ox::is_signed) { + const auto valBits = bytes << 3; + // get sign + uint64_t sign = decoded >> (valBits - 1); + // remove sign + decoded &= uint64_t(~0) ^ (uint64_t(1) << valBits); + // set sign + *reinterpret_cast*>(&out) |= sign << (Bits - 1); + } + return {out, 0}; + } + return {0, OxError(1)}; +} + +template +[[nodiscard]] ValErr decodeInteger(McInt m) noexcept { + return decodeInteger(m.data, 9); +} + } diff --git a/deps/ox/src/ox/mc/test/CMakeLists.txt b/deps/ox/src/ox/mc/test/CMakeLists.txt index a3c66141..43a8f3b5 100644 --- a/deps/ox/src/ox/mc/test/CMakeLists.txt +++ b/deps/ox/src/ox/mc/test/CMakeLists.txt @@ -11,3 +11,4 @@ target_link_libraries( add_test("Test\\ McTest\\ Writer" McTest MetalClawWriter) add_test("Test\\ McTest\\ Reader" McTest MetalClawReader) add_test("Test\\ McTest\\ encodeInteger" McTest encodeInteger) +add_test("Test\\ McTest\\ decodeInteger" McTest decodeInteger) diff --git a/deps/ox/src/ox/mc/test/tests.cpp b/deps/ox/src/ox/mc/test/tests.cpp index c75800f6..eaba734e 100644 --- a/deps/ox/src/ox/mc/test/tests.cpp +++ b/deps/ox/src/ox/mc/test/tests.cpp @@ -184,6 +184,7 @@ std::map tests = { oxAssert(check(encodeInteger(int64_t(-130)), {255, 126, 255, 255, 255, 255, 255, 255, 255}), "Encode -130 fail"); oxAssert(check(encodeInteger(int64_t(-131)), {255, 125, 255, 255, 255, 255, 255, 255, 255}), "Encode -131 fail"); + oxAssert(check(encodeInteger(uint32_t(0xffffffff)), {0b11101111, 255, 255, 255, 0b00011111}), "Encode 0xffffffff fail"); oxAssert(check(encodeInteger(uint64_t(1)), {0b0010}), "Encode 1 fail"); oxAssert(check(encodeInteger(uint64_t(2)), {0b0100}), "Encode 2 fail"); oxAssert(check(encodeInteger(uint64_t(3)), {0b0110}), "Encode 3 fail"); @@ -200,6 +201,37 @@ std::map tests = { return OxError(0); } }, + { + "decodeInteger", + [] { + using ox::MaxValue; + using ox::mc::McInt; + using ox::mc::encodeInteger; + using ox::mc::decodeInteger; + + constexpr auto check = [](auto val) { + auto result = decodeInteger(encodeInteger(val)); + oxReturnError(result.error); + if (result.value != val) { + std::cout << "Bad value: " << result.value << ", expected: " << val << '\n'; + } + return OxError(result.value != val); + }; + oxAssert(check(int64_t(1)), "Decode of 1 failed."); + oxAssert(check(int64_t(2)), "Decode of 2 failed."); + oxAssert(check(int64_t(130)), "Decode of 130 failed."); + oxAssert(check(int64_t(131)), "Decode of 131 failed."); + oxAssert(check(uint64_t(1)), "Decode of 1 failed."); + oxAssert(check(uint64_t(2)), "Decode of 2 failed."); + oxAssert(check(uint64_t(130)), "Decode of 130 failed."); + oxAssert(check(uint64_t(131)), "Decode of 131 failed."); + oxAssert(check(0xffffffff), "Decode of 0xffffffff failed."); + oxAssert(check(0xffffffffffff), "Decode of 0xffffffffffff failed."); + oxAssert(check(0xffffffffffffffff), "Decode of U64 max failed."); + + return OxError(0); + } + }, { "MetalClawDef", [] {