[ox] Correct bad bit_cast uses and improve constexpr-ness

This commit is contained in:
Gary Talent 2021-11-28 21:03:29 -06:00
parent 22f08f83c5
commit 1f24912ddd
35 changed files with 247 additions and 214 deletions

View File

@ -34,7 +34,7 @@ if(NOT OX_BUILD_EXEC OR NOT OX_USE_STDLIB)
set(OX_RUN_TESTS OFF)
endif()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

View File

@ -41,13 +41,13 @@ Error readClaw(const char *buff, std::size_t buffLen, T *val) {
switch (header.fmt) {
case ClawFormat::Metal:
{
MetalClawReader reader(bit_cast<uint8_t*>(header.data), buffLen);
MetalClawReader reader(reinterpret_cast<const uint8_t*>(header.data), buffLen);
return model(&reader, val);
}
case ClawFormat::Organic:
{
#ifdef OX_USE_STDLIB
OrganicClawReader reader(bit_cast<uint8_t*>(header.data), buffLen);
OrganicClawReader reader(header.data, buffLen);
return model(&reader, val);
#else
break;

View File

@ -50,20 +50,18 @@ struct TestStruct {
int32_t Int7 = 0;
int32_t Int8 = 0;
TestUnion Union;
char *CString = nullptr;
ox::BString<32> String = "";
uint32_t List[4] = {0, 0, 0, 0};
TestStructNest EmptyStruct;
TestStructNest Struct;
~TestStruct() {
delete[] CString;
}
};
template<typename T>
ox::Error model(T *io, TestUnion *obj) {
constexpr ox::Error model(T *io, TestUnion *obj) {
io->template setTypeInfo<TestUnion>();
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
@ -72,7 +70,7 @@ ox::Error model(T *io, TestUnion *obj) {
}
template<typename T>
ox::Error model(T *io, TestStructNest *obj) {
constexpr ox::Error model(T *io, TestStructNest *obj) {
io->template setTypeInfo<TestStructNest>();
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
@ -81,7 +79,7 @@ ox::Error model(T *io, TestStructNest *obj) {
}
template<typename T>
ox::Error model(T *io, TestStruct *obj) {
constexpr ox::Error model(T *io, TestStruct *obj) {
io->template setTypeInfo<TestStruct>();
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
@ -94,7 +92,6 @@ ox::Error model(T *io, TestStruct *obj) {
oxReturnError(io->field("Int7", &obj->Int7));
oxReturnError(io->field("Int8", &obj->Int8));
oxReturnError(io->field("Union", ox::UnionView{&obj->Union, 1}));
oxReturnError(io->field("CString", ox::SerStr(&obj->CString)));
oxReturnError(io->field("String", &obj->String));
oxReturnError(io->field("List", obj->List, 4));
oxReturnError(io->field("EmptyStruct", &obj->EmptyStruct));
@ -146,8 +143,6 @@ std::map<std::string_view, ox::Error(*)()> tests = {
testIn.Int = 42;
testIn.Union.Int = 42;
testIn.String = "Test String 1";
testIn.CString = new char[ox_strlen("c-string") + 1];
ox_strcpy(testIn.CString, "c-string");
testIn.List[0] = 1;
testIn.List[1] = 2;
testIn.List[2] = 3;
@ -171,7 +166,6 @@ std::map<std::string_view, ox::Error(*)()> tests = {
oxAssert(testIn.Int6 == testOut.Int6, "Int6 value mismatch");
oxAssert(testIn.Int7 == testOut.Int7, "Int7 value mismatch");
oxAssert(testIn.Int8 == testOut.Int8, "Int8 value mismatch");
oxAssert(ox_strcmp(testIn.CString, testOut.CString) == 0, "CString value mismatch");
oxAssert(testIn.Union.Int == testOut.Union.Int, "Union.Int value mismatch");
oxAssert(testIn.String == testOut.String, "String value mismatch");
oxAssert(testIn.List[0] == testOut.List[0], "List[0] value mismatch");

View File

@ -11,6 +11,7 @@
#include <ox/std/defines.hpp>
#include <ox/std/error.hpp>
#include <ox/std/memory.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/vector.hpp>
namespace ox {
@ -44,7 +45,8 @@ class Signal {
virtual ~BaseSlot() = default;
virtual void call(Args...) = 0;
virtual void cleanup(Signal*) noexcept {}
virtual const void *receiver() noexcept { return nullptr; }
[[nodiscard]]
virtual const void *receiver() const noexcept { return nullptr; }
};
template<typename F>
@ -86,7 +88,8 @@ class Signal {
oxIgnoreError(m_receiver->destruction.disconnectSignal(signal));
}
const void *receiver() noexcept final {
[[nodiscard]]
const void *receiver() const noexcept final {
return m_receiver;
}
};
@ -112,7 +115,8 @@ class Signal {
void cleanup(Signal*) noexcept final {
}
const void *receiver() noexcept final {
[[nodiscard]]
const void *receiver() const noexcept final {
return m_receiver;
}
};
@ -228,7 +232,8 @@ class Signal<Error(Args...)> {
virtual ~BaseSlot() = default;
virtual Error call(Args...) noexcept = 0;
virtual void cleanup(Signal*) noexcept {}
virtual const void *receiver() noexcept { return nullptr; }
[[nodiscard]]
virtual const void *receiver() const noexcept { return nullptr; }
};
struct FunctionSlot: public BaseSlot {
@ -261,7 +266,8 @@ class Signal<Error(Args...)> {
oxIgnoreError(m_receiver->destruction.disconnectSignal(signal));
}
const void *receiver() noexcept final {
[[nodiscard]]
const void *receiver() const noexcept final {
return m_receiver;
}
};
@ -283,7 +289,8 @@ class Signal<Error(Args...)> {
void cleanup(Signal*) noexcept final {
}
const void *receiver() noexcept final {
[[nodiscard]]
const void *receiver() const noexcept final {
return m_receiver;
}
};

View File

@ -12,11 +12,6 @@
namespace ox {
FileAddress::FileAddress() noexcept {
m_data.inode = 0;
m_type = FileAddressType::Inode;
}
FileAddress::FileAddress(const FileAddress &other) noexcept {
operator=(other);
}
@ -50,6 +45,9 @@ FileAddress::~FileAddress() noexcept {
}
FileAddress &FileAddress::operator=(const FileAddress &other) noexcept {
if (this == &other) {
return *this;
}
cleanup();
m_type = other.m_type;
switch (m_type) {
@ -71,9 +69,26 @@ FileAddress &FileAddress::operator=(const FileAddress &other) noexcept {
}
FileAddress &FileAddress::operator=(FileAddress &&other) noexcept {
if (this == &other) {
return *this;
}
cleanup();
m_type = other.m_type;
memcpy(this, &other, sizeof(*this));
switch (m_type) {
case FileAddressType::Path:
{
auto strSize = ox_strlen(other.m_data.path) + 1;
m_data.path = new char[strSize];
ox_memcpy(m_data.path, other.m_data.path, strSize);
break;
}
case FileAddressType::ConstPath:
case FileAddressType::Inode:
m_data = other.m_data;
break;
case FileAddressType::None:
break;
}
other.clear();
return *this;
}
@ -86,10 +101,8 @@ void FileAddress::cleanup() noexcept {
}
void FileAddress::clear() noexcept {
if (m_type == FileAddressType::Path) {
m_data.path = nullptr;
m_type = FileAddressType::None;
}
m_data.path = nullptr;
m_type = FileAddressType::None;
}
}

View File

@ -9,6 +9,7 @@
#pragma once
#include <ox/std/std.hpp>
#include <ox/model/typenamecatcher.hpp>
#include <ox/model/types.hpp>
namespace ox {
@ -32,7 +33,7 @@ class FileAddress {
union Data {
static constexpr auto TypeName = "net.drinkingtea.ox.FileAddress.Data";
static constexpr auto Fields = 3;
char *path;
char *path = nullptr;
const char *constPath;
uint64_t inode;
};
@ -42,7 +43,10 @@ class FileAddress {
Data m_data;
public:
FileAddress() noexcept;
constexpr FileAddress() noexcept {
m_data.inode = 0;
m_type = FileAddressType::Inode;
}
FileAddress(const FileAddress &other) noexcept;
@ -93,7 +97,7 @@ class FileAddress {
}
}
constexpr operator bool() const noexcept {
explicit constexpr operator bool() const noexcept {
return m_type != FileAddressType::None;
}
@ -110,6 +114,16 @@ class FileAddress {
};
template<>
constexpr const char *getModelTypeName<FileAddress::Data>() noexcept {
return FileAddress::Data::TypeName;
}
template<>
constexpr const char *getModelTypeName<FileAddress>() noexcept {
return FileAddress::TypeName;
}
template<typename T>
constexpr Error model(T *io, FileAddress::Data *obj) noexcept {
io->template setTypeInfo<FileAddress::Data>();
@ -122,7 +136,9 @@ constexpr Error model(T *io, FileAddress::Data *obj) noexcept {
template<typename T>
constexpr Error model(T *io, FileAddress *fa) noexcept {
io->template setTypeInfo<FileAddress>();
oxReturnError(io->field("type", bit_cast<int8_t*>(&fa->m_type)));
auto type = static_cast<int8_t>(fa->m_type);
oxReturnError(io->field("type", &type));
fa->m_type = static_cast<FileAddressType>(type);
oxReturnError(io->field("data", UnionView(&fa->m_data, static_cast<int>(fa->m_type))));
return OxError(0);
}

View File

@ -269,7 +269,7 @@ Result<const char*> FileSystemTemplate<FileStore, Directory>::directAccess(uint6
if (!data.valid()) {
return OxError(1);
}
return bit_cast<char*>(data.get());
return reinterpret_cast<char*>(data.get());
}
template<typename FileStore, typename Directory>

View File

@ -101,7 +101,7 @@ Result<Vector<String>> PassThroughFS::ls(const char *dir) noexcept {
oxReturnError(OxError(ec.value(), "PassThroughFS: ls failed"));
for (const auto &p : di) {
const auto u8p = p.path().filename().u8string();
out.emplace_back(bit_cast<const char*>(u8p.c_str()));
out.emplace_back(reinterpret_cast<const char*>(u8p.c_str()));
}
return ox::move(out);
}

View File

@ -95,8 +95,7 @@ Error PassThroughFS::ls(const char *dir, F cb) noexcept {
const auto di = std::filesystem::directory_iterator(m_path / stripSlash(dir), ec);
oxReturnError(OxError(ec.value(), "PassThroughFS: ls failed"));
for (auto &p : di) {
const auto u8p = p.path().filename().u8string();
oxReturnError(cb(bit_cast<const char*>(u8p.c_str()), 0));
oxReturnError(cb(p.path().filename().c_str(), 0));
}
return OxError(0);
}

View File

@ -5,6 +5,10 @@ add_library(
write.cpp
)
if(NOT MSVC)
target_compile_options(OxMetalClaw PRIVATE -Wsign-conversion)
endif()
target_link_libraries(
OxMetalClaw PUBLIC
OxModel

View File

@ -25,14 +25,14 @@ static constexpr auto Bits = sizeof(T) << 3;
template<typename I>
[[nodiscard]]
constexpr std::size_t highestBit(I val) noexcept {
int shiftStart = sizeof(I) * 8 - 1;
unsigned shiftStart = sizeof(I) * 8 - 1;
// find most significant non-sign indicator bit
std::size_t highestBit = 0;
// start at one bit lower if signed
if constexpr(is_signed_v<I>) {
--shiftStart;
}
for (auto i = shiftStart; i > -1; --i) {
for (auto i = shiftStart; i < MaxValue<decltype(i)>; --i) {
const auto bitValue = (val >> i) & 1;
if (bitValue) {
highestBit = i;
@ -80,7 +80,7 @@ constexpr McInt encodeInteger(I input) noexcept {
const auto bytesIndicator = onMask<uint8_t>(bytes - 1);
// ensure we are copying from little endian represenstation
LittleEndian<I> leVal = val;
LittleEndian<I> leVal = static_cast<I>(val);
if (bytes == 9) {
out.data[0] = bytesIndicator;
ox_memcpy(&out.data[1], &leVal, sizeof(I));

View File

@ -14,20 +14,4 @@ namespace ox {
template class FieldBitmapReader<uint8_t*>;
template class FieldBitmapReader<const uint8_t*>;
FieldBitmap::FieldBitmap(uint8_t *map, std::size_t maxLen) noexcept: FieldBitmapReader<uint8_t*>(map, maxLen) {
}
Error FieldBitmap::set(std::size_t i, bool on) noexcept {
if (i / 8 < m_mapLen) {
if (on) {
m_map[i / 8] |= 1 << (i % 8);
} else {
m_map[i / 8] &= ~(1 << (i % 8));
}
return OxError(0);
} else {
return OxError(MC_PRESENCEMASKOUTBOUNDS);
}
}
}

View File

@ -18,29 +18,29 @@ class FieldBitmapReader {
protected:
T m_map = nullptr;
std::size_t m_mapLen = 0;
std::size_t m_fields = 0;
public:
FieldBitmapReader(T map, std::size_t maxLen) noexcept;
constexpr FieldBitmapReader(T map, std::size_t maxLen) noexcept;
Result<bool> get(std::size_t i) const noexcept;
constexpr Result<bool> get(std::size_t i) const noexcept;
void setFields(int) noexcept;
constexpr void setFields(int) noexcept;
void setMaxLen(int) noexcept;
constexpr void setMaxLen(int) noexcept;
int getMaxLen() const noexcept;
[[nodiscard]]
constexpr int64_t getMaxLen() const noexcept;
};
template<typename T>
FieldBitmapReader<T>::FieldBitmapReader(T map, std::size_t maxLen) noexcept {
constexpr FieldBitmapReader<T>::FieldBitmapReader(T map, std::size_t maxLen) noexcept {
m_map = map;
m_mapLen = maxLen;
}
template<typename T>
Result<bool> FieldBitmapReader<T>::get(std::size_t i) const noexcept {
constexpr Result<bool> FieldBitmapReader<T>::get(std::size_t i) const noexcept {
if (i / 8 < m_mapLen) {
return (m_map[i / 8] >> (i % 8)) & 1;
} else {
@ -49,19 +49,18 @@ Result<bool> FieldBitmapReader<T>::get(std::size_t i) const noexcept {
}
template<typename T>
void FieldBitmapReader<T>::setFields(int fields) noexcept {
m_fields = fields;
m_mapLen = (fields / 8 + 1) - (fields % 8 == 0);
constexpr void FieldBitmapReader<T>::setFields(int fields) noexcept {
m_mapLen = static_cast<std::size_t>((fields / 8 + 1) - (fields % 8 == 0));
}
template<typename T>
void FieldBitmapReader<T>::setMaxLen(int maxLen) noexcept {
m_mapLen = maxLen;
constexpr void FieldBitmapReader<T>::setMaxLen(int maxLen) noexcept {
m_mapLen = static_cast<std::size_t>(maxLen);
}
template<typename T>
int FieldBitmapReader<T>::getMaxLen() const noexcept {
return m_mapLen;
constexpr int64_t FieldBitmapReader<T>::getMaxLen() const noexcept {
return static_cast<int64_t>(m_mapLen);
}
extern template class FieldBitmapReader<uint8_t*>;
@ -70,10 +69,26 @@ extern template class FieldBitmapReader<const uint8_t*>;
class FieldBitmap: public FieldBitmapReader<uint8_t*> {
public:
FieldBitmap(uint8_t *map, std::size_t maxLen) noexcept;
constexpr FieldBitmap(uint8_t *map, std::size_t maxLen) noexcept;
Error set(std::size_t i, bool on) noexcept;
constexpr Error set(std::size_t i, bool on) noexcept;
};
constexpr FieldBitmap::FieldBitmap(uint8_t *map, std::size_t maxLen) noexcept: FieldBitmapReader<uint8_t*>(map, maxLen) {
}
constexpr Error FieldBitmap::set(std::size_t i, bool on) noexcept {
if (i / 8 < m_mapLen) {
if (on) {
m_map[i / 8] |= 1 << (i % 8);
} else {
m_map[i / 8] &= ~(1 << (i % 8));
}
return OxError(0);
} else {
return OxError(MC_PRESENCEMASKOUTBOUNDS);
}
}
}

View File

@ -66,7 +66,7 @@ Error MetalClawReader::field(const char*, uint64_t *val) noexcept {
Error MetalClawReader::field(const char*, bool *val) noexcept {
if (m_unionIdx == -1 || m_unionIdx == m_field) {
auto valErr = m_fieldPresence.get(m_field);
auto valErr = m_fieldPresence.get(static_cast<std::size_t>(m_field));
*val = valErr.value;
oxReturnError(valErr.error);
}
@ -76,7 +76,7 @@ Error MetalClawReader::field(const char*, bool *val) noexcept {
Error MetalClawReader::field(const char*, SerStr val) noexcept {
if (m_unionIdx == -1 || m_unionIdx == m_field) {
if (m_fieldPresence.get(m_field)) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
if (m_buffIt >= m_buffLen) {
return OxError(MC_BUFFENDED);
@ -110,7 +110,7 @@ Error MetalClawReader::field(const char*, SerStr val) noexcept {
}
Result<ArrayLength> MetalClawReader::arrayLength(const char*, bool pass) noexcept {
if ((m_unionIdx == -1 || m_unionIdx == m_field) && m_fieldPresence.get(m_field)) {
if ((m_unionIdx == -1 || m_unionIdx == m_field) && m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
if (m_buffIt >= m_buffLen) {
return OxError(MC_BUFFENDED);
@ -127,7 +127,7 @@ Result<ArrayLength> MetalClawReader::arrayLength(const char*, bool pass) noexcep
[[nodiscard]]
StringLength MetalClawReader::stringLength(const char*) noexcept {
if ((m_unionIdx == -1 || m_unionIdx == m_field) && m_fieldPresence.get(m_field)) {
if ((m_unionIdx == -1 || m_unionIdx == m_field) && m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
std::size_t bytesRead = 0;
auto len = mc::decodeInteger<StringLength>(&m_buff[m_buffIt], m_buffLen - m_buffIt, &bytesRead);
@ -141,11 +141,11 @@ MetalClawReader MetalClawReader::child(const char*, int unionIdx) noexcept {
}
bool MetalClawReader::fieldPresent(const char*) const noexcept {
return m_fieldPresence.get(m_field).value;
return m_fieldPresence.get(static_cast<std::size_t>(m_field)).value;
}
bool MetalClawReader::fieldPresent(int fieldNo) const noexcept {
return m_fieldPresence.get(fieldNo).value;
return m_fieldPresence.get(static_cast<std::size_t>(fieldNo)).value;
}
void MetalClawReader::nextField() noexcept {

View File

@ -90,7 +90,9 @@ class MetalClawReader {
StringLength stringLength(const char *name) noexcept;
template<typename T = std::nullptr_t>
void setTypeInfo(const char *name = getModelTypeName<T>(), int fields = countFields<T>()) noexcept;
constexpr void setTypeInfo() noexcept;
constexpr void setTypeInfo(const char *name, int fields) noexcept;
/**
* Returns a MetalClawReader to parse a child object.
@ -128,7 +130,7 @@ Error MetalClawReader::field(const char *name, T *val) noexcept {
if constexpr(isVector_v<T>) {
if (m_unionIdx == -1 || m_unionIdx == m_field) {
// set size of val if the field is present, don't worry about it if not
if (m_fieldPresence.get(m_field)) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
oxRequire(len, arrayLength(name, false));
val->resize(len);
}
@ -148,7 +150,7 @@ Error MetalClawReader::field(const char *name, T *val) noexcept {
template<typename U>
Error MetalClawReader::field(const char*, UnionView<U> val) noexcept {
if ((m_unionIdx == -1 || m_unionIdx == m_field) && val.get() && m_fieldPresence.get(m_field)) {
if ((m_unionIdx == -1 || m_unionIdx == m_field) && val.get() && m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
auto reader = child("", val.idx());
oxReturnError(model(&reader, val.get()));
}
@ -164,7 +166,7 @@ Error MetalClawReader::field(const char *name, BString<L> *val) noexcept {
template<typename I>
Error MetalClawReader::readInteger(I *val) noexcept {
if (m_unionIdx == -1 || m_unionIdx == m_field) {
if (m_fieldPresence.get(m_field)) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
std::size_t bytesRead = 0;
if (m_buffIt >= m_buffLen) {
oxTrace("ox::MetalClaw::readInteger") << "Buffer ended";
@ -186,7 +188,7 @@ Error MetalClawReader::readInteger(I *val) noexcept {
template<typename T>
Error MetalClawReader::field(const char *name, T *val, std::size_t valLen) noexcept {
if (m_unionIdx == -1 || m_unionIdx == m_field) {
if (m_fieldPresence.get(m_field)) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
if (m_buffIt >= m_buffLen) {
return OxError(MC_BUFFENDED);
@ -198,7 +200,7 @@ Error MetalClawReader::field(const char *name, T *val, std::size_t valLen) noexc
// read the list
if (valLen >= len) {
auto reader = child("");
reader.setTypeInfo("List", len);
reader.setTypeInfo("List", static_cast<int>(len));
for (std::size_t i = 0; i < len; i++) {
oxReturnError(reader.field("", &val[i]));
}
@ -215,7 +217,7 @@ Error MetalClawReader::field(const char *name, T *val, std::size_t valLen) noexc
template<typename T>
Error MetalClawReader::field(const char*, HashMap<String, T> *val) noexcept {
if (m_unionIdx == -1 || m_unionIdx == m_field) {
if (m_fieldPresence.get(m_field)) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
if (m_buffIt >= m_buffLen) {
return OxError(MC_BUFFENDED);
@ -226,11 +228,12 @@ Error MetalClawReader::field(const char*, HashMap<String, T> *val) noexcept {
// read the list
auto reader = child("");
reader.setTypeInfo("List", len);
reader.setTypeInfo("List", static_cast<int>(len));
for (std::size_t i = 0; i < len; i++) {
const auto keyLen = reader.stringLength(nullptr);
auto wkey = ox_malloca(keyLen + 1, char, 0);
oxReturnError(reader.field("", SerStr(wkey.get(), keyLen)));
auto wkeyPtr = wkey.get();
oxReturnError(reader.field("", SerStr(&wkeyPtr, static_cast<int>(keyLen))));
oxReturnError(reader.field("", &val->operator[](wkey.get())));
}
}
@ -242,7 +245,7 @@ Error MetalClawReader::field(const char*, HashMap<String, T> *val) noexcept {
template<typename T, typename Handler>
Error MetalClawReader::field(const char*, Handler handler) noexcept {
if (m_unionIdx == -1 || m_unionIdx == m_field) {
if (m_fieldPresence.get(m_field)) {
if (m_fieldPresence.get(static_cast<std::size_t>(m_field))) {
// read the length
if (m_buffIt >= m_buffLen) {
return OxError(MC_BUFFENDED);
@ -253,7 +256,7 @@ Error MetalClawReader::field(const char*, Handler handler) noexcept {
// read the list
auto reader = child("");
reader.setTypeInfo("List", len);
reader.setTypeInfo("List", static_cast<int>(len));
for (std::size_t i = 0; i < len; i++) {
T val;
oxReturnError(reader.field("", &val));
@ -266,16 +269,20 @@ Error MetalClawReader::field(const char*, Handler handler) noexcept {
}
template<typename T>
void MetalClawReader::setTypeInfo(const char*, int fields) noexcept {
constexpr void MetalClawReader::setTypeInfo() noexcept {
setTypeInfo(ModelTypeName_v<T>, countFields<T>());
}
constexpr void MetalClawReader::setTypeInfo(const char*, int fields) noexcept {
m_fields = fields;
m_buffIt = (fields / 8 + 1) - (fields % 8 == 0);
m_buffIt = static_cast<std::size_t>((fields / 8 + 1) - (fields % 8 == 0));
m_fieldPresence.setFields(fields);
m_fieldPresence.setMaxLen(static_cast<int>(m_buffIt));
}
template<typename T>
Error readMC(const char *buff, std::size_t buffLen, T *val) noexcept {
MetalClawReader reader(bit_cast<uint8_t*>(buff), buffLen);
MetalClawReader reader(reinterpret_cast<const uint8_t*>(buff), buffLen);
return model(&reader, val);
}

View File

@ -36,7 +36,7 @@ struct TestStructNest {
struct TestStruct {
static constexpr auto TypeName = "TestStruct";
static constexpr auto Fields = 17;
static constexpr auto Fields = 16;
bool Bool = false;
int32_t Int = 0;
int32_t Int1 = 0;
@ -48,21 +48,15 @@ struct TestStruct {
int32_t Int7 = 0;
int32_t Int8 = 0;
TestUnion Union;
char *CString = nullptr;
ox::BString<32> String = "";
uint32_t List[4] = {0, 0, 0, 0};
ox::HashMap<ox::String, int> Map;
TestStructNest EmptyStruct;
TestStructNest Struct;
~TestStruct() {
delete[] CString;
}
};
template<typename T>
ox::Error model(T *io, TestUnion *obj) {
constexpr ox::Error model(T *io, TestUnion *obj) noexcept {
io->template setTypeInfo<TestUnion>();
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
@ -71,7 +65,7 @@ ox::Error model(T *io, TestUnion *obj) {
}
template<typename T>
ox::Error model(T *io, TestStructNest *obj) {
constexpr ox::Error model(T *io, TestStructNest *obj) noexcept {
io->template setTypeInfo<TestStructNest>();
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
@ -80,7 +74,7 @@ ox::Error model(T *io, TestStructNest *obj) {
}
template<typename T>
ox::Error model(T *io, TestStruct *obj) {
constexpr ox::Error model(T *io, TestStruct *obj) noexcept {
io->template setTypeInfo<TestStruct>();
oxReturnError(io->field("Bool", &obj->Bool));
oxReturnError(io->field("Int", &obj->Int));
@ -93,7 +87,6 @@ ox::Error model(T *io, TestStruct *obj) {
oxReturnError(io->field("Int7", &obj->Int7));
oxReturnError(io->field("Int8", &obj->Int8));
oxReturnError(io->field("Union", ox::UnionView{&obj->Union, 1}));
oxReturnError(io->field("CString", ox::SerStr(&obj->CString)));
oxReturnError(io->field("String", &obj->String));
oxReturnError(io->field("List", obj->List, 4));
oxReturnError(io->field("Map", &obj->Map));
@ -127,8 +120,6 @@ std::map<std::string, ox::Error(*)()> tests = {
testIn.Int = 42;
testIn.Union.Int = 42;
testIn.String = "Test String 1";
testIn.CString = new char[ox_strlen("c-string") + 1];
ox_strcpy(testIn.CString, "c-string");
testIn.List[0] = 1;
testIn.List[1] = 2;
testIn.List[2] = 3;
@ -153,7 +144,6 @@ std::map<std::string, ox::Error(*)()> tests = {
oxAssert(testIn.Int6 == testOut.Int6, "Int6 value mismatch");
oxAssert(testIn.Int7 == testOut.Int7, "Int7 value mismatch");
oxAssert(testIn.Int8 == testOut.Int8, "Int8 value mismatch");
oxAssert(ox_strcmp(testIn.CString, testOut.CString) == 0, "CString value mismatch");
oxAssert(testIn.Union.Int == testOut.Union.Int, "Union.Int value mismatch");
oxAssert(testIn.String == testOut.String, "String value mismatch");
oxAssert(testIn.List[0] == testOut.List[0], "List[0] value mismatch");

View File

@ -63,7 +63,7 @@ Error MetalClawWriter::field(const char*, uint64_t *val) noexcept {
Error MetalClawWriter::field(const char*, bool *val) noexcept {
if (m_unionIdx == -1 || m_unionIdx == m_field) {
oxReturnError(m_fieldPresence.set(m_field, *val));
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), *val));
}
++m_field;
return OxError(0);
@ -74,18 +74,18 @@ Error MetalClawWriter::field(const char*, SerStr val) noexcept {
if (val.len() && (m_unionIdx == -1 || m_unionIdx == m_field)) {
// write the length
const auto strLen = mc::encodeInteger(val.len());
if (m_buffIt + strLen.length + val.len() < m_buffLen) {
if (m_buffIt + strLen.length + static_cast<std::size_t>(val.len()) < m_buffLen) {
ox_memcpy(&m_buff[m_buffIt], strLen.data, strLen.length);
m_buffIt += strLen.length;
// write the string
ox_memcpy(&m_buff[m_buffIt], val.c_str(), val.len());
m_buffIt += val.len();
ox_memcpy(&m_buff[m_buffIt], val.c_str(), static_cast<std::size_t>(val.len()));
m_buffIt += static_cast<std::size_t>(val.len());
fieldSet = true;
} else {
return OxError(MC_BUFFENDED);
}
}
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
++m_field;
return OxError(0);
}

View File

@ -107,7 +107,7 @@ Error MetalClawWriter::field(const char*, T *val) noexcept {
fieldSet = true;
}
}
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
m_field++;
return OxError(0);
}
@ -124,7 +124,7 @@ Error MetalClawWriter::field(const char*, UnionView<U> val) noexcept {
fieldSet = true;
}
}
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
m_field++;
return OxError(0);
}
@ -155,7 +155,7 @@ Error MetalClawWriter::field(const char*, T *val, std::size_t len) noexcept {
fieldSet = true;
}
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
m_field++;
return OxError(0);
}
@ -194,7 +194,7 @@ Error MetalClawWriter::field(const char*, HashMap<String, T> *val) noexcept {
fieldSet = true;
}
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
m_field++;
return OxError(0);
}
@ -212,7 +212,7 @@ Error MetalClawWriter::appendInteger(I val) noexcept {
return OxError(MC_BUFFENDED);
}
}
oxReturnError(m_fieldPresence.set(m_field, fieldSet));
oxReturnError(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
m_field++;
return OxError(0);
}
@ -221,14 +221,14 @@ template<typename T>
void MetalClawWriter::setTypeInfo(const char*, int fields) noexcept {
m_fields = fields;
m_fieldPresence.setFields(fields);
m_buffIt = m_fieldPresence.getMaxLen();
m_buffIt = static_cast<std::size_t>(m_fieldPresence.getMaxLen());
ox_memset(m_buff, 0, m_buffIt);
}
template<typename T>
Result<Buffer> writeMC(T *val) noexcept {
Buffer buff(10 * units::MB);
MetalClawWriter writer(bit_cast<uint8_t*>(buff.data()), buff.size());
MetalClawWriter writer(reinterpret_cast<uint8_t*>(buff.data()), buff.size());
oxReturnError(model(&writer, val));
buff.resize(writer.size());
return move(buff);
@ -236,7 +236,7 @@ Result<Buffer> writeMC(T *val) noexcept {
template<typename T>
Error writeMC(char *buff, std::size_t buffLen, T *val, std::size_t *sizeOut = nullptr) noexcept {
MetalClawWriter writer(bit_cast<uint8_t*>(buff), buffLen);
MetalClawWriter writer(reinterpret_cast<uint8_t*>(buff), buffLen);
auto err = model(&writer, val);
if (sizeOut) {
*sizeOut = writer.size();

View File

@ -109,8 +109,8 @@ using FieldList = Vector<DescriptorField>;
struct DescriptorType {
static constexpr auto TypeVersion = 1;
TypeName typeName;
PrimitiveType primitiveType;
TypeName typeName = "";
PrimitiveType primitiveType = PrimitiveType::UnsignedInteger;
// fieldList only applies to structs
FieldList fieldList;
// - number of bytes for integer and float types
@ -123,7 +123,7 @@ struct DescriptorType {
DescriptorType(const TypeName &tn, PrimitiveType t, int b): typeName(tn), primitiveType(t), length(b) {
}
DescriptorType(const TypeName &tn, PrimitiveType t, FieldList fl): typeName(tn), primitiveType(t), fieldList(fl) {
DescriptorType(const TypeName &tn, PrimitiveType t, const FieldList &fl): typeName(tn), primitiveType(t), fieldList(fl) {
}
};
@ -132,7 +132,9 @@ template<typename T>
constexpr Error model(T *io, DescriptorType *type) noexcept {
io->template setTypeInfo<T>("net.drinkingtea.ox.DescriptorType", 5);
oxReturnError(io->field("typeName", &type->typeName));
oxReturnError(io->field("primitiveType", bit_cast<uint8_t*>(&type->primitiveType)));
auto pt = std::bit_cast<uint8_t>(type->primitiveType);
oxReturnError(io->field("primitiveType", &pt));
type->primitiveType = std::bit_cast<PrimitiveType>(pt);
oxReturnError(io->field("fieldList", &type->fieldList));
oxReturnError(io->field("length", &type->length));
oxReturnError(io->field("preloadable", &type->preloadable));
@ -143,8 +145,8 @@ template<typename T>
Error modelWrite(T *io, DescriptorField *field) noexcept {
io->setTypeInfo("ox::DescriptorField", 4);
if (field->ownsType) {
BString<2> empty = "";
oxReturnError(io->field("typeName", SerStr(&empty)));
String empty = "";
oxReturnError(io->field("typeName", &empty));
oxReturnError(io->field("type", field->type));
} else {
oxReturnError(io->field("typeName", &field->type->typeName));

View File

@ -8,6 +8,7 @@
#pragma once
#include <ox/std/bit.hpp>
#include <ox/std/error.hpp>
#include <ox/std/types.hpp>
#include <ox/std/utility.hpp>
@ -196,7 +197,7 @@ class Equals {
template<typename FT>
constexpr Error field(const char*, const FT *v) noexcept {
const auto &src = *v;
const auto &dst = *cbit_cast<FT*>(m_other->vars[m_i]);
const auto &dst = std::bit_cast<FT>(*m_other->vars[m_i]);
++m_i;
if (dst == src) {
return OxError(0);

View File

@ -24,11 +24,13 @@ namespace OpType {
template<typename T, typename O>
constexpr Error modelRead(T*, O*) noexcept {
oxAssert(OxError(1), "Missing modelRead function");
return OxError(1, "Model: modelRead not implemented");
}
template<typename T, typename O>
constexpr Error modelWrite(T*, O*) noexcept {
oxAssert(OxError(1), "Missing modelWrite function");
return OxError(1, "Model: modelWrite not implemented");
}

View File

@ -10,6 +10,7 @@
#include <ox/std/string.hpp>
#include <ox/std/types.hpp>
#include <ox/std/memory.hpp>
#include "fieldcounter.hpp"
#include "optype.hpp"
@ -45,9 +46,11 @@ struct TypeNameCatcher {
template<typename T>
constexpr const char *getModelTypeName() noexcept {
T t;
auto a = std::allocator<T>();
auto t = a.allocate(1);
TypeNameCatcher nc;
oxIgnoreError(model(&nc, &t));
oxIgnoreError(model(&nc, t));
a.deallocate(t, 1);
return nc.name;
}
@ -58,4 +61,7 @@ constexpr const char *getModelTypeName(T *val) noexcept {
return nc.name;
}
template<typename T>
constexpr auto ModelTypeName_v = getModelTypeName<T>();
}

View File

@ -143,9 +143,9 @@ constexpr Error model(Reader *rdr, DataWalker<Reader, FH> *walker) noexcept {
}
template<typename Reader, typename Handler>
Error walkModel(DescriptorType *type, char *data, std::size_t dataLen, Handler handler) noexcept {
Error walkModel(DescriptorType *type, const char *data, std::size_t dataLen, Handler handler) noexcept {
DataWalker<Reader, Handler> walker(type, handler);
Reader rdr(bit_cast<uint8_t*>(data), dataLen);
Reader rdr(reinterpret_cast<const uint8_t*>(data), dataLen);
return model(&rdr, &walker);
}

View File

@ -13,7 +13,7 @@
namespace ox {
OrganicClawReader::OrganicClawReader(const uint8_t *buff, std::size_t buffSize) {
auto json = bit_cast<const char*>(buff);
auto json = reinterpret_cast<const char*>(buff);
auto jsonLen = ox_strnlen(json, buffSize);
Json::CharReaderBuilder parserBuilder;
auto parser = std::unique_ptr<Json::CharReader>(parserBuilder.newCharReader());

View File

@ -85,7 +85,15 @@ class OrganicClawReader {
std::size_t stringLength(const char *name) noexcept;
template<typename T = void>
constexpr void setTypeInfo(const char* = getModelTypeName<T>(), int = countFields<T>()) noexcept {
constexpr void setTypeInfo() noexcept {
}
template<typename T = void>
constexpr void setTypeInfo(const char*) noexcept {
}
template<typename T = void>
constexpr void setTypeInfo(const char*, int) noexcept {
}
/**

View File

@ -8,14 +8,26 @@
#pragma once
#if __cplusplus >= 202002L && defined(OX_USE_STDLIB)
#if defined(OX_USE_STDLIB)
#include <bit>
#endif
#include "defines.hpp"
#include "memops.hpp"
#include "types.hpp"
#include "typetraits.hpp"
#if defined(OX_OS_Darwin) || !defined(OX_USE_STDLIB)
namespace std {
template<typename To, typename From>
constexpr typename ox::enable_if<sizeof(To) == sizeof(From), To>::type bit_cast(const From &src) noexcept {
return __builtin_bit_cast(To, src);
}
}
#endif
namespace ox {
template<typename To, typename From>
@ -25,23 +37,16 @@ constexpr typename enable_if<sizeof(To) == sizeof(From), To>::type cbit_cast(Fro
return dst;
}
template<typename To, typename From>
typename enable_if<sizeof(To) == sizeof(From), To>::type bit_cast(From src) noexcept {
To dst;
memcpy(&dst, &src, sizeof(src));
return dst;
}
template<typename T>
[[nodiscard]] constexpr T rotl(T i, int shift) noexcept {
constexpr auto bits = sizeof(i) * 8;
return (i << static_cast<T>(shift)) | (i >> (bits - static_cast<T>(shift)));
}
template<typename T>
[[nodiscard]] constexpr T onMask(int bits = sizeof(T) << 3 /* *8 */) noexcept {
template<typename T, typename B = int>
[[nodiscard]] constexpr T onMask(B bits = sizeof(T) << 3 /* *8 */) noexcept {
T out = T(0);
for (auto i = 0; i < bits; i++) {
for (B i = 0; i < bits; i++) {
out |= static_cast<T>(1) << i;
}
return out;
@ -56,23 +61,3 @@ static_assert(onMask<int>(3) == 7);
static_assert(onMask<int>(4) == 15);
}
namespace std {
#if __cplusplus >= 202002L && !defined(OX_USE_STDLIB)
template<typename To, typename From>
constexpr typename ox::enable_if<sizeof(To) == sizeof(From), To>::type bit_cast(const From &src) noexcept {
return __builtin_bit_cast(To, src);
}
#elif __cplusplus < 202002L
template<typename To, typename From>
typename ox::enable_if<sizeof(To) == sizeof(From), To>::type bit_cast(From src) noexcept {
To dst = {};
memcpy(&dst, &src, sizeof(src));
return dst;
}
#endif
}

View File

@ -42,7 +42,12 @@ constexpr const char *toCString(const BasicString<size> &s) noexcept {
}
#if __has_include(<string>)
constexpr const char *toCString(const std::string &s) noexcept {
#ifdef OX_OS_Darwin
constexpr
#else
inline
#endif
const char *toCString(const std::string &s) noexcept {
return s.c_str();
}
#endif

View File

@ -161,7 +161,7 @@ Result<T&> HashMap<K, T>::at(K k) noexcept {
auto p = access(m_pairs, k);
if (!p) {
AllocAlias<T> v;
return {*bit_cast<T*>(&v), OxError(1)};
return {*reinterpret_cast<T*>(&v), OxError(1)};
}
return p->value;
}
@ -190,7 +190,7 @@ Result<const T&> HashMap<K, T>::at(K k) const noexcept {
auto p = access(m_pairs, k);
if (!p) {
AllocAlias<T> v;
return {*bit_cast<T*>(&v), OxError(1)};
return {*reinterpret_cast<T*>(&v), OxError(1)};
}
return p->value;
}

View File

@ -25,29 +25,29 @@ static constexpr std::size_t alignedSize(T = {}) noexcept {
return alignedSize(sizeof(T));
}
void HeapSegment::init(std::size_t maxSize = bit_cast<std::size_t>(g_heapEnd)) noexcept {
this->size = maxSize - bit_cast<std::size_t>(this);
void HeapSegment::init(std::size_t maxSize = std::bit_cast<std::size_t>(g_heapEnd)) noexcept {
this->size = maxSize - std::bit_cast<std::size_t>(this);
this->inUse = false;
}
template<typename T>
T *HeapSegment::data() noexcept {
return bit_cast<T*>(bit_cast<uint8_t*>(this) + alignedSize(this));
return reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(this) + alignedSize(this));
}
template<typename T>
T *HeapSegment::end() noexcept {
const auto size = alignedSize(this) + alignedSize(this->size);
auto e = bit_cast<uintptr_t>(bit_cast<uint8_t*>(this) + size);
return bit_cast<T*>(e);
auto e = std::bit_cast<uintptr_t>(reinterpret_cast<uint8_t*>(this) + size);
return reinterpret_cast<T*>(e);
}
void initHeap(char *heapBegin, char *heapEnd) noexcept {
g_heapBegin = bit_cast<HeapSegment*>(heapBegin);
g_heapEnd = bit_cast<HeapSegment*>(heapEnd);
void initHeap(void *heapBegin, void *heapEnd) noexcept {
g_heapBegin = reinterpret_cast<HeapSegment*>(heapBegin);
g_heapEnd = reinterpret_cast<HeapSegment*>(heapEnd);
heapIdx = g_heapBegin;
heapIdx->size = bit_cast<std::size_t>(heapEnd) - bit_cast<std::size_t>(heapIdx);
heapIdx->size = std::bit_cast<std::size_t>(heapEnd) - std::bit_cast<std::size_t>(heapIdx);
heapIdx->inUse = false;
}

View File

@ -26,7 +26,7 @@ struct HeapSegment {
};
void initHeap(char *heapBegin, char *heapEnd) noexcept;
void initHeap(void *heapBegin, void *heapEnd) noexcept;
[[nodiscard]]
void *malloc(std::size_t allocSize) noexcept;

View File

@ -11,27 +11,5 @@
namespace ox {
Random::Random() noexcept {
m_seed[0] = 540932923848;
m_seed[1] = 540932540932;
}
Random::Random(RandomSeed seed) noexcept {
m_seed[0] = seed[0];
m_seed[1] = seed[1];
}
uint64_t Random::gen() noexcept {
// An implementation of the Xoroshiro128+ algorithm
auto s0 = m_seed[0];
auto s1 = m_seed[1];
auto retval = s0 + s1;
s1 ^= s0;
m_seed[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14);
m_seed[1] = rotl(s1, 36);
return retval;
}
}

View File

@ -15,16 +15,31 @@ namespace ox {
using RandomSeed = uint64_t[2];
// An implementation of the Xoroshiro128+ algorithm
class OX_PACKED Random {
private:
RandomSeed m_seed;
RandomSeed m_seed = {540932923848, 540932540932};
public:
Random() noexcept;
constexpr Random() noexcept = default;
explicit Random(RandomSeed seed) noexcept;
explicit constexpr Random(const RandomSeed &seed) noexcept;
uint64_t gen() noexcept;
constexpr uint64_t gen() noexcept;
};
constexpr Random::Random(const RandomSeed &seed) noexcept: m_seed{seed[0], seed[1]} {
}
constexpr uint64_t Random::gen() noexcept {
auto s0 = m_seed[0];
auto s1 = m_seed[1];
const auto retval = s0 + s1;
// reseed for next number
s1 ^= s0;
m_seed[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14);
m_seed[1] = rotl(s1, 36);
return retval;
}
}

View File

@ -26,35 +26,37 @@ namespace ox {
#if defined(OX_USE_STDLIB) && __has_include(<unistd.h>)
[[nodiscard]]
static Vector<String> symbolicate([[maybe_unused]]const char **frames, [[maybe_unused]]std::size_t symbolsLen, [[maybe_unused]]const char *linePrefix) {
static auto symbolicate([[maybe_unused]]char **frames,
[[maybe_unused]]std::size_t symbolsLen,
[[maybe_unused]]const char *linePrefix) {
using StrT = BasicString<100>;
Vector<StrT, 30> out;
#if __has_include(<cxxabi.h>)
Vector<String> out;
for (auto i = 0u; i < symbolsLen; ++i) {
Dl_info info;
if (dladdr(frames[i], &info) && info.dli_sname) {
int status = -1;
const auto name = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
if (status == 0) {
out.emplace_back(sfmt("{}: {}", i, name));
out.emplace_back(sfmt<StrT>("{}: {}", i, name));
continue;
}
}
out.emplace_back(sfmt("{}", frames[i]));
out.emplace_back(sfmt<StrT>("{}", frames[i]));
}
return move(out);
#else // __has_include(<cxxabi.h>)
return {};
#endif // __has_include(<cxxabi.h>)
return move(out);
}
#endif // defined(OX_USE_STDLIB) && __has_include(<unistd.h>)
void printStackTrace([[maybe_unused]]unsigned shave) noexcept {
#if defined(OX_USE_STDLIB) && __has_include(<unistd.h>)
Vector<void*> frames(1000);
constexpr auto FrameCnt = 100;
Vector<void*, FrameCnt> frames(FrameCnt);
frames.resize(static_cast<std::size_t>(backtrace(frames.data(), static_cast<int>(frames.size()))));
if (frames.size() - shave > 2) {
const auto symbols = backtrace_symbols(frames.data() + shave, static_cast<int>(frames.size() - shave));
const auto symbolicatedStacktrace = symbolicate(bit_cast<const char**>(frames.data() + shave), frames.size() - shave, "\t");
const auto symbolicatedStacktrace = symbolicate(reinterpret_cast<char**>(frames.data() + shave), frames.size() - shave, "\t");
free(symbols);
oxErr("Stacktrace:\n");
for (const auto &s : symbolicatedStacktrace) {

View File

@ -19,8 +19,8 @@ std::map<std::string, std::function<ox::Error()>> tests = {
[] {
ox::Buffer buff(ox::units::MB);
ox::heapmgr::initHeap(&buff[0], &buff[buff.size()-1]);
auto a1 = ox::bit_cast<char*>(ox::heapmgr::malloc(5));
auto a2 = ox::bit_cast<char*>(ox::heapmgr::malloc(5));
auto a1 = static_cast<char*>(ox::heapmgr::malloc(5));
auto a2 = static_cast<char*>(ox::heapmgr::malloc(5));
oxAssert(a1 >= &buff.front().value && a1 < &buff.back().value, "malloc is broken");
oxAssert(a2 >= &buff.front().value && a2 < &buff.back().value && a2 > a1 + 5, "malloc is broken");
return OxError(0);

View File

@ -265,7 +265,7 @@ inline void logError(const char *file, int line, const char *fmt, const Error &e
} else {
trc << "Error:";
}
trc << sfmt<BasicString<100>>(fmt, static_cast<uint64_t>(err));
trc << ox::sfmt<BasicString<100>>(fmt, static_cast<uint64_t>(err));
}
}