[ox/mc] Finish encoding for variable length integers
This commit is contained in:
parent
9e55e5f63c
commit
17fc48aa26
1
deps/ox/src/ox/mc/CMakeLists.txt
vendored
1
deps/ox/src/ox/mc/CMakeLists.txt
vendored
@ -20,6 +20,7 @@ set_property(
|
|||||||
|
|
||||||
install(
|
install(
|
||||||
FILES
|
FILES
|
||||||
|
intops.hpp
|
||||||
err.hpp
|
err.hpp
|
||||||
mc.hpp
|
mc.hpp
|
||||||
presenceindicator.hpp
|
presenceindicator.hpp
|
||||||
|
90
deps/ox/src/ox/mc/intops.hpp
vendored
Normal file
90
deps/ox/src/ox/mc/intops.hpp
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 - 2018 gtalent2@gmail.com
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ox/std/assert.hpp>
|
||||||
|
#include <ox/std/byteswap.hpp>
|
||||||
|
|
||||||
|
namespace ox::mc {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns highest bit other than possible signed bit.
|
||||||
|
* Bit numbering starts at 0.
|
||||||
|
*/
|
||||||
|
template<typename I>
|
||||||
|
[[nodiscard]] constexpr auto highestBit(I val) noexcept {
|
||||||
|
constexpr auto Bits = sizeof(I) * 8;
|
||||||
|
// find most significant non-sign indicator bit
|
||||||
|
std::size_t highestBit = Bits;
|
||||||
|
// start at one bit lower if signed
|
||||||
|
if constexpr(ox::is_signed<I>) {
|
||||||
|
--highestBit;
|
||||||
|
}
|
||||||
|
for (int i = Bits - 1; i > -1; --i) {
|
||||||
|
const auto bitValue = (val >> i) & 1;
|
||||||
|
if (bitValue) {
|
||||||
|
highestBit = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return highestBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(highestBit(1) == 0);
|
||||||
|
static_assert(highestBit(2) == 1);
|
||||||
|
static_assert(highestBit(4) == 2);
|
||||||
|
static_assert(highestBit(8) == 3);
|
||||||
|
static_assert(highestBit(1 << 31) == 31);
|
||||||
|
static_assert(highestBit(uint64_t(1) << 63) == 63);
|
||||||
|
|
||||||
|
struct McInt {
|
||||||
|
uint8_t data[9];
|
||||||
|
// length of integer in bytes
|
||||||
|
std::size_t length = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename I>
|
||||||
|
McInt encodeInteger(I val) noexcept {
|
||||||
|
McInt out;
|
||||||
|
if (val) {
|
||||||
|
// bits needed to represent number factoring in space possibly
|
||||||
|
// needed for signed bit
|
||||||
|
const auto bits = highestBit(val) + 1 + (ox::is_signed<I> ? 1 : 0);
|
||||||
|
// bytes needed to store value
|
||||||
|
std::size_t bytes = bits / 8 + (bits % 8 != 0);
|
||||||
|
auto bitsAvailable = bytes * 8;
|
||||||
|
// factor in bits needed for bytesIndicator (does not affect bytesIndicator)
|
||||||
|
// bits for integer + bits neded to represent bytes > bits available
|
||||||
|
if (bits + bytes > bitsAvailable && bytes != 9) {
|
||||||
|
++bytes;
|
||||||
|
bitsAvailable = bytes * 8;
|
||||||
|
}
|
||||||
|
const auto bytesIndicator = onMask<uint8_t>(bytes - 1); // << (7 - bytes);
|
||||||
|
|
||||||
|
// move sign bit
|
||||||
|
if constexpr(ox::is_signed<I>) {
|
||||||
|
if (val < 0) {
|
||||||
|
val ^= int64_t(1) << (sizeof(I) * 8 - 1);
|
||||||
|
val |= int64_t(1) << (bitsAvailable - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ensure we are copying from little endian represenstation
|
||||||
|
LittleEndian<I> leVal = val;
|
||||||
|
if (bytes == 9) {
|
||||||
|
out.data[0] = bytesIndicator;
|
||||||
|
*reinterpret_cast<I*>(&out.data[1]) = *reinterpret_cast<I*>(&leVal);
|
||||||
|
} else {
|
||||||
|
*reinterpret_cast<I*>(&out.data[0]) = (leVal << bytes) | bytesIndicator;
|
||||||
|
}
|
||||||
|
out.length = bytes;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1
deps/ox/src/ox/mc/mc.hpp
vendored
1
deps/ox/src/ox/mc/mc.hpp
vendored
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "intops.hpp"
|
||||||
#include "read.hpp"
|
#include "read.hpp"
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
#include "walk.hpp"
|
#include "walk.hpp"
|
||||||
|
2
deps/ox/src/ox/mc/read.hpp
vendored
2
deps/ox/src/ox/mc/read.hpp
vendored
@ -171,7 +171,7 @@ Error MetalClawReader::op(const char*, ox::Vector<T> *val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
int readMC(uint8_t *buff, std::size_t buffLen, T *val) {
|
Error readMC(uint8_t *buff, std::size_t buffLen, T *val) {
|
||||||
MetalClawReader reader(buff, buffLen);
|
MetalClawReader reader(buff, buffLen);
|
||||||
return ioOp(&reader, val);
|
return ioOp(&reader, val);
|
||||||
}
|
}
|
||||||
|
1
deps/ox/src/ox/mc/test/CMakeLists.txt
vendored
1
deps/ox/src/ox/mc/test/CMakeLists.txt
vendored
@ -10,3 +10,4 @@ target_link_libraries(
|
|||||||
|
|
||||||
add_test("Test\\ McTest\\ Writer" McTest MetalClawWriter)
|
add_test("Test\\ McTest\\ Writer" McTest MetalClawWriter)
|
||||||
add_test("Test\\ McTest\\ Reader" McTest MetalClawReader)
|
add_test("Test\\ McTest\\ Reader" McTest MetalClawReader)
|
||||||
|
add_test("Test\\ McTest\\ encodeInteger" McTest encodeInteger)
|
||||||
|
58
deps/ox/src/ox/mc/test/tests.cpp
vendored
58
deps/ox/src/ox/mc/test/tests.cpp
vendored
@ -11,6 +11,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include <ox/mc/mc.hpp>
|
#include <ox/mc/mc.hpp>
|
||||||
#include <ox/ser/ser.hpp>
|
#include <ox/ser/ser.hpp>
|
||||||
#include <ox/std/std.hpp>
|
#include <ox/std/std.hpp>
|
||||||
@ -132,6 +133,63 @@ std::map<std::string, ox::Error(*)()> tests = {
|
|||||||
return OxError(0);
|
return OxError(0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"encodeInteger",
|
||||||
|
[] {
|
||||||
|
using ox::MaxValue;
|
||||||
|
using ox::mc::McInt;
|
||||||
|
using ox::mc::encodeInteger;
|
||||||
|
|
||||||
|
constexpr auto check = [](McInt val, std::vector<uint8_t> &&expected) {
|
||||||
|
if (val.length != expected.size()) {
|
||||||
|
return OxError(1);
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < expected.size(); i++) {
|
||||||
|
if (expected[i] != val.data[i]) {
|
||||||
|
std::cout << static_cast<uint32_t>(val.data[i]) << '\n';
|
||||||
|
return OxError(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OxError(0);
|
||||||
|
};
|
||||||
|
constexpr auto check64 = [](McInt val, auto expected) {
|
||||||
|
if (val.length != 9) {
|
||||||
|
std::cout << "val.length: " << val.length << '\n';
|
||||||
|
return OxError(1);
|
||||||
|
}
|
||||||
|
ox::LittleEndian<decltype(expected)> decoded = *reinterpret_cast<decltype(expected)*>(&val.data[1]);
|
||||||
|
if (expected != decoded) {
|
||||||
|
std::cout << "decoded: " << decoded << ", expected: " << expected << '\n';
|
||||||
|
return OxError(1);
|
||||||
|
}
|
||||||
|
return OxError(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
oxAssert(check(encodeInteger(int64_t(1)), {0b0010}), "Encode 1 fail");
|
||||||
|
oxAssert(check(encodeInteger(int64_t(2)), {0b0100}), "Encode 2 fail");
|
||||||
|
oxAssert(check(encodeInteger(int64_t(3)), {0b0110}), "Encode 3 fail");
|
||||||
|
oxAssert(check(encodeInteger(int64_t(4)), {0b1000}), "Encode 4 fail");
|
||||||
|
oxAssert(check(encodeInteger(int64_t(128)), {0b0001, 0b10}), "Encode 128 fail");
|
||||||
|
oxAssert(check(encodeInteger(int64_t(129)), {0b0101, 0b10}), "Encode 129 fail");
|
||||||
|
oxAssert(check(encodeInteger(int64_t(130)), {0b1001, 0b10}), "Encode 130 fail");
|
||||||
|
oxAssert(check(encodeInteger(int64_t(131)), {0b1101, 0b10}), "Encode 131 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");
|
||||||
|
oxAssert(check(encodeInteger(uint64_t(4)), {0b1000}), "Encode 4 fail");
|
||||||
|
oxAssert(check(encodeInteger(uint64_t(128)), {0b0001, 0b10}), "Encode 128 fail");
|
||||||
|
oxAssert(check(encodeInteger(uint64_t(129)), {0b0101, 0b10}), "Encode 129 fail");
|
||||||
|
oxAssert(check(encodeInteger(uint64_t(130)), {0b1001, 0b10}), "Encode 130 fail");
|
||||||
|
oxAssert(check(encodeInteger(uint64_t(131)), {0b1101, 0b10}), "Encode 131 fail");
|
||||||
|
|
||||||
|
// Signed check needs lambda templates to run correctly without
|
||||||
|
// code deduplication
|
||||||
|
//oxAssert(check64(encodeInteger(MaxValue<int64_t>), MaxValue<int64_t>), "Encode MaxValue<int64_t> fail");
|
||||||
|
oxAssert(check64(encodeInteger(MaxValue<uint64_t>), MaxValue<uint64_t>), "Encode MaxValue<uint64_t> fail");
|
||||||
|
return OxError(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"MetalClawDef",
|
"MetalClawDef",
|
||||||
[] {
|
[] {
|
||||||
|
61
deps/ox/src/ox/mc/write.hpp
vendored
61
deps/ox/src/ox/mc/write.hpp
vendored
@ -10,11 +10,13 @@
|
|||||||
|
|
||||||
#include <ox/ser/optype.hpp>
|
#include <ox/ser/optype.hpp>
|
||||||
#include <ox/ser/types.hpp>
|
#include <ox/ser/types.hpp>
|
||||||
|
#include <ox/std/bitops.hpp>
|
||||||
#include <ox/std/byteswap.hpp>
|
#include <ox/std/byteswap.hpp>
|
||||||
#include <ox/std/string.hpp>
|
#include <ox/std/string.hpp>
|
||||||
#include <ox/std/types.hpp>
|
#include <ox/std/types.hpp>
|
||||||
#include <ox/std/vector.hpp>
|
#include <ox/std/vector.hpp>
|
||||||
|
|
||||||
|
#include "intops.hpp"
|
||||||
#include "err.hpp"
|
#include "err.hpp"
|
||||||
#include "presenceindicator.hpp"
|
#include "presenceindicator.hpp"
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
@ -60,7 +62,7 @@ class MetalClawWriter {
|
|||||||
Error op(const char*, SerStr val);
|
Error op(const char*, SerStr val);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
int op(const char*, T *val);
|
Error op(const char*, T *val);
|
||||||
|
|
||||||
void setTypeInfo(const char *name, int fields);
|
void setTypeInfo(const char *name, int fields);
|
||||||
|
|
||||||
@ -81,7 +83,7 @@ Error MetalClawWriter::op(const char *name, ox::BString<L> *val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
int MetalClawWriter::op(const char*, T *val) {
|
Error MetalClawWriter::op(const char*, T *val) {
|
||||||
int err = 0;
|
int err = 0;
|
||||||
bool fieldSet = false;
|
bool fieldSet = false;
|
||||||
MetalClawWriter writer(m_buff + m_buffIt, m_buffLen - m_buffIt);
|
MetalClawWriter writer(m_buff + m_buffIt, m_buffLen - m_buffIt);
|
||||||
@ -102,24 +104,6 @@ Error MetalClawWriter::op(const char*, ox::Vector<T> *val) {
|
|||||||
return op(nullptr, val->data(), val->size());
|
return op(nullptr, val->data(), val->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename I>
|
|
||||||
Error MetalClawWriter::appendInteger(I val) {
|
|
||||||
Error err = 0;
|
|
||||||
bool fieldSet = false;
|
|
||||||
if (val) {
|
|
||||||
if (m_buffIt + sizeof(I) < m_buffLen) {
|
|
||||||
*reinterpret_cast<LittleEndian<I>*>(&m_buff[m_buffIt]) = val;
|
|
||||||
fieldSet = true;
|
|
||||||
m_buffIt += sizeof(I);
|
|
||||||
} else {
|
|
||||||
err |= MC_BUFFENDED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err |= m_fieldPresence.set(m_field, fieldSet);
|
|
||||||
m_field++;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Error MetalClawWriter::op(const char*, T *val, std::size_t len) {
|
Error MetalClawWriter::op(const char*, T *val, std::size_t len) {
|
||||||
Error err = 0;
|
Error err = 0;
|
||||||
@ -151,6 +135,43 @@ Error MetalClawWriter::op(const char*, T *val, std::size_t len) {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename I>
|
||||||
|
Error MetalClawWriter::appendInteger(I val) {
|
||||||
|
Error err = 0;
|
||||||
|
bool fieldSet = false;
|
||||||
|
if (val) {
|
||||||
|
if (m_buffIt + sizeof(I) < m_buffLen) {
|
||||||
|
LittleEndian<I> leVal = val;
|
||||||
|
mc::encodeInteger(val);
|
||||||
|
// bits needed to represent number factoring in space possibly needed
|
||||||
|
// for signed bit
|
||||||
|
const auto bits = mc::highestBit(val) + (ox::is_signed<I> ? 1 : 0) / 8;
|
||||||
|
// bytes needed to store value
|
||||||
|
std::size_t bytes = bits / 8 + (bits % 8 != 0);
|
||||||
|
const auto bytesIndicator = onMask<uint8_t>(bytes - 1) << (7 - bytes);
|
||||||
|
// factor in bits needed for bytesIndicator (does not affect bytesIndicator)
|
||||||
|
// bits for integer + bits needed to represent bytes > bits available
|
||||||
|
if (bits + bytes > bytes * 8) {
|
||||||
|
++bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes == 9) {
|
||||||
|
m_buff[m_buffIt++] = bytesIndicator;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
*reinterpret_cast<LittleEndian<I>*>(&m_buff[m_buffIt]) = leVal;
|
||||||
|
fieldSet = true;
|
||||||
|
m_buffIt += sizeof(I);
|
||||||
|
} else {
|
||||||
|
err |= MC_BUFFENDED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err |= m_fieldPresence.set(m_field, fieldSet);
|
||||||
|
m_field++;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Error writeMC(uint8_t *buff, std::size_t buffLen, T *val, std::size_t *sizeOut = nullptr) {
|
Error writeMC(uint8_t *buff, std::size_t buffLen, T *val, std::size_t *sizeOut = nullptr) {
|
||||||
MetalClawWriter writer(buff, buffLen);
|
MetalClawWriter writer(buff, buffLen);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user