[ox] Fix MC negative int encoding and bump MC version to 2

This commit is contained in:
Gary Talent 2022-03-03 01:25:54 -06:00
parent bd2dd3f000
commit 6cebe52904
9 changed files with 126 additions and 68 deletions

View File

@ -41,7 +41,7 @@ Result<ClawHeader> readClawHeader(const char *buff, std::size_t buffLen) noexcep
buffLen -= s3Size + 1;
ClawHeader hdr;
if (fmt == "M1") {
if (fmt == "M2") {
hdr.fmt = ClawFormat::Metal;
} else if (fmt == "O1") {
hdr.fmt = ClawFormat::Organic;

View File

@ -45,7 +45,6 @@ Error readClaw(const char *buff, std::size_t buffLen, T *val) {
return OxError(Error_ClawTypeMismatch, "Claw Read: Type mismatch");
}
if (header.typeVersion != getModelTypeVersion<T>()) {
oxDebugf("version: {}, {}", header.typeVersion, getModelTypeVersion<T>());
return OxError(Error_ClawTypeVersionMismatch, "Claw Read: Type Version mismatch");
}
switch (header.fmt) {

View File

@ -116,7 +116,7 @@ std::map<std::string_view, ox::Error(*)()> tests = {
{
"ClawHeaderReader2",
[] {
ox::String hdr = "M1;com.drinkingtea.ox.claw.test.Header2;3;";
ox::String hdr = "M2;com.drinkingtea.ox.claw.test.Header2;3;";
auto [ch, err] = ox::readClawHeader(hdr.c_str(), hdr.len() + 1);
oxAssert(err, "Error parsing header");
oxAssert(ch.fmt == ox::ClawFormat::Metal, "Format wrong");

View File

@ -62,7 +62,7 @@ Result<String> writeClawHeader(T *t, ClawFormat fmt) noexcept {
String out;
switch (fmt) {
case ClawFormat::Metal:
out += "M1;";
out += "M2;";
break;
case ClawFormat::Organic:
out += "O1;";

View File

@ -20,3 +20,9 @@ SignalHandler::~SignalHandler() noexcept {
}
}
/*
* 1. ProjectExplorer::fileOpened cannot notify StudioUI that it is being destroyed.
* 2. StudioUI tries to unsubscribe from ProjectExplorer::fileOpened upon its destruction.
* 3. Segfault
*/

View File

@ -8,6 +8,8 @@
#pragma once
#include <ox/std/assert.hpp>
#include <ox/std/def.hpp>
#include <ox/std/defines.hpp>
#include <ox/std/error.hpp>
#include <ox/std/memory.hpp>
@ -36,10 +38,6 @@ struct isError<Error> {
template<class... Args>
class Signal {
private:
template<typename T>
static constexpr void ignoreValue(const T&) {}
protected:
struct BaseSlot {
virtual ~BaseSlot() = default;
@ -85,7 +83,10 @@ class Signal {
}
void cleanup(Signal *signal) noexcept final {
oxIgnoreError(m_receiver->destruction.disconnectSignal(signal));
auto err = m_receiver->destruction.disconnectSignal(signal);
if (err) {
oxErrorf("Signal could not notify receiver that it is being destroyed. Destruction of receiver will cause use-after-free. ({})", toStr(err));
}
}
[[nodiscard]]
@ -196,7 +197,7 @@ Error Signal<Args...>::disconnectObject(const void *receiver) const noexcept {
--i;
}
}
return OxError(0);
return OxError(1, "Signal::disconnectObject: Receiver was not found among this Signal's slots");
}
template<class... Args>
@ -263,7 +264,9 @@ class Signal<Error(Args...)> {
}
void cleanup(Signal *signal) noexcept final {
oxIgnoreError(m_receiver->destruction.disconnectSignal(signal));
auto err = m_receiver->destruction.disconnectSignal(signal);
oxErrorf("{}", toStr(err));
//oxAssert(err, "Signal could not notify receiver that it is being destroyed. Destruction of receiver will cause use-after-free.");
}
[[nodiscard]]
@ -327,6 +330,11 @@ class SignalHandler {
public:
Signal<Error(const SignalHandler*)> destruction;
constexpr SignalHandler() noexcept = default;
SignalHandler(const SignalHandler&) = delete;
SignalHandler(SignalHandler&) = delete;
SignalHandler(SignalHandler&&) = delete;
virtual ~SignalHandler() noexcept;
};
@ -377,7 +385,7 @@ Error Signal<Error(Args...)>::disconnectObject(const void *receiver) const noexc
--i;
}
}
return OxError(0);
return OxError(1, "Signal::disconnectObject: Receiver was not found among this Signal's slots");
}
template<class... Args>

View File

@ -11,6 +11,7 @@
#include <ox/std/assert.hpp>
#include <ox/std/bit.hpp>
#include <ox/std/byteswap.hpp>
#include <ox/std/math.hpp>
#include <ox/std/memops.hpp>
namespace ox::mc {
@ -43,6 +44,9 @@ constexpr std::size_t highestBit(I val) noexcept {
}
static_assert(highestBit(int8_t(0b10000000)) == 0);
static_assert(highestBit(~static_cast<int8_t>(-1)) == 0);
static_assert(highestBit(~static_cast<int8_t>(-2)) == 0);
static_assert(highestBit(~static_cast<int8_t>(-3)) == 1);
static_assert(highestBit(1) == 0);
static_assert(highestBit(2) == 1);
static_assert(highestBit(4) == 2);
@ -60,6 +64,7 @@ template<typename I>
[[nodiscard]]
constexpr McInt encodeInteger(I input) noexcept {
McInt out;
auto inputNegative = is_signed_v<I> && input < 0;
// move input to uint64_t to allow consistent bit manipulation, and to avoid
// overflow concerns
uint64_t val = 0;
@ -67,26 +72,35 @@ constexpr McInt encodeInteger(I input) noexcept {
if (val) {
// bits needed to represent number factoring in space possibly
// needed for signed bit
const auto bits = highestBit(val) + 1 + (is_signed_v<I> ? 1 : 0);
const auto highBit = inputNegative ? (highestBit(~val)) : highestBit(val);
const auto bits = highBit + 1 + (is_signed_v<I> ? 1 : 0);
// bytes needed to store value
std::size_t bytes = bits / 8 + (bits % 8 != 0);
const auto bitsAvailable = bytes * 8; // bits available to integer value
const auto bitsNeeded = bits + bytes;
// factor in bits needed for bytesIndicator (does not affect bytesIndicator)
// bits for integer + bits neded to represent bytes > bits available
// bits for integer + bits needed to represent bytes > bits available
if (bitsNeeded > bitsAvailable && bytes != 9) {
++bytes;
}
const auto bytesIndicator = onMask<uint8_t>(bytes - 1);
// ensure we are copying from little endian represenstation
LittleEndian<I> leVal = static_cast<I>(val);
// ensure we are copying from little endian representation
LittleEndian<uint64_t> leVal = val;
if (inputNegative) {
leVal |= static_cast<uint64_t>(1 << (bitsNeeded - 1));
}
if (bytes == 9) {
out.data[0] = bytesIndicator;
ox_memcpy(&out.data[1], &leVal, sizeof(I));
if (inputNegative) {
out.data[1] |= 0b1000'0000;
}
} else {
const auto valBits = bytes * 8;
uint64_t negBit = inputNegative ? 1 : 0;
auto intermediate =
static_cast<uint64_t>(leVal.raw()) << bytes |
static_cast<uint64_t>(leVal.raw() | (negBit << (valBits - 1))) << bytes |
static_cast<uint64_t>(bytesIndicator);
ox_memcpy(out.data, &intermediate, sizeof(intermediate));
}
@ -106,15 +120,15 @@ static constexpr std::size_t countBytes(uint8_t b) noexcept {
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);
static_assert(countBytes(0b0000'0000) == 1);
static_assert(countBytes(0b0000'0001) == 2);
static_assert(countBytes(0b0000'0011) == 3);
static_assert(countBytes(0b0000'0111) == 4);
static_assert(countBytes(0b0000'1111) == 5);
static_assert(countBytes(0b0001'1111) == 6);
static_assert(countBytes(0b0011'1111) == 7);
static_assert(countBytes(0b0111'1111) == 8);
static_assert(countBytes(0b1111'1111) == 9);
template<typename I>
Result<I> decodeInteger(const uint8_t buff[9], std::size_t buffLen, std::size_t *bytesRead) noexcept {
@ -122,26 +136,37 @@ Result<I> decodeInteger(const uint8_t buff[9], std::size_t buffLen, std::size_t
if (bytes == 9) {
*bytesRead = bytes;
I out = 0;
memcpy(&out, &buff[1], sizeof(I));
ox_memcpy(&out, &buff[1], sizeof(I));
return static_cast<I>(LittleEndian<I>(out));
} else if (buffLen >= bytes) {
*bytesRead = bytes;
uint64_t decoded = 0;
memcpy(&decoded, &buff[0], bytes);
ox_memcpy(&decoded, &buff[0], bytes);
decoded >>= bytes;
auto out = static_cast<I>(decoded);
// move sign bit
if constexpr(is_signed_v<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
decoded |= sign << (Bits<I> - 1);
memcpy(&out, &decoded, sizeof(out));
const auto negBit = bytes * 8 - bytes - 1;
// move sign
const auto negative = (decoded >> negBit) == 1;
if (negative) {
// fill in all bits between encoded sign and real sign with 1s
// split it up because the 32-bit ARM can't shift more than 32 bits
uint32_t *d = reinterpret_cast<uint32_t*>(&decoded);
auto bit = negBit;
for (; bit < ox::min<std::size_t>(Bits<I>, 32); ++bit) {
d[0] |= 1 << bit;
}
for (; bit < Bits<I>; ++bit) {
d[1] |= 1 << bit;
}
}
I out = 0;
ox_memcpy(&out, &decoded, sizeof(out));
return out;
} else {
auto out = static_cast<I>(decoded);
return out;
}
return out;
}
return OxError(1);
}

View File

@ -196,23 +196,23 @@ std::map<std::string, ox::Error(*)()> tests = {
return OxError(0);
};
oxAssert(check(encodeInteger(int64_t(1)), {0b00000010}), "Encode 1 fail");
oxAssert(check(encodeInteger(int64_t(2)), {0b00000100}), "Encode 2 fail");
oxAssert(check(encodeInteger(int64_t(3)), {0b00000110}), "Encode 3 fail");
oxAssert(check(encodeInteger(int64_t(4)), {0b00001000}), "Encode 4 fail");
oxAssert(check(encodeInteger(int64_t(128)), {0b00000001, 0b10}), "Encode 128 fail");
oxAssert(check(encodeInteger(int64_t(129)), {0b00000101, 0b10}), "Encode 129 fail");
oxAssert(check(encodeInteger(int64_t(130)), {0b00001001, 0b10}), "Encode 130 fail");
oxAssert(check(encodeInteger(int64_t(131)), {0b00001101, 0b10}), "Encode 131 fail");
oxAssert(check(encodeInteger(int64_t(1)), {0b000'0001'0}), "Encode 1 fail");
oxAssert(check(encodeInteger(int64_t(2)), {0b000'0010'0}), "Encode 2 fail");
oxAssert(check(encodeInteger(int64_t(3)), {0b000'0011'0}), "Encode 3 fail");
oxAssert(check(encodeInteger(int64_t(4)), {0b000'0100'0}), "Encode 4 fail");
oxAssert(check(encodeInteger(int64_t(128)), {0b00'0000'01, 0b10}), "Encode 128 fail");
oxAssert(check(encodeInteger(int64_t(129)), {0b00'0001'01, 0b10}), "Encode 129 fail");
oxAssert(check(encodeInteger(int64_t(130)), {0b00'0010'01, 0b10}), "Encode 130 fail");
oxAssert(check(encodeInteger(int64_t(131)), {0b00'0011'01, 0b10}), "Encode 131 fail");
oxAssert(check(encodeInteger(int64_t(-1)), {255, 255, 255, 255, 255, 255, 255, 255, 255}), "Encode -1 fail");
oxAssert(check(encodeInteger(int64_t(-2)), {255, 254, 255, 255, 255, 255, 255, 255, 255}), "Encode -2 fail");
oxAssert(check(encodeInteger(int64_t(-3)), {255, 253, 255, 255, 255, 255, 255, 255, 255}), "Encode -3 fail");
oxAssert(check(encodeInteger(int64_t(-4)), {255, 252, 255, 255, 255, 255, 255, 255, 255}), "Encode -4 fail");
oxAssert(check(encodeInteger(int64_t(-128)), {255, 128, 255, 255, 255, 255, 255, 255, 255}), "Encode -128 fail");
oxAssert(check(encodeInteger(int64_t(-129)), {255, 127, 255, 255, 255, 255, 255, 255, 255}), "Encode -129 fail");
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( int64_t(-1)), {0b111'1111'0}), "Encode -1 fail");
oxAssert(check(encodeInteger( int64_t(-2)), {0b111'1110'0}), "Encode -2 fail");
oxAssert(check(encodeInteger( int64_t(-3)), {0b111'1101'0}), "Encode -3 fail");
oxAssert(check(encodeInteger( int64_t(-4)), {0b111'1100'0}), "Encode -4 fail");
oxAssert(check(encodeInteger(int64_t(-128)), {0b00'0000'01, 0b11'1111'10}), "Encode -128 fail");
oxAssert(check(encodeInteger(int64_t(-129)), {0b11'1111'01, 0b11'1111'01}), "Encode -129 fail");
oxAssert(check(encodeInteger(int64_t(-130)), {0b11'1110'01, 0b11'1111'01}), "Encode -130 fail");
oxAssert(check(encodeInteger(int64_t(-131)), {0b11'1101'01, 0b11'1111'01}), "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");
@ -250,11 +250,17 @@ std::map<std::string, ox::Error(*)()> tests = {
};
oxAssert(check(uint32_t(14)), "Decode of 14 failed.");
oxAssert(check(int64_t(-1)), "Decode of -1 failed.");
oxAssert(check(int64_t(-2)), "Decode of -2 failed.");
oxAssert(check(int64_t(-127)), "Decode of -127 failed.");
oxAssert(check(int64_t(-128)), "Decode of -128 failed.");
oxAssert(check(int64_t(-129)), "Decode of -129 failed.");
oxAssert(check(int64_t(-129000)), "Decode of -129000 failed.");
oxAssert(check(int64_t(1)), "Decode of 1 failed.");
oxAssert(check(int64_t(2)), "Decode of 2 failed.");
oxAssert(check(int64_t(42)), "Decode of 42 failed.");
oxAssert(check(int64_t(130)), "Decode of 130 failed.");
oxAssert(check(int64_t(131)), "Decode of 131 failed.");
oxAssert(check(int64_t(131000)), "Decode of 131000 failed.");
oxAssert(check(uint64_t(1)), "Decode of 1 failed.");
oxAssert(check(uint64_t(2)), "Decode of 2 failed.");
oxAssert(check(uint64_t(42)), "Decode of 42 failed.");

View File

@ -8,7 +8,9 @@
#pragma once
#include "defines.hpp"
#include "types.hpp"
#include "typetraits.hpp"
#if __has_include(<cstring>)
#include<cstring>
@ -27,29 +29,41 @@ void *memset(void *ptr, int val, std::size_t size);
int ox_memcmp(const void *ptr1, const void *ptr2, std::size_t size) noexcept;
constexpr void *ox_memcpy(void *dest, const void *src, std::size_t size) noexcept {
auto srcBuf = static_cast<const char*>(src);
auto dstBuf = static_cast<char*>(dest);
for (std::size_t i = 0; i < size; ++i) {
dstBuf[i] = static_cast<char>(srcBuf[i]);
if (std::is_constant_evaluated() || !ox::defines::UseStdLib) {
auto srcBuf = static_cast<const char *>(src);
auto dstBuf = static_cast<char *>(dest);
for (std::size_t i = 0; i < size; ++i) {
dstBuf[i] = static_cast<char>(srcBuf[i]);
}
return dest;
} else {
return memcpy(dest, src, size);
}
return dest;
}
constexpr void *ox_memmove(void *dest, const void *src, std::size_t size) noexcept {
auto srcBuf = static_cast<const char*>(src);
auto dstBuf = static_cast<char*>(dest);
for (std::size_t i = 0; i < size; ++i) {
dstBuf[i] = static_cast<char>(srcBuf[i]);
if (std::is_constant_evaluated() || !ox::defines::UseStdLib) {
auto srcBuf = static_cast<const char *>(src);
auto dstBuf = static_cast<char *>(dest);
for (std::size_t i = 0; i < size; ++i) {
dstBuf[i] = static_cast<char>(srcBuf[i]);
}
return dest;
} else {
return memmove(dest, src, size);
}
return dest;
}
constexpr void *ox_memset(void *ptr, int val, std::size_t size) noexcept {
auto buf = static_cast<uint8_t*>(ptr);
for (std::size_t i = 0; i < size; ++i) {
buf[i] = static_cast<uint8_t>(val);
if (std::is_constant_evaluated() || !ox::defines::UseStdLib) {
auto buf = static_cast<uint8_t *>(ptr);
for (std::size_t i = 0; i < size; ++i) {
buf[i] = static_cast<uint8_t>(val);
}
return ptr;
} else {
return memset(ptr, val, size);
}
return ptr;
}
namespace ox {