载入中...
搜索中...
未找到
RuntimeDefinition.cpp
浏览该文件的文档.
2
4
5#include <bit>
6#include <cmath>
7#include <limits>
8
9namespace eve::asset {
10namespace {
11
12enum class Tag : std::uint8_t {
13 Null = 0,
14 False = 1,
15 True = 2,
16 Int64 = 3,
17 Double = 4,
18 String = 5,
19 Array = 6,
20 Object = 7,
21};
22
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));
26}
27
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));
31}
32
33Result<void> appendString(std::vector<std::uint8_t>& out, std::string_view value,
34 const RuntimeDefinitionLimits& limits) {
35 if (value.size() > limits.maximumStringBytes ||
36 value.size() > std::numeric_limits<std::uint32_t>::max() || !isValidUtf8(value))
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());
42 return Result<void>::success();
43}
44
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) {
48 if (depth > limits.maximumDepth || count >= limits.maximumValues)
50 "runtime definition structure exceeds limits", {}, {},
51 "asset.runtime-definition"));
52 ++count;
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));
57 break;
59 out.push_back(static_cast<std::uint8_t>(Tag::Int64));
60 put64(out, std::bit_cast<std::uint64_t>(value.asInt()));
61 break;
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()));
69 break;
71 out.push_back(static_cast<std::uint8_t>(Tag::String));
72 auto appended = appendString(out, value.asString(), limits);
73 if (!appended) return appended;
74 break;
75 }
76 case Value::Type::Array: {
77 const auto* values = value.getIf<Value::Array>();
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) {
85 auto encoded = encodeValue(child, out, limits, depth + 1, count);
86 if (!encoded) return encoded;
87 }
88 break;
89 }
91 const auto* object = value.getIf<Value::Object>();
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;
101 auto encoded = encodeValue(child, out, limits, depth + 1, count);
102 if (!encoded) return encoded;
103 }
104 break;
105 }
106 }
107 if (out.size() > limits.maximumBytes)
109 "runtime definition exceeds byte budget", {}, {},
110 "asset.runtime-definition"));
111 return Result<void>::success();
112}
113
114class Decoder {
115public:
116 Decoder(std::span<const std::uint8_t> bytes, const RuntimeDefinitionLimits& limits)
117 : bytes_(bytes), limits_(limits) {}
118
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"));
124 ++count_;
125 std::uint8_t rawTag = 0;
126 if (!byte(rawTag))
128 "runtime definition value is truncated", {}, {},
129 "asset.runtime-definition"));
130 const auto tag = static_cast<Tag>(rawTag);
131 if (tag == Tag::Null) return Result<Value>::success(Value());
132 if (tag == Tag::False) return Result<Value>::success(Value(false));
133 if (tag == Tag::True) return Result<Value>::success(Value(true));
134 if (tag == Tag::Int64) {
135 std::uint64_t raw = 0;
136 if (!u64(raw))
138 {}, "asset.runtime-definition"));
139 return Result<Value>::success(Value(std::bit_cast<std::int64_t>(raw)));
140 }
141 if (tag == Tag::Double) {
142 std::uint64_t raw = 0;
143 if (!u64(raw))
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"));
151 return Result<Value>::success(Value(decoded));
152 }
153 if (tag == Tag::String) {
154 auto decoded = string();
155 if (!decoded) return Result<Value>::failure(decoded.status());
156 return Result<Value>::success(Value(std::move(decoded).takeValue()));
157 }
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) {
165 Value::Array result;
166 result.reserve(size);
167 for (std::uint32_t i = 0; i < size; ++i) {
168 auto child = value(depth + 1);
169 if (!child) return child;
170 result.push_back(std::move(child).takeValue());
171 }
172 return Result<Value>::success(Value(std::move(result)));
173 }
174 Value::Object result;
175 std::string previous;
176 for (std::uint32_t i = 0; i < size; ++i) {
177 auto key = string();
178 if (!key) return Result<Value>::failure(key.status());
179 if (key.value().empty() || (!previous.empty() && key.value() <= previous))
181 "runtime definition object keys are not canonical", {},
182 {}, "asset.runtime-definition"));
183 previous = key.value();
184 auto child = value(depth + 1);
185 if (!child) return child;
186 result.emplace(std::move(key).takeValue(), std::move(child).takeValue());
187 }
188 return Result<Value>::success(Value(std::move(result)));
189 }
190
191 std::size_t cursor() const noexcept { return cursor_; }
192
193private:
194 bool byte(std::uint8_t& value) {
195 if (cursor_ == bytes_.size()) return false;
196 value = bytes_[cursor_++];
197 return true;
198 }
199 bool u32(std::uint32_t& value) {
200 if (bytes_.size() - cursor_ < 4) return false;
201 value = 0;
202 for (unsigned shift = 0; shift != 32; shift += 8)
203 value |= std::uint32_t(bytes_[cursor_++]) << shift;
204 return true;
205 }
206 bool u64(std::uint64_t& value) {
207 if (bytes_.size() - cursor_ < 8) return false;
208 value = 0;
209 for (unsigned shift = 0; shift != 64; shift += 8)
210 value |= std::uint64_t(bytes_[cursor_++]) << shift;
211 return true;
212 }
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);
220 cursor_ += size;
221 if (!isValidUtf8(result))
223 "runtime definition string is not valid UTF-8", {},
224 {}, "asset.runtime-definition"));
225 return Result<std::string>::success(std::move(result));
226 }
227
228 std::span<const std::uint8_t> bytes_;
229 const RuntimeDefinitionLimits& limits_;
230 std::size_t cursor_ = 0;
231 std::uint32_t count_ = 0;
232};
233
234} // namespace
235
237 const Value& value, const RuntimeDefinitionLimits& limits) {
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;
241 auto encoded = encodeValue(value, result, limits, 0, count);
242 if (!encoded) return Result<std::vector<std::uint8_t>>::failure(encoded.status());
243 return Result<std::vector<std::uint8_t>>::success(std::move(result));
244}
245
246Result<Value> decodeRuntimeDefinition(std::span<const std::uint8_t> bytes,
248 static constexpr std::uint8_t magic[] = {'E', 'V', 'D', 'E', 'F', 0, 1, 0};
249 if (bytes.size() < sizeof(magic) || bytes.size() > limits.maximumBytes ||
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))
259 DiagnosticCode::ParseError, "runtime definition has trailing bytes", {}, {}, "asset.runtime-definition"));
260 return result;
261}
262
263} // namespace eve::asset
double value
std::map< std::string, Var > values
std::uint32_t key
std::uint64_t bytes
const std::string * tag
graphics::Canvas * previous
JSON-free versioned runtime metadata codec.
std::string string
std::uint32_t count
std::size_t cursor
Json object
float size
Definition TreeMesh.cpp:156
std::uint32_t depth
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.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
std::map< std::string, Value > Object
Definition Value.h:34
std::vector< Value > Array
Definition Value.h:33
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.
Definition zip_writer.h:56
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
WidgetDesc child(std::string id, std::vector< WidgetDesc > children, float width, float height)
Scrollable child region with an explicit size.
Definition Widget.cpp:635
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.
Definition Runtime.h:119
enum EVENGINE_API_FOUNDATION Array
OT_ARRAY (editable via Runtime array APIs / ReflectedPropertyModel).
Definition Runtime.h:120
Structural and allocation limits for untrusted EVDEF metadata.