409 lines
14 KiB
C++
409 lines
14 KiB
C++
/*
|
|
* 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/.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <ox/model/fieldcounter.hpp>
|
|
#include <ox/model/modelhandleradaptor.hpp>
|
|
#include <ox/model/optype.hpp>
|
|
#include <ox/model/types.hpp>
|
|
#include <ox/std/bit.hpp>
|
|
#include <ox/std/buffer.hpp>
|
|
#include <ox/std/byteswap.hpp>
|
|
#include <ox/std/hashmap.hpp>
|
|
#include <ox/std/optional.hpp>
|
|
#include <ox/std/string.hpp>
|
|
#include <ox/std/types.hpp>
|
|
#include <ox/std/units.hpp>
|
|
|
|
#include "intops.hpp"
|
|
#include "err.hpp"
|
|
#include "presenceindicator.hpp"
|
|
#include "types.hpp"
|
|
|
|
namespace ox {
|
|
|
|
template<Writer_c Writer>
|
|
class MetalClawWriter {
|
|
|
|
private:
|
|
ox::Vector<uint8_t, 16> m_presenceMapBuff{};
|
|
FieldBitmap m_fieldPresence;
|
|
int m_field = 0;
|
|
ox::Optional<int> m_unionIdx;
|
|
std::size_t m_writerBeginP{};
|
|
Writer &m_writer;
|
|
|
|
public:
|
|
constexpr explicit MetalClawWriter(Writer &writer, ox::Optional<int> const&unionIdx = {}) noexcept;
|
|
|
|
constexpr ~MetalClawWriter() noexcept = default;
|
|
|
|
constexpr Error field(const char*, const int8_t *val) noexcept;
|
|
constexpr Error field(const char*, const int16_t *val) noexcept;
|
|
constexpr Error field(const char*, const int32_t *val) noexcept;
|
|
constexpr Error field(const char*, const int64_t *val) noexcept;
|
|
|
|
constexpr Error field(const char*, const uint8_t *val) noexcept;
|
|
constexpr Error field(const char*, const uint16_t *val) noexcept;
|
|
constexpr Error field(const char*, const uint32_t *val) noexcept;
|
|
constexpr Error field(const char*, const uint64_t *val) noexcept;
|
|
|
|
constexpr Error field(const char*, const bool *val) noexcept;
|
|
|
|
template<typename T>
|
|
constexpr Error field(const char*, const T *val, std::size_t len) noexcept;
|
|
|
|
template<typename T>
|
|
constexpr Error field(const char *name, const HashMap<String, T> *val) noexcept;
|
|
|
|
template<std::size_t SmallStringSize>
|
|
constexpr Error field(const char*, const BasicString<SmallStringSize> *val) noexcept;
|
|
|
|
template<std::size_t L>
|
|
constexpr Error field(const char*, const IString<L> *val) noexcept;
|
|
|
|
constexpr Error fieldCString(const char *name, const char *const*val, std::size_t buffLen) noexcept;
|
|
|
|
constexpr Error fieldCString(const char *name, const char **val) noexcept;
|
|
|
|
constexpr Error fieldCString(const char *name, const char *const*val) noexcept;
|
|
|
|
constexpr Error fieldCString(const char *name, const char *val, std::size_t len) noexcept;
|
|
|
|
template<typename T>
|
|
constexpr Error field(const char*, const T *val) noexcept;
|
|
|
|
template<typename U, bool force = false>
|
|
constexpr Error field(const char*, UnionView<U, force> val) noexcept;
|
|
|
|
template<typename T = std::nullptr_t>
|
|
constexpr ox::Error setTypeInfo(
|
|
const char *name = T::TypeName,
|
|
int version = T::TypeVersion,
|
|
const Vector<String>& = {},
|
|
std::size_t fields = ModelFieldCount_v<T>) noexcept;
|
|
|
|
/**
|
|
* stringLength is not implemented in MetalClawWriter
|
|
*/
|
|
[[nodiscard]]
|
|
constexpr auto stringLength(const char*) noexcept {
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* stringLength is not implemented in MetalClawWriter
|
|
*/
|
|
[[nodiscard]]
|
|
constexpr auto arrayLength(const char*, bool = true) noexcept {
|
|
return 0;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
static constexpr auto opType() noexcept {
|
|
return OpType::Write;
|
|
}
|
|
|
|
ox::Error finalize() noexcept;
|
|
|
|
private:
|
|
constexpr Error appendInteger(Integer_c auto val) noexcept {
|
|
bool fieldSet = false;
|
|
if (val && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
|
|
auto mi = mc::encodeInteger(val);
|
|
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<const char*>(mi.data.data()), mi.length));
|
|
fieldSet = true;
|
|
}
|
|
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
|
++m_field;
|
|
return ox::Error(0);
|
|
}
|
|
|
|
};
|
|
|
|
extern template class ModelHandlerInterface<MetalClawWriter<BufferWriter>>;
|
|
extern template class ModelHandlerInterface<MetalClawWriter<CharBuffWriter>>;
|
|
|
|
template<Writer_c Writer>
|
|
constexpr MetalClawWriter<Writer>::MetalClawWriter(Writer &writer, ox::Optional<int> const&unionIdx) noexcept:
|
|
m_fieldPresence(m_presenceMapBuff.data(), m_presenceMapBuff.size()),
|
|
m_unionIdx(unionIdx),
|
|
m_writerBeginP(writer.tellp()),
|
|
m_writer(writer) {
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, const int8_t *val) noexcept {
|
|
return appendInteger(*val);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, const int16_t *val) noexcept {
|
|
return appendInteger(*val);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, const int32_t *val) noexcept {
|
|
return appendInteger(*val);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, const int64_t *val) noexcept {
|
|
return appendInteger(*val);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, const uint8_t *val) noexcept {
|
|
return appendInteger(*val);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, const uint16_t *val) noexcept {
|
|
return appendInteger(*val);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, const uint32_t *val) noexcept {
|
|
return appendInteger(*val);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, const uint64_t *val) noexcept {
|
|
return appendInteger(*val);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, const bool *val) noexcept {
|
|
if (!m_unionIdx.has_value() || *m_unionIdx == m_field) {
|
|
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), *val));
|
|
}
|
|
++m_field;
|
|
return ox::Error(0);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
template<std::size_t SmallStringSize>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, const BasicString<SmallStringSize> *val) noexcept {
|
|
bool fieldSet = false;
|
|
if (val->len() && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
|
|
// write the length
|
|
const auto strLen = mc::encodeInteger(val->len());
|
|
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<const char*>(strLen.data.data()), strLen.length));
|
|
// write the string
|
|
OX_RETURN_ERROR(m_writer.write(val->c_str(), static_cast<std::size_t>(val->len())));
|
|
fieldSet = true;
|
|
}
|
|
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
|
++m_field;
|
|
return ox::Error(0);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
template<std::size_t L>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char *name, const IString<L> *val) noexcept {
|
|
return fieldCString(name, val->data(), val->len());
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
constexpr Error MetalClawWriter<Writer>::fieldCString(const char*, const char *const*val, std::size_t) noexcept {
|
|
bool fieldSet = false;
|
|
if (!m_unionIdx.has_value() || *m_unionIdx == m_field) {
|
|
const auto strLen = *val ? ox::strlen(*val) : 0;
|
|
// write the length
|
|
const auto strLenBuff = mc::encodeInteger(strLen);
|
|
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<const char*>(strLenBuff.data.data()), strLenBuff.length));
|
|
// write the string
|
|
OX_RETURN_ERROR(m_writer.write(*val, static_cast<std::size_t>(strLen)));
|
|
fieldSet = true;
|
|
}
|
|
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
|
++m_field;
|
|
return ox::Error(0);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
constexpr Error MetalClawWriter<Writer>::fieldCString(const char *name, const char **val) noexcept {
|
|
return fieldCString(name, val, {});
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
constexpr Error MetalClawWriter<Writer>::fieldCString(const char *name, const char *const*val) noexcept {
|
|
return fieldCString(name, val, {});
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
constexpr Error MetalClawWriter<Writer>::fieldCString(const char*, const char *val, std::size_t strLen) noexcept {
|
|
bool fieldSet = false;
|
|
if (strLen && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
|
|
// write the length
|
|
const auto strLenBuff = mc::encodeInteger(strLen);
|
|
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<const char*>(strLenBuff.data.data()), strLenBuff.length));
|
|
// write the string
|
|
OX_RETURN_ERROR(m_writer.write(val, static_cast<std::size_t>(strLen)));
|
|
fieldSet = true;
|
|
}
|
|
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
|
++m_field;
|
|
return ox::Error(0);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
template<typename T>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, const T *val) noexcept {
|
|
if constexpr(isVector_v<T> || isArray_v<T>) {
|
|
return field(nullptr, val->data(), val->size());
|
|
} else {
|
|
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));
|
|
OX_RETURN_ERROR(writer.finalize());
|
|
fieldSet = writeIdx != m_writer.tellp();
|
|
}
|
|
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
|
++m_field;
|
|
return {};
|
|
}
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
template<typename U, bool force>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, UnionView<U, force> val) noexcept {
|
|
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, ox::Optional<int>(ox::in_place, val.idx()));
|
|
ModelHandlerInterface handler{&writer};
|
|
OX_RETURN_ERROR(model(&handler, val.get()));
|
|
OX_RETURN_ERROR(writer.finalize());
|
|
fieldSet = writeIdx != m_writer.tellp();
|
|
}
|
|
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
|
++m_field;
|
|
return {};
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
template<typename T>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, const T *val, std::size_t len) noexcept {
|
|
bool fieldSet = false;
|
|
if (len && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
|
|
// write the length
|
|
const auto arrLen = mc::encodeInteger(len);
|
|
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<const char*>(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<std::size_t>(len)));
|
|
// write the array
|
|
for (std::size_t i = 0; i < len; ++i) {
|
|
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
|
OX_RETURN_ERROR(handler.field("", &val[i]));
|
|
OX_ALLOW_UNSAFE_BUFFERS_END
|
|
}
|
|
OX_RETURN_ERROR(writer.finalize());
|
|
fieldSet = writeIdx != m_writer.tellp();
|
|
}
|
|
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
|
++m_field;
|
|
return ox::Error(0);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
template<typename T>
|
|
constexpr Error MetalClawWriter<Writer>::field(const char*, const HashMap<String, T> *val) noexcept {
|
|
const auto &keys = val->keys();
|
|
const auto len = keys.size();
|
|
bool fieldSet = false;
|
|
if (len && (!m_unionIdx.has_value() || *m_unionIdx == m_field)) {
|
|
// write the length
|
|
const auto arrLen = mc::encodeInteger(len);
|
|
OX_RETURN_ERROR(m_writer.write(reinterpret_cast<const char*>(arrLen.data.data()), arrLen.length));
|
|
// write map
|
|
MetalClawWriter<Writer> writer(m_writer);
|
|
ModelHandlerInterface handler{&writer};
|
|
// double len for both key and value
|
|
OX_RETURN_ERROR(handler.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) -> ox::Error {
|
|
const auto keyLen = key.len();
|
|
auto wkey = ox_malloca(keyLen + 1, char, 0);
|
|
memcpy(wkey.get(), key.c_str(), keyLen + 1);
|
|
OX_RETURN_ERROR(handler.fieldCString("", wkey.get(), keyLen));
|
|
OX_REQUIRE_M(value, val.at(key));
|
|
return handler.field("", value);
|
|
};
|
|
// write the array
|
|
for (std::size_t i = 0; i < len; ++i) {
|
|
auto const&key = keys[i];
|
|
OX_RETURN_ERROR(loopBody(handler, key, *val));
|
|
}
|
|
OX_RETURN_ERROR(writer.finalize());
|
|
fieldSet = true;
|
|
}
|
|
OX_RETURN_ERROR(m_fieldPresence.set(static_cast<std::size_t>(m_field), fieldSet));
|
|
++m_field;
|
|
return ox::Error(0);
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
template<typename T>
|
|
constexpr ox::Error MetalClawWriter<Writer>::setTypeInfo(
|
|
const char*,
|
|
int,
|
|
const Vector<String>&,
|
|
std::size_t fields) noexcept {
|
|
const auto fieldPresenceLen = (fields - 1) / 8 + 1;
|
|
OX_RETURN_ERROR(m_writer.write(nullptr, fieldPresenceLen));
|
|
m_presenceMapBuff.resize(fieldPresenceLen);
|
|
m_fieldPresence.setBuffer(m_presenceMapBuff.data(), m_presenceMapBuff.size());
|
|
m_fieldPresence.setFields(static_cast<int>(fields));
|
|
return {};
|
|
}
|
|
|
|
template<Writer_c Writer>
|
|
ox::Error MetalClawWriter<Writer>::finalize() noexcept {
|
|
const auto end = m_writer.tellp();
|
|
OX_RETURN_ERROR(m_writer.seekp(m_writerBeginP));
|
|
OX_RETURN_ERROR(m_writer.write(
|
|
reinterpret_cast<const char*>(m_presenceMapBuff.data()),
|
|
m_presenceMapBuff.size()));
|
|
OX_RETURN_ERROR(m_writer.seekp(end));
|
|
return {};
|
|
}
|
|
|
|
Result<Buffer> writeMC(Writer_c auto &writer, const auto &val) noexcept {
|
|
MetalClawWriter mcWriter(writer);
|
|
ModelHandlerInterface handler{&mcWriter};
|
|
OX_RETURN_ERROR(model(&handler, &val));
|
|
OX_RETURN_ERROR(mcWriter.finalize());
|
|
return {};
|
|
}
|
|
|
|
Result<Buffer> writeMC(auto const&val, std::size_t buffReserveSz = 2 * units::KB) noexcept {
|
|
Buffer buff(buffReserveSz);
|
|
BufferWriter bw(&buff, 0);
|
|
OX_RETURN_ERROR(writeMC(bw, val));
|
|
buff.resize(bw.tellp());
|
|
return buff;
|
|
}
|
|
|
|
Error writeMC(char *buff, std::size_t buffLen, auto const&val, std::size_t *sizeOut = nullptr) noexcept {
|
|
CharBuffWriter bw{{buff, buffLen}};
|
|
OX_RETURN_ERROR(writeMC(bw, val));
|
|
if (sizeOut) {
|
|
*sizeOut = bw.tellp();
|
|
}
|
|
return {};
|
|
}
|
|
|
|
}
|