12enum class Tag : std::uint8_t {
23void put32(std::vector<std::uint8_t>& out, std::uint32_t
value) {
24 for (
unsigned shift = 0; shift != 32; shift += 8)
25 out.push_back(
static_cast<std::uint8_t
>(
value >> shift));
28void put64(std::vector<std::uint8_t>& out, std::uint64_t
value) {
29 for (
unsigned shift = 0; shift != 64; shift += 8)
30 out.push_back(
static_cast<std::uint8_t
>(
value >> shift));
33Result<void> appendString(std::vector<std::uint8_t>& out, std::string_view
value,
34 const RuntimeDefinitionLimits&
limits) {
38 "runtime definition string is invalid or exceeds limits", {}, {},
39 "asset.runtime-definition"));
40 put32(out,
static_cast<std::uint32_t
>(
value.size()));
41 out.insert(out.end(),
value.begin(),
value.end());
45Result<void> encodeValue(
const Value&
value, std::vector<std::uint8_t>& out,
46 const RuntimeDefinitionLimits&
limits, std::uint32_t
depth,
47 std::uint32_t&
count) {
50 "runtime definition structure exceeds limits", {}, {},
51 "asset.runtime-definition"));
53 switch (
value.type()) {
54 case Value::Type::Null: out.push_back(
static_cast<std::uint8_t
>(Tag::Null));
break;
56 out.push_back(
static_cast<std::uint8_t
>(
value.asBool() ? Tag::True : Tag::False));
59 out.push_back(
static_cast<std::uint8_t
>(Tag::Int64));
60 put64(out, std::bit_cast<std::uint64_t>(
value.asInt()));
63 if (!std::isfinite(
value.asDouble()))
65 "runtime definition contains a non-finite number", {},
66 {},
"asset.runtime-definition"));
67 out.push_back(
static_cast<std::uint8_t
>(Tag::Double));
68 put64(out, std::bit_cast<std::uint64_t>(
value.asDouble()));
71 out.push_back(
static_cast<std::uint8_t
>(Tag::String));
72 auto appended = appendString(out,
value.asString(),
limits);
73 if (!appended)
return appended;
78 if (!
values ||
values->size() > std::numeric_limits<std::uint32_t>::max())
80 "runtime definition array exceeds limits", {}, {},
81 "asset.runtime-definition"));
82 out.push_back(
static_cast<std::uint8_t
>(Tag::Array));
83 put32(out,
static_cast<std::uint32_t
>(
values->size()));
84 for (
const auto& child : *
values) {
86 if (!encoded)
return encoded;
92 if (!
object ||
object->size() > std::numeric_limits<std::uint32_t>::max())
94 "runtime definition object exceeds limits", {}, {},
95 "asset.runtime-definition"));
96 out.push_back(
static_cast<std::uint8_t
>(Tag::Object));
97 put32(out,
static_cast<std::uint32_t
>(
object->size()));
98 for (
const auto& [
key, child] : *
object) {
99 auto appended = appendString(out,
key,
limits);
100 if (!appended)
return appended;
102 if (!encoded)
return encoded;
107 if (out.size() >
limits.maximumBytes)
109 "runtime definition exceeds byte budget", {}, {},
110 "asset.runtime-definition"));
116 Decoder(std::span<const std::uint8_t>
bytes,
const RuntimeDefinitionLimits&
limits)
119 Result<Value>
value(std::uint32_t
depth = 0) {
120 if (
depth > limits_.maximumDepth || count_ >= limits_.maximumValues)
122 "runtime definition structure exceeds limits", {}, {},
123 "asset.runtime-definition"));
125 std::uint8_t rawTag = 0;
128 "runtime definition value is truncated", {}, {},
129 "asset.runtime-definition"));
130 const auto tag =
static_cast<Tag
>(rawTag);
134 if (
tag == Tag::Int64) {
135 std::uint64_t raw = 0;
138 {},
"asset.runtime-definition"));
141 if (
tag == Tag::Double) {
142 std::uint64_t raw = 0;
145 {},
"asset.runtime-definition"));
146 const double decoded = std::bit_cast<double>(raw);
147 if (!std::isfinite(decoded))
149 "runtime definition double is non-finite", {}, {},
150 "asset.runtime-definition"));
153 if (
tag == Tag::String) {
158 std::uint32_t
size = 0;
159 if ((
tag != Tag::Array &&
tag != Tag::Object) || !u32(
size) ||
160 size > limits_.maximumValues - count_)
162 "runtime definition collection is invalid", {}, {},
163 "asset.runtime-definition"));
164 if (
tag == Tag::Array) {
166 result.reserve(
size);
167 for (std::uint32_t i = 0; i <
size; ++i) {
169 if (!child)
return child;
170 result.push_back(std::move(child).takeValue());
176 for (std::uint32_t i = 0; i <
size; ++i) {
181 "runtime definition object keys are not canonical", {},
182 {},
"asset.runtime-definition"));
185 if (!child)
return child;
186 result.emplace(std::move(
key).takeValue(), std::move(child).takeValue());
191 std::size_t
cursor() const noexcept {
return cursor_; }
194 bool byte(std::uint8_t&
value) {
195 if (cursor_ == bytes_.size())
return false;
196 value = bytes_[cursor_++];
199 bool u32(std::uint32_t&
value) {
200 if (bytes_.size() - cursor_ < 4)
return false;
202 for (
unsigned shift = 0; shift != 32; shift += 8)
203 value |= std::uint32_t(bytes_[cursor_++]) << shift;
206 bool u64(std::uint64_t&
value) {
207 if (bytes_.size() - cursor_ < 8)
return false;
209 for (
unsigned shift = 0; shift != 64; shift += 8)
210 value |= std::uint64_t(bytes_[cursor_++]) << shift;
213 Result<std::string>
string() {
214 std::uint32_t
size = 0;
215 if (!u32(
size) ||
size > limits_.maximumStringBytes ||
size > bytes_.size() - cursor_)
217 "runtime definition string is invalid", {}, {},
218 "asset.runtime-definition"));
219 std::string result(
reinterpret_cast<const char*
>(bytes_.data() + cursor_),
size);
223 "runtime definition string is not valid UTF-8", {},
224 {},
"asset.runtime-definition"));
228 std::span<const std::uint8_t> bytes_;
229 const RuntimeDefinitionLimits& limits_;
230 std::size_t cursor_ = 0;
231 std::uint32_t count_ = 0;
238 static constexpr std::uint8_t magic[] = {
'E',
'V',
'D',
'E',
'F', 0, 1, 0};
239 std::vector<std::uint8_t> result(std::begin(magic), std::end(magic));
240 std::uint32_t
count = 0;
248 static constexpr std::uint8_t magic[] = {
'E',
'V',
'D',
'E',
'F', 0, 1, 0};
250 !std::equal(std::begin(magic), std::end(magic),
bytes.begin()))
252 "runtime definition header or byte budget is invalid", {}, {},
253 "asset.runtime-definition"));
254 Decoder decoder(
bytes.subspan(
sizeof(magic)),
limits);
255 auto result = decoder.value();
256 if (!result)
return result;
257 if (decoder.cursor() !=
bytes.size() -
sizeof(magic))
std::map< std::string, Var > values
graphics::Canvas * previous
JSON-free versioned runtime metadata codec.
const AssetImportLimits & limits
Strict allocation-free UTF-8 validation.
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
The canonical owning dynamic value used by data-facing protocols.
std::map< std::string, Value > Object
std::vector< Value > Array
Result< Value > decodeRuntimeDefinition(std::span< const std::uint8_t > bytes, const RuntimeDefinitionLimits &limits)
Decode a bounded EVDEF\0\1 metadata tree without a JSON parser.
Result< std::vector< std::uint8_t > > encodeRuntimeDefinition(const Value &value, const RuntimeDefinitionLimits &limits)
Encode an owning metadata tree as deterministic EVDEF\0\1 bytes.
void put32(std::vector< char > &b, uint32_t v)
Put 32.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
WidgetDesc child(std::string id, std::vector< WidgetDesc > children, float width, float height)
Scrollable child region with an explicit size.
bool isValidUtf8(std::string_view text, Utf8NullPolicy nullPolicy) noexcept
Validate shortest-form UTF-8, Unicode scalar range and optional NUL exclusion.
enum EVENGINE_API_FOUNDATION String
OT_STRING.
enum EVENGINE_API_FOUNDATION Array
OT_ARRAY (editable via Runtime array APIs / ReflectedPropertyModel).
Structural and allocation limits for untrusted EVDEF metadata.