[ox/mc] Add VLI decode

This commit is contained in:
Gary Talent 2019-03-17 00:04:00 -05:00
parent 341dbf14ae
commit df44abc316
3 changed files with 82 additions and 0 deletions

View File

@ -96,4 +96,53 @@ template<typename I>
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<typename I>
[[nodiscard]] ValErr<I> decodeInteger(uint8_t buff[9], std::size_t buffLen) noexcept {
const auto bytes = countBytes(buff[0]);
if (bytes == 9) {
return {LittleEndian<I>(*reinterpret_cast<I*>(&buff[1])), 0};
} else if (buffLen >= bytes) {
uint64_t decoded = LittleEndian<uint64_t>(*reinterpret_cast<uint64_t*>(&buff[0]));
decoded >>= bytes;
auto out = static_cast<I>(decoded);
// move sign bit
if constexpr(ox::is_signed<I>) {
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<Unsigned<I>*>(&out) |= sign << (Bits<I> - 1);
}
return {out, 0};
}
return {0, OxError(1)};
}
template<typename I>
[[nodiscard]] ValErr<I> decodeInteger(McInt m) noexcept {
return decodeInteger<I>(m.data, 9);
}
}

View File

@ -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)

View File

@ -184,6 +184,7 @@ std::map<std::string, ox::Error(*)()> 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<std::string, ox::Error(*)()> 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<decltype(val)>(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",
[] {