[ox] Cleanup MetalClawWriter and FieldBitmapWriter

This commit is contained in:
2026-01-27 23:05:03 -06:00
parent a3a56b24e8
commit 58e19fad48
6 changed files with 123 additions and 188 deletions

View File

@@ -1,6 +1,5 @@
add_library(
OxMetalClaw
presenceindicator.cpp
read.cpp
write.cpp
)

View File

@@ -1,17 +0,0 @@
/*
* Copyright 2015 - 2025 gary@drinkingtea.net
*
* 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 https://mozilla.org/MPL/2.0/.
*/
#include "err.hpp"
#include "presenceindicator.hpp"
namespace ox {
template class FieldBitmapWriterBase<uint8_t>;
template class FieldBitmapWriterBase<uint8_t const>;
}

View File

@@ -22,145 +22,101 @@ namespace ox {
template<Reader_c Reader>
class FieldBitmapReader {
protected:
mutable size_t m_mapBlockIdx = ~size_t{0};
mutable uint64_t m_mapBlock = 0;
size_t m_mapStart = 0;
mutable size_t m_mapBlockIdx = ~size_t{};
mutable uint64_t m_mapBlock{};
size_t m_mapStart{};
Reader &m_reader;
public:
explicit constexpr FieldBitmapReader(Reader &reader) noexcept;
explicit constexpr FieldBitmapReader(Reader &reader) noexcept:
m_mapStart(reader.tellg()),
m_reader(reader) {
}
constexpr Result<bool> get(size_t idx) const noexcept;
constexpr Result<bool> get(size_t idx) const noexcept {
constexpr auto blockBits = sizeof(m_mapBlock);
auto const blockIdx = idx / blockBits;
if (m_mapBlockIdx != blockIdx) [[unlikely]] {
OX_RETURN_ERROR(loadMapBlock(blockIdx));
}
idx %= blockBits;
return (m_mapBlock >> idx) & 1;
}
private:
constexpr Error loadMapBlock(size_t idx) const noexcept;
};
template<Reader_c Reader>
constexpr FieldBitmapReader<Reader>::FieldBitmapReader(Reader &reader) noexcept:
m_mapStart(reader.tellg()),
m_reader(reader) {
}
template<Reader_c Reader>
constexpr Result<bool> FieldBitmapReader<Reader>::get(size_t idx) const noexcept {
constexpr auto blockBits = sizeof(m_mapBlock);
auto const blockIdx = idx / blockBits;
if (m_mapBlockIdx != blockIdx) [[unlikely]] {
OX_RETURN_ERROR(loadMapBlock(blockIdx));
}
idx %= blockBits;
return (m_mapBlock >> idx) & 1;
}
template<Reader_c Reader>
constexpr Error FieldBitmapReader<Reader>::loadMapBlock(size_t const idx) const noexcept {
OX_REQUIRE(g, m_reader.tellg());
OX_RETURN_ERROR(m_reader.seekg(static_cast<int>(m_mapStart + idx), ox::ios_base::beg));
Array<char, sizeof(m_mapBlock)> mapBlock{};
OX_RETURN_ERROR(m_reader.read(mapBlock.data(), sizeof(m_mapBlock)));
// Warning: narrow-conv
OX_RETURN_ERROR(m_reader.seekg(static_cast<int>(g), ox::ios_base::beg));
m_mapBlock = 0;
for (uint64_t i{}; auto b : mapBlock) {
m_mapBlock |= static_cast<uint64_t>(std::bit_cast<uint8_t>(b)) << i;
i += 8;
}
m_mapBlockIdx = idx;
return {};
}
template<typename T>
class FieldBitmapWriterBase {
protected:
Span<T> m_map;
size_t m_mapLen = 0;
public:
explicit constexpr FieldBitmapWriterBase(Span<T> map) noexcept;
constexpr auto setBuffer(Span<T> map) noexcept;
constexpr Result<bool> get(size_t i) const noexcept;
constexpr Error setFields(int) noexcept;
constexpr void setMaxLen(int) noexcept;
[[nodiscard]]
constexpr int64_t getMaxLen() const noexcept;
};
template<typename T>
constexpr FieldBitmapWriterBase<T>::FieldBitmapWriterBase(Span<T> map) noexcept:
m_map(map),
m_mapLen(m_map.size()) {
}
template<typename T>
constexpr auto FieldBitmapWriterBase<T>::setBuffer(Span<T> map) noexcept {
m_map = map;
m_mapLen = map.size();
}
template<typename T>
constexpr Result<bool> FieldBitmapWriterBase<T>::get(size_t const i) const noexcept {
if (i / 8 < m_mapLen) {
return (m_map[i / 8] >> (i % 8)) & 1;
} else {
return Error{McPresenceMapOverflow};
}
}
template<typename T>
constexpr Error FieldBitmapWriterBase<T>::setFields(int const fields) noexcept {
m_mapLen = static_cast<size_t>((fields / 8 + 1) - (fields % 8 == 0));
if (m_mapLen > m_map.size()) [[unlikely]] {
return Error{McPresenceMapOverflow};
}
return {};
}
template<typename T>
constexpr void FieldBitmapWriterBase<T>::setMaxLen(int const maxLen) noexcept {
m_mapLen = static_cast<size_t>(maxLen);
}
template<typename T>
constexpr int64_t FieldBitmapWriterBase<T>::getMaxLen() const noexcept {
return static_cast<int64_t>(m_mapLen);
}
extern template class FieldBitmapWriterBase<uint8_t>;
extern template class FieldBitmapWriterBase<uint8_t const>;
class FieldBitmap: public FieldBitmapWriterBase<uint8_t> {
public:
explicit constexpr FieldBitmap(Span<uint8_t> map) noexcept;
constexpr Error set(size_t i, bool on) noexcept;
};
constexpr FieldBitmap::FieldBitmap(Span<uint8_t> map) noexcept:
FieldBitmapWriterBase(map) {
}
constexpr Error FieldBitmap::set(size_t const i, bool const on) noexcept {
if (i / 8 < m_mapLen) {
if (on) {
m_map[i / 8] |= 1 << (i % 8);
} else {
m_map[i / 8] &= ~static_cast<uint8_t>(1 << (i % 8));
constexpr Error loadMapBlock(size_t const idx) const noexcept {
OX_REQUIRE(g, m_reader.tellg());
OX_RETURN_ERROR(m_reader.seekg(static_cast<int>(m_mapStart + idx), ox::ios_base::beg));
Array<char, sizeof(m_mapBlock)> mapBlock{};
OX_RETURN_ERROR(m_reader.read(mapBlock.data(), sizeof(m_mapBlock)));
// Warning: narrow-conv
OX_RETURN_ERROR(m_reader.seekg(static_cast<int>(g), ox::ios_base::beg));
m_mapBlock = 0;
for (uint64_t i{}; auto b : mapBlock) {
m_mapBlock |= static_cast<uint64_t>(std::bit_cast<uint8_t>(b)) << i;
i += 8;
}
m_mapBlockIdx = idx;
return {};
}
return {};
} else {
return Error(McPresenceMapOverflow);
}
}
};
class FieldBitmapWriter {
protected:
Span<char> m_map;
size_t m_mapLen{};
public:
explicit constexpr FieldBitmapWriter(Span<char> const &map) noexcept:
m_map(map),
m_mapLen(m_map.size()) {
}
constexpr auto setBuffer(Span<char> const &map) noexcept {
m_map = map;
m_mapLen = map.size();
}
constexpr Result<bool> get(size_t const i) const noexcept {
if (i / 8 < m_mapLen) {
return (std::bit_cast<uint8_t>(m_map[i / 8]) >> (i % 8)) & 1;
}
return Error{McPresenceMapOverflow};
}
constexpr Error setFields(int const fields) noexcept {
m_mapLen = static_cast<size_t>((fields / 8 + 1) - (fields % 8 == 0));
if (m_mapLen > m_map.size()) [[unlikely]] {
return Error{McPresenceMapOverflow};
}
return {};
}
constexpr void setMaxLen(int const maxLen) noexcept {
m_mapLen = static_cast<size_t>(maxLen);
}
constexpr int64_t getMaxLen() const noexcept {
return static_cast<int64_t>(m_mapLen);
}
constexpr Error set(size_t const i, bool const on) noexcept {
if (i / 8 < m_mapLen) {
char &actual = m_map[i / 8];
uint8_t v = std::bit_cast<uint8_t>(actual);
if (on) {
v |= 1 << (i % 8);
} else {
v &= ~static_cast<uint8_t>(1 << (i % 8));
}
actual = std::bit_cast<char>(v);
return {};
}
return Error{McPresenceMapOverflow};
}
};
}

View File

@@ -29,12 +29,12 @@
namespace ox {
template<Writer_c Writer>
class MetalClawWriter {
class MetalClawWriter: public ModelHandlerBase<MetalClawWriter<Writer>, OpType::Write> {
private:
Vector<uint8_t, 16> m_presenceMapBuff{};
FieldBitmap m_fieldPresence;
int m_field = 0;
Vector<char, 16> m_presenceMapBuff{};
FieldBitmapWriter m_fieldPresence{m_presenceMapBuff};
int m_field{};
Optional<int> m_unionIdx;
size_t m_writerBeginP{};
Writer &m_writer;
@@ -105,12 +105,7 @@ class MetalClawWriter {
return 0;
}
[[nodiscard]]
static constexpr auto opType() noexcept {
return OpType::Write;
}
Error finalize() noexcept;
constexpr Error finalize() noexcept;
private:
constexpr Error appendInteger(Integer_c auto val) noexcept {
@@ -132,7 +127,6 @@ extern template class ModelHandlerInterface<MetalClawWriter<CharBuffWriter>>;
template<Writer_c Writer>
constexpr MetalClawWriter<Writer>::MetalClawWriter(Writer &writer, Optional<int> const &unionIdx) noexcept:
m_fieldPresence(m_presenceMapBuff),
m_unionIdx(unionIdx),
m_writerBeginP(writer.tellp()),
m_writer(writer) {
@@ -267,9 +261,8 @@ constexpr Error MetalClawWriter<Writer>::field(CString, T const *val) noexcept {
bool fieldSet = false;
if (val && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
auto const writeIdx = m_writer.tellp();
MetalClawWriter<Writer> writer(m_writer);
ModelHandlerInterface<MetalClawWriter<Writer>> handler{writer};
OX_RETURN_ERROR(model(&handler, val));
MetalClawWriter writer(m_writer);
OX_RETURN_ERROR(model(writer.interface(), val));
OX_RETURN_ERROR(writer.finalize());
fieldSet = writeIdx != m_writer.tellp();
}
@@ -285,9 +278,8 @@ constexpr Error MetalClawWriter<Writer>::field(CString, UnionView<U, force> val)
bool fieldSet = false;
if (val.get() && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
auto const writeIdx = m_writer.tellp();
MetalClawWriter<Writer> writer(m_writer, Optional<int>(in_place, val.idx()));
ModelHandlerInterface handler{writer};
OX_RETURN_ERROR(model(&handler, val.get()));
MetalClawWriter writer(m_writer, Optional<int>(in_place, val.idx()));
OX_RETURN_ERROR(model(writer.interface(), val.get()));
OX_RETURN_ERROR(writer.finalize());
fieldSet = writeIdx != m_writer.tellp();
}
@@ -305,13 +297,12 @@ constexpr Error MetalClawWriter<Writer>::field(CString, T const *val, size_t con
auto const arrLen = mc::encodeInteger(len);
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<CString>(arrLen.data.data()), arrLen.length));
auto const writeIdx = m_writer.tellp();
MetalClawWriter<Writer> writer(m_writer);
ModelHandlerInterface handler{writer};
OX_RETURN_ERROR(handler.template setTypeInfo<T>("List", 0, {}, static_cast<size_t>(len)));
MetalClawWriter writer(m_writer);
OX_RETURN_ERROR(writer.interface()->template setTypeInfo<T>("List", 0, {}, static_cast<size_t>(len)));
// write the array
for (size_t i{}; i < len; ++i) {
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
OX_RETURN_ERROR(handler.field("", &val[i]));
OX_RETURN_ERROR(writer.interface()->field("", &val[i]));
OX_ALLOW_UNSAFE_BUFFERS_END
}
OX_RETURN_ERROR(writer.finalize());
@@ -333,10 +324,9 @@ constexpr Error MetalClawWriter<Writer>::field(CString, HashMap<String, T> const
auto const arrLen = mc::encodeInteger(len);
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<CString>(arrLen.data.data()), arrLen.length));
// write map
MetalClawWriter<Writer> writer(m_writer);
ModelHandlerInterface handler{writer};
MetalClawWriter writer(m_writer);
// double len for both key and value
OX_RETURN_ERROR(handler.setTypeInfo("Map", 0, {}, len * 2));
OX_RETURN_ERROR(writer.interface()->setTypeInfo("Map", 0, {}, len * 2));
// this loop body needs to be in a lambda because of the potential alloca call
constexpr auto loopBody = [](auto &handler, auto const &key, auto const &val) -> Error {
auto const keyLen = key.size();
@@ -349,7 +339,7 @@ constexpr Error MetalClawWriter<Writer>::field(CString, HashMap<String, T> const
// write the array
for (size_t i{}; i < len; ++i) {
auto const &key = keys[i];
OX_RETURN_ERROR(loopBody(handler, key, *val));
OX_RETURN_ERROR(loopBody(*writer.interface(), key, *val));
}
OX_RETURN_ERROR(writer.finalize());
fieldSet = true;
@@ -374,20 +364,19 @@ constexpr Error MetalClawWriter<Writer>::setTypeInfo(
}
template<Writer_c Writer>
Error MetalClawWriter<Writer>::finalize() noexcept {
constexpr Error MetalClawWriter<Writer>::finalize() noexcept {
auto const end = m_writer.tellp();
OX_RETURN_ERROR(m_writer.seekp(m_writerBeginP));
OX_RETURN_ERROR(m_writer.write(
reinterpret_cast<CString>(m_presenceMapBuff.data()),
m_presenceMapBuff.size()));
m_presenceMapBuff.data(),
m_presenceMapBuff.size()));
OX_RETURN_ERROR(m_writer.seekp(end));
return {};
}
Result<Buffer> writeMC(Writer_c auto &writer, auto const &val) noexcept {
MetalClawWriter mcWriter(writer);
ModelHandlerInterface handler{mcWriter};
OX_RETURN_ERROR(model(&handler, &val));
OX_RETURN_ERROR(model(mcWriter.interface(), &val));
OX_RETURN_ERROR(mcWriter.finalize());
return {};
}

View File

@@ -123,7 +123,7 @@ class ModelHandlerInterface {
{
auto &u = v->template get<ModelUnion>();
if constexpr(opType_v == OpType::Read) {
u.setActiveField(m_handler.whichFieldPresent(name, u));
u.setActiveField(whichFieldPresent(m_handler, name, u));
return m_handler.field(name, UnionView<ModelUnion, true>(&u, u.unionIdx()));
} else {
return m_handler.field(name, UnionView<const ModelUnion, true>(&u, u.unionIdx()));
@@ -199,6 +199,20 @@ class ModelHandlerInterface {
constexpr auto handler() noexcept {
return m_handler;
}
private:
template<typename H>
static constexpr int whichFieldPresent(H &h, const char *name, ModelUnion const &u) noexcept
requires(H::opType() == OpType::Read) {
return h.whichFieldPresent(name, u);
}
template<typename H>
static constexpr int whichFieldPresent(H&, const char*, ModelUnion const&) noexcept
requires(H::opType() != OpType::Read) {
return 0;
}
};
template<typename Handler, OpType opType_v = Handler::opType()>

View File

@@ -200,8 +200,6 @@ class ModelValue {
template<typename T>
constexpr Error set(T &&v) noexcept;
constexpr ModelValue &operator=(ModelValue &val) noexcept;
constexpr ModelValue &operator=(const ModelValue &val) noexcept;
constexpr ModelValue &operator=(ModelValue &&val) noexcept;
@@ -1202,10 +1200,6 @@ constexpr Error ModelValue::set(T &&v) noexcept {
return {};
}
constexpr ModelValue &ModelValue::operator=(ModelValue &other) noexcept {
return this->operator=(const_cast<const ModelValue&>(other));
}
constexpr ModelValue &ModelValue::operator=(const ModelValue &other) noexcept {
if (this == &other) [[unlikely]] {
return *this;