13 std::string
text()
const {
return {
reinterpret_cast<const char*
>(
bytes.data()),
bytes.size()}; }
17 explicit Reader(std::span<const uint8_t> data) : data_(data) {}
18 bool empty()
const {
return offset_ == data_.size(); }
21 for (
int shift = 0; shift < 70; shift += 7) {
23 const uint8_t
b = data_[offset_++];
25 n |=
static_cast<uint64_t
>(
b & 127) << shift;
26 if (!(
b & 128))
return n;
30 std::span<const uint8_t> take(uint64_t
n) {
32 auto result = data_.subspan(offset_,
static_cast<size_t>(
n));
33 offset_ +=
static_cast<size_t>(
n);
37 const uint64_t
tag = varint();
38 if ((
tag >> 3) == 0 || (
tag >> 3) > 0x1fffffff)
41 f.id =
static_cast<int>(
tag >> 3);
44 case 0:
f.number = varint();
break;
45 case 1:
f.bytes = take(8);
break;
46 case 2:
f.bytes = take(varint());
break;
47 case 5:
f.bytes = take(4);
break;
54 std::span<const uint8_t> data_;
57void wire(
const Field&
f,
int expected) {
60void integers(
const Field&
f, std::vector<int64_t>& out) {
62 out.push_back(std::bit_cast<int64_t>(
f.number));
66 while (!
r.empty()) out.push_back(std::bit_cast<int64_t>(
r.varint()));
69void reals(
const Field&
f, std::vector<float>& out) {
72 for (
size_t i = 0; i <
f.bytes.size(); i += 4) {
74 std::memcpy(&
x,
f.bytes.data() + i, 4);
78std::pair<std::string, RuntimeTensor>
tensor(std::span<const uint8_t> data) {
82 std::vector<int64_t> iv;
83 std::vector<float> fv;
84 bool raw =
false,
type =
false;
88 case 1: integers(
f,
v.shape);
break;
95 case 4: reals(
f, fv);
break;
97 case 7: integers(
f, iv);
break;
105 v.bytes.assign(
f.bytes.begin(),
f.bytes.end());
125 if (fv.size() !=
n || !iv.empty())
128 if (
n) std::memcpy(
v.bytes.data(), fv.data(),
v.bytes.size());
130 if (iv.size() !=
n || !fv.empty())
133 for (
size_t i = 0; i <
n; ++i) {
134 const auto x = iv[i];
140 std::memcpy(
v.bytes.data() + i *
size, &
x,
size);
145 return {std::move(
name), std::move(
v)};
147ModelData parseGraph(std::span<const uint8_t>
bytes,
size_t depth);
148std::pair<std::string, Attribute> attribute(std::span<const uint8_t>
bytes,
size_t depth) {
161 a.type =
static_cast<int>(
f.number);
165 std::memcpy(&
a.real,
f.bytes.data(), 4);
170 a.integer = std::bit_cast<int64_t>(
f.number);
180 case 7: reals(
f,
a.reals);
break;
181 case 8: integers(
f,
a.integers);
break;
184 a.strings.push_back(
f.text());
188 a.graph = std::make_shared<ModelData>(parseGraph(
f.bytes,
depth + 1));
196 return {std::move(
name), std::move(
a)};
203 if (
f.id >= 1 &&
f.id <= 5)
wire(
f, 2);
205 case 1:
n.inputs.push_back(
f.text());
break;
206 case 2:
n.outputs.push_back(
f.text());
break;
207 case 3:
n.name =
f.text();
break;
208 case 4:
n.op =
f.text();
break;
210 if (!
n.attrs.insert(attribute(
f.bytes,
depth)).second)
224std::pair<std::string, Input>
input(std::span<const uint8_t>
bytes) {
236 Reader
type(
f.bytes);
237 while (!
type.empty()) {
238 auto t =
type.next();
246 while (!tt.empty()) {
250 value.type =
static_cast<int>(
p.number);
255 while (!
shape.empty()) {
257 if (
d.id != 1)
continue;
261 while (!dim.empty()) {
265 v = std::bit_cast<int64_t>(
x.number);
281ModelData parseGraph(std::span<const uint8_t>
bytes,
size_t depth) {
295 t.second.bytes.retainAcrossRuns();
296 if (
t.first.empty() || !
model.constants.emplace(std::move(
t)).second)
302 model.info.inputs.push_back(
v.first);
303 if (!
model.inputs.emplace(std::move(
v)).second)
308 model.info.outputs.push_back(
input(
f.bytes).first);
317 if constexpr (std::endian::native != std::endian::little)
319 if (
bytes.empty() ||
bytes.size() > 512u * 1024u * 1024u)
323 std::span<const uint8_t>
graph;
328 model.info.irVersion =
static_cast<int64_t
>(
f.number);
340 while (!
ops.empty()) {
351 if (!
model.opsets.emplace(domain, version).second)
354 if (
f.id == 20 ||
f.id == 25 ||
f.id == 26)
357 if (
model.info.irVersion < 3 ||
model.info.irVersion > 10 || !
model.opsets.contains(
"") ||
358 model.opsets.at(
"") < 13 ||
model.opsets.at(
"") > 17)
360 if (
model.opsets.contains(
"com.microsoft") &&
model.opsets.at(
"com.microsoft") != 1)
363 auto parsed = parseGraph(
graph, 0);
364 model.nodes = std::move(parsed.nodes);
365 model.constants = std::move(parsed.constants);
366 model.inputs = std::move(parsed.inputs);
367 model.info.inputs = std::move(parsed.info.inputs);
368 model.info.outputs = std::move(parsed.info.outputs);
369 std::set<std::string> names;
370 for (
const auto& [
name,
v] :
model.constants) {
372 model.info.initializerBytes +=
v.bytes.size();
375 for (
const auto&
n :
model.nodes) {
377 for (
const auto&
name :
n.inputs)
378 if (!
name.empty() && !names.contains(
name))
380 for (
const auto&
name :
n.outputs)
381 if (!
name.empty() && !names.insert(
name).second)
383 if (!
isSupported(
n))
model.info.unsupportedNodes.push_back(
n.name +
" [" +
n.domain +
"::" +
n.op +
"]");
385 for (
const auto& out :
model.info.outputs)
387 size_t recursiveNodes = 0;
388 std::function<void(
const ModelData&,
const std::set<std::string>&,
bool)> validateNested;
389 validateNested = [&](
const ModelData&
g,
const std::set<std::string>& outer,
bool root) {
390 recursiveNodes +=
g.nodes.size();
392 auto available = outer;
393 std::set<std::string> locals;
394 for (
const auto& [
name,
t] :
g.constants) locals.insert(
name);
395 for (
const auto& [
name,
t] :
g.inputs) locals.insert(
name);
396 for (
const auto&
n :
g.nodes)
397 for (
const auto& out :
n.outputs)
398 if (!out.empty() && !locals.insert(out).second)
400 available.insert(locals.begin(), locals.end());
401 for (
const auto&
n :
g.nodes) {
402 if (!
model.opsets.contains(
n.domain))
404 for (
const auto&
input :
n.inputs)
405 if (!
input.empty() && !available.contains(
input))
408 model.info.unsupportedNodes.push_back(
n.name +
" [" +
n.domain +
"::" +
n.op +
"]");
409 for (
const auto& [
key,
a] :
n.attrs)
410 if (
a.graph) validateNested(*
a.graph, available,
false);
412 for (
const auto& out :
g.info.outputs)
415 validateNested(
model, {},
true);
const RuntimeTensor & tensor
std::map< std::string, std::vector< std::string > > graph
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
bool isSupported(const Node &n)
True when supported.
void validate(const RuntimeTensor &v)
Validate.
size_t elementSize(OnnxElement e)
Element size.
ModelData parse(std::span< const uint8_t > bytes)
Parse.
OnnxElement
ONNX wire element types; distinct from block-quantized Tensor storage.
DiagnosticCode
Stable machine-readable diagnostic codes.