sdn-flow 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/.claude/SKILLS.md +7 -0
  2. package/.claude/skills/sdn-plugin-abi-compliance/SKILL.md +56 -0
  3. package/.claude/todo/001-js-host-startup-and-deno.md +85 -0
  4. package/LICENSE +21 -0
  5. package/README.md +223 -0
  6. package/bin/sdn-flow-host.js +169 -0
  7. package/docs/.nojekyll +0 -0
  8. package/docs/ARCHITECTURE.md +200 -0
  9. package/docs/HOST_CAPABILITY_MODEL.md +317 -0
  10. package/docs/PLUGIN_ARCHITECTURE.md +145 -0
  11. package/docs/PLUGIN_COMPATIBILITY.md +61 -0
  12. package/docs/PLUGIN_COMPLIANCE_CHECKS.md +82 -0
  13. package/docs/PLUGIN_MANIFEST.md +94 -0
  14. package/docs/css/style.css +465 -0
  15. package/docs/index.html +218 -0
  16. package/docs/js/app.mjs +751 -0
  17. package/docs/js/editor-panel.mjs +203 -0
  18. package/docs/js/flow-canvas.mjs +515 -0
  19. package/docs/js/flow-model.mjs +391 -0
  20. package/docs/js/workers/emception.worker.js +146 -0
  21. package/docs/js/workers/pyodide.worker.js +134 -0
  22. package/native/flow_source_generator.cpp +1958 -0
  23. package/package.json +67 -0
  24. package/schemas/FlowRuntimeAbi.fbs +91 -0
  25. package/src/auth/canonicalize.js +5 -0
  26. package/src/auth/index.js +11 -0
  27. package/src/auth/permissions.js +8 -0
  28. package/src/compiler/CppFlowSourceGenerator.js +475 -0
  29. package/src/compiler/EmceptionCompilerAdapter.js +244 -0
  30. package/src/compiler/SignedArtifactCatalog.js +152 -0
  31. package/src/compiler/index.js +8 -0
  32. package/src/compiler/nativeFlowSourceGeneratorTool.js +144 -0
  33. package/src/compliance/index.js +13 -0
  34. package/src/compliance/pluginCompliance.js +11 -0
  35. package/src/deploy/FlowDeploymentClient.js +532 -0
  36. package/src/deploy/index.js +8 -0
  37. package/src/designer/FlowDesignerSession.js +158 -0
  38. package/src/designer/index.js +2 -0
  39. package/src/designer/requirements.js +184 -0
  40. package/src/generated/runtimeAbiLayouts.js +544 -0
  41. package/src/host/appHost.js +105 -0
  42. package/src/host/autoHost.js +113 -0
  43. package/src/host/browserHostAdapters.js +108 -0
  44. package/src/host/compiledFlowRuntimeHost.js +703 -0
  45. package/src/host/constants.js +55 -0
  46. package/src/host/dependencyRuntime.js +227 -0
  47. package/src/host/descriptorAbi.js +351 -0
  48. package/src/host/fetchService.js +237 -0
  49. package/src/host/httpHostAdapters.js +280 -0
  50. package/src/host/index.js +91 -0
  51. package/src/host/installedFlowHost.js +885 -0
  52. package/src/host/invocationAbi.js +440 -0
  53. package/src/host/normalize.js +372 -0
  54. package/src/host/packageManagers.js +369 -0
  55. package/src/host/profile.js +134 -0
  56. package/src/host/runtimeAbi.js +106 -0
  57. package/src/host/workspace.js +895 -0
  58. package/src/index.js +8 -0
  59. package/src/runtime/FlowRuntime.js +273 -0
  60. package/src/runtime/MethodRegistry.js +295 -0
  61. package/src/runtime/constants.js +44 -0
  62. package/src/runtime/index.js +19 -0
  63. package/src/runtime/normalize.js +377 -0
  64. package/src/transport/index.js +7 -0
  65. package/src/transport/pki.js +7 -0
  66. package/src/utils/crypto.js +7 -0
  67. package/src/utils/encoding.js +65 -0
  68. package/src/utils/wasmCrypto.js +69 -0
  69. package/tools/run-plugin-compliance-check.mjs +153 -0
@@ -0,0 +1,1958 @@
1
+ #include <cctype>
2
+ #include <cstddef>
3
+ #include <cstdint>
4
+ #include <filesystem>
5
+ #include <fstream>
6
+ #include <iomanip>
7
+ #include <iostream>
8
+ #include <sstream>
9
+ #include <stdexcept>
10
+ #include <string>
11
+ #include <string_view>
12
+ #include <vector>
13
+
14
+ namespace {
15
+
16
+ constexpr std::uint32_t kInvalidIndex = 0xffffffffu;
17
+ constexpr char kRequestMagic[] = "SDNFLOWCPPGEN1";
18
+
19
+ struct TypeDescriptor {
20
+ std::string schema_name;
21
+ std::string file_identifier;
22
+ std::string schema_hash_hex;
23
+ bool accepts_any_flatbuffer = false;
24
+ };
25
+
26
+ struct TriggerDescriptor {
27
+ std::string trigger_id;
28
+ std::string kind;
29
+ std::string source;
30
+ std::string protocol_id;
31
+ std::uint32_t default_interval_ms = 0;
32
+ std::uint32_t accepted_type_index_offset = 0;
33
+ std::uint32_t accepted_type_index_count = 0;
34
+ std::string description;
35
+ };
36
+
37
+ struct NodeDescriptor {
38
+ std::string node_id;
39
+ std::string plugin_id;
40
+ std::string method_id;
41
+ std::string kind;
42
+ std::string drain_policy;
43
+ std::uint32_t time_slice_micros = 0;
44
+ std::uint32_t ingress_index_offset = 0;
45
+ std::uint32_t ingress_index_count = 0;
46
+ };
47
+
48
+ struct EdgeDescriptor {
49
+ std::string edge_id;
50
+ std::string from_node_id;
51
+ std::uint32_t from_node_index = kInvalidIndex;
52
+ std::string from_port_id;
53
+ std::string to_node_id;
54
+ std::uint32_t to_node_index = kInvalidIndex;
55
+ std::string to_port_id;
56
+ std::string backpressure_policy;
57
+ std::uint32_t queue_depth = 0;
58
+ std::uint32_t accepted_type_index_offset = 0;
59
+ std::uint32_t accepted_type_index_count = 0;
60
+ std::uint32_t target_ingress_index = kInvalidIndex;
61
+ };
62
+
63
+ struct TriggerBindingDescriptor {
64
+ std::string trigger_id;
65
+ std::uint32_t trigger_index = kInvalidIndex;
66
+ std::string target_node_id;
67
+ std::uint32_t target_node_index = kInvalidIndex;
68
+ std::string target_port_id;
69
+ std::string backpressure_policy;
70
+ std::uint32_t queue_depth = 0;
71
+ std::uint32_t target_ingress_index = kInvalidIndex;
72
+ };
73
+
74
+ struct IngressDescriptor {
75
+ std::string ingress_id;
76
+ std::string source_kind;
77
+ std::uint32_t source_index = kInvalidIndex;
78
+ std::uint32_t source_node_index = kInvalidIndex;
79
+ std::string source_port_id;
80
+ std::uint32_t target_node_index = kInvalidIndex;
81
+ std::string target_node_id;
82
+ std::string target_port_id;
83
+ std::string backpressure_policy;
84
+ std::uint32_t queue_depth = 0;
85
+ };
86
+
87
+ struct ExternalInterfaceDescriptor {
88
+ std::string interface_id;
89
+ std::string kind;
90
+ std::string direction;
91
+ std::string capability;
92
+ std::string resource;
93
+ std::string protocol_id;
94
+ std::string topic;
95
+ std::string path;
96
+ bool required = true;
97
+ std::uint32_t accepted_type_index_offset = 0;
98
+ std::uint32_t accepted_type_index_count = 0;
99
+ std::string description;
100
+ };
101
+
102
+ struct SignedArtifactDependency {
103
+ std::string dependency_id;
104
+ std::string plugin_id;
105
+ std::string version;
106
+ std::string sha256;
107
+ std::string signature;
108
+ std::string signer_public_key;
109
+ std::string entrypoint;
110
+ std::string manifest_bytes_symbol;
111
+ std::string manifest_size_symbol;
112
+ std::string init_symbol;
113
+ std::string destroy_symbol;
114
+ std::string malloc_symbol;
115
+ std::string free_symbol;
116
+ std::string stream_invoke_symbol;
117
+ std::vector<std::uint8_t> wasm_bytes;
118
+ std::vector<std::uint8_t> manifest_bytes;
119
+ };
120
+
121
+ struct Request {
122
+ std::string namespace_name;
123
+ std::vector<std::uint8_t> manifest_buffer;
124
+ std::string program_id;
125
+ std::string program_name;
126
+ std::string program_version;
127
+ std::string program_description;
128
+ std::vector<std::string> required_plugins;
129
+ std::vector<TypeDescriptor> type_descriptors;
130
+ std::vector<std::uint32_t> accepted_type_indices;
131
+ std::vector<TriggerDescriptor> triggers;
132
+ std::vector<NodeDescriptor> nodes;
133
+ std::vector<EdgeDescriptor> edges;
134
+ std::vector<TriggerBindingDescriptor> trigger_bindings;
135
+ std::vector<IngressDescriptor> ingress_descriptors;
136
+ std::vector<ExternalInterfaceDescriptor> external_interfaces;
137
+ std::vector<SignedArtifactDependency> dependencies;
138
+ std::vector<std::uint32_t> node_ingress_indices;
139
+ };
140
+
141
+ class BinaryReader {
142
+ public:
143
+ explicit BinaryReader(std::vector<std::uint8_t> bytes)
144
+ : bytes_(std::move(bytes)) {}
145
+
146
+ void expectMagic() {
147
+ const std::string magic = readString();
148
+ if (magic != kRequestMagic) {
149
+ throw std::runtime_error("invalid flow source generator request header");
150
+ }
151
+ }
152
+
153
+ std::uint8_t readU8() {
154
+ ensureAvailable(1);
155
+ return bytes_[offset_++];
156
+ }
157
+
158
+ bool readBool() { return readU8() != 0; }
159
+
160
+ std::uint32_t readU32() {
161
+ ensureAvailable(4);
162
+ const std::uint32_t value =
163
+ static_cast<std::uint32_t>(bytes_[offset_]) |
164
+ (static_cast<std::uint32_t>(bytes_[offset_ + 1]) << 8u) |
165
+ (static_cast<std::uint32_t>(bytes_[offset_ + 2]) << 16u) |
166
+ (static_cast<std::uint32_t>(bytes_[offset_ + 3]) << 24u);
167
+ offset_ += 4;
168
+ return value;
169
+ }
170
+
171
+ std::string readString() {
172
+ const auto data = readBytes();
173
+ return std::string(data.begin(), data.end());
174
+ }
175
+
176
+ std::vector<std::uint8_t> readBytes() {
177
+ const std::uint32_t size = readU32();
178
+ ensureAvailable(size);
179
+ std::vector<std::uint8_t> result(bytes_.begin() + offset_,
180
+ bytes_.begin() + offset_ + size);
181
+ offset_ += size;
182
+ return result;
183
+ }
184
+
185
+ bool hasRemaining() const { return offset_ < bytes_.size(); }
186
+
187
+ private:
188
+ void ensureAvailable(std::size_t count) const {
189
+ if (offset_ + count > bytes_.size()) {
190
+ throw std::runtime_error("unexpected end of flow generator request");
191
+ }
192
+ }
193
+
194
+ std::vector<std::uint8_t> bytes_;
195
+ std::size_t offset_ = 0;
196
+ };
197
+
198
+ std::vector<std::uint8_t> readFileBytes(const std::filesystem::path& path) {
199
+ std::ifstream input(path, std::ios::binary);
200
+ if (!input) {
201
+ throw std::runtime_error("failed to open input request file");
202
+ }
203
+ input.seekg(0, std::ios::end);
204
+ const auto size = static_cast<std::size_t>(input.tellg());
205
+ input.seekg(0, std::ios::beg);
206
+ std::vector<std::uint8_t> buffer(size);
207
+ if (size > 0) {
208
+ input.read(reinterpret_cast<char*>(buffer.data()),
209
+ static_cast<std::streamsize>(buffer.size()));
210
+ }
211
+ return buffer;
212
+ }
213
+
214
+ void writeFileString(const std::filesystem::path& path, const std::string& text) {
215
+ std::ofstream output(path, std::ios::binary | std::ios::trunc);
216
+ if (!output) {
217
+ throw std::runtime_error("failed to open output source file");
218
+ }
219
+ output.write(text.data(), static_cast<std::streamsize>(text.size()));
220
+ }
221
+
222
+ std::string cppStringLiteral(std::string_view value) {
223
+ std::ostringstream out;
224
+ out << '"';
225
+ for (const unsigned char ch : value) {
226
+ switch (ch) {
227
+ case '\\':
228
+ out << "\\\\";
229
+ break;
230
+ case '"':
231
+ out << "\\\"";
232
+ break;
233
+ case '\n':
234
+ out << "\\n";
235
+ break;
236
+ case '\r':
237
+ out << "\\r";
238
+ break;
239
+ case '\t':
240
+ out << "\\t";
241
+ break;
242
+ default:
243
+ if (std::isprint(ch) != 0) {
244
+ out << static_cast<char>(ch);
245
+ } else {
246
+ out << "\\x" << std::hex << std::setw(2) << std::setfill('0')
247
+ << static_cast<int>(ch) << std::dec << std::setfill(' ');
248
+ }
249
+ break;
250
+ }
251
+ }
252
+ out << '"';
253
+ return out.str();
254
+ }
255
+
256
+ std::string cppBoolLiteral(bool value) { return value ? "true" : "false"; }
257
+
258
+ std::string formatUnsigned(std::uint32_t value) {
259
+ return std::to_string(value) + "u";
260
+ }
261
+
262
+ std::string formatIndex(std::uint32_t value) {
263
+ if (value == kInvalidIndex) {
264
+ return "kInvalidIndex";
265
+ }
266
+ return std::to_string(value) + "u";
267
+ }
268
+
269
+ std::string sanitizeIdentifier(std::string_view value, std::string_view fallback) {
270
+ std::string normalized;
271
+ normalized.reserve(value.size());
272
+ for (const unsigned char ch : value) {
273
+ if (std::isalnum(ch) != 0 || ch == '_') {
274
+ normalized.push_back(static_cast<char>(ch));
275
+ } else {
276
+ normalized.push_back('_');
277
+ }
278
+ }
279
+ if (normalized.empty()) {
280
+ normalized.assign(fallback);
281
+ }
282
+ if (!(std::isalpha(static_cast<unsigned char>(normalized.front())) != 0 ||
283
+ normalized.front() == '_')) {
284
+ normalized.insert(normalized.begin(), '_');
285
+ }
286
+ while (normalized.find("__") != std::string::npos) {
287
+ normalized.replace(normalized.find("__"), 2, "_");
288
+ }
289
+ bool only_underscores = true;
290
+ for (const char ch : normalized) {
291
+ if (ch != '_') {
292
+ only_underscores = false;
293
+ break;
294
+ }
295
+ }
296
+ if (only_underscores) {
297
+ normalized.assign(fallback);
298
+ }
299
+ return normalized;
300
+ }
301
+
302
+ std::string renderByteArray(std::string_view symbol_name,
303
+ const std::vector<std::uint8_t>& bytes) {
304
+ std::ostringstream out;
305
+ if (bytes.empty()) {
306
+ out << "static const std::uint8_t " << symbol_name
307
+ << "[] = { 0x00 };";
308
+ return out.str();
309
+ }
310
+ out << "static const std::uint8_t " << symbol_name << "[] = {\n";
311
+ for (std::size_t index = 0; index < bytes.size(); index += 12) {
312
+ out << " ";
313
+ const auto stop = std::min<std::size_t>(bytes.size(), index + 12);
314
+ for (std::size_t cursor = index; cursor < stop; ++cursor) {
315
+ if (cursor > index) {
316
+ out << ", ";
317
+ }
318
+ out << "0x" << std::hex << std::setw(2) << std::setfill('0')
319
+ << static_cast<int>(bytes[cursor]) << std::dec << std::setfill(' ');
320
+ }
321
+ if (stop < bytes.size()) {
322
+ out << ',';
323
+ }
324
+ out << '\n';
325
+ }
326
+ out << "};";
327
+ return out.str();
328
+ }
329
+
330
+ std::string renderRecordArray(std::string_view type_name,
331
+ std::string_view symbol_name,
332
+ const std::vector<std::string>& records,
333
+ std::string_view empty_initializer = "{}") {
334
+ std::ostringstream out;
335
+ if (records.empty()) {
336
+ out << "static const " << type_name << ' ' << symbol_name << "[] = { "
337
+ << empty_initializer << " };";
338
+ return out.str();
339
+ }
340
+ out << "static const " << type_name << ' ' << symbol_name << "[] = {\n";
341
+ for (std::size_t index = 0; index < records.size(); ++index) {
342
+ out << records[index];
343
+ if (index + 1 < records.size()) {
344
+ out << ',';
345
+ }
346
+ out << '\n';
347
+ }
348
+ out << "};";
349
+ return out.str();
350
+ }
351
+
352
+ std::string renderMutableRecordArray(std::string_view type_name,
353
+ std::string_view symbol_name,
354
+ std::size_t count) {
355
+ std::ostringstream out;
356
+ if (count == 0) {
357
+ out << "static " << type_name << ' ' << symbol_name << "[] = { {} };";
358
+ return out.str();
359
+ }
360
+ out << "static " << type_name << ' ' << symbol_name << '[' << count
361
+ << "] = {};";
362
+ return out.str();
363
+ }
364
+
365
+ std::string renderStringPointerArray(std::string_view symbol_name,
366
+ const std::vector<std::string>& values) {
367
+ std::ostringstream out;
368
+ if (values.empty()) {
369
+ out << "static const char * " << symbol_name << "[] = { nullptr };";
370
+ return out.str();
371
+ }
372
+ out << "static const char * " << symbol_name << "[] = {\n";
373
+ for (std::size_t index = 0; index < values.size(); ++index) {
374
+ out << " " << cppStringLiteral(values[index]);
375
+ if (index + 1 < values.size()) {
376
+ out << ',';
377
+ }
378
+ out << '\n';
379
+ }
380
+ out << "};";
381
+ return out.str();
382
+ }
383
+
384
+ std::string renderIntegerArray(std::string_view type_name,
385
+ std::string_view symbol_name,
386
+ const std::vector<std::uint32_t>& values) {
387
+ std::ostringstream out;
388
+ if (values.empty()) {
389
+ out << "static const " << type_name << ' ' << symbol_name
390
+ << "[] = { 0 };";
391
+ return out.str();
392
+ }
393
+ out << "static const " << type_name << ' ' << symbol_name << "[] = {\n";
394
+ for (std::size_t index = 0; index < values.size(); ++index) {
395
+ out << " " << values[index] << 'u';
396
+ if (index + 1 < values.size()) {
397
+ out << ',';
398
+ }
399
+ out << '\n';
400
+ }
401
+ out << "};";
402
+ return out.str();
403
+ }
404
+
405
+ Request readRequest(BinaryReader& reader) {
406
+ Request request;
407
+ reader.expectMagic();
408
+ request.namespace_name = reader.readString();
409
+ request.manifest_buffer = reader.readBytes();
410
+ request.program_id = reader.readString();
411
+ request.program_name = reader.readString();
412
+ request.program_version = reader.readString();
413
+ request.program_description = reader.readString();
414
+
415
+ const std::uint32_t required_plugin_count = reader.readU32();
416
+ request.required_plugins.reserve(required_plugin_count);
417
+ for (std::uint32_t index = 0; index < required_plugin_count; ++index) {
418
+ request.required_plugins.push_back(reader.readString());
419
+ }
420
+
421
+ const std::uint32_t type_descriptor_count = reader.readU32();
422
+ request.type_descriptors.reserve(type_descriptor_count);
423
+ for (std::uint32_t index = 0; index < type_descriptor_count; ++index) {
424
+ TypeDescriptor descriptor;
425
+ descriptor.schema_name = reader.readString();
426
+ descriptor.file_identifier = reader.readString();
427
+ descriptor.schema_hash_hex = reader.readString();
428
+ descriptor.accepts_any_flatbuffer = reader.readBool();
429
+ request.type_descriptors.push_back(std::move(descriptor));
430
+ }
431
+
432
+ const std::uint32_t accepted_type_index_count = reader.readU32();
433
+ request.accepted_type_indices.reserve(accepted_type_index_count);
434
+ for (std::uint32_t index = 0; index < accepted_type_index_count; ++index) {
435
+ request.accepted_type_indices.push_back(reader.readU32());
436
+ }
437
+
438
+ const std::uint32_t trigger_count = reader.readU32();
439
+ request.triggers.reserve(trigger_count);
440
+ for (std::uint32_t index = 0; index < trigger_count; ++index) {
441
+ TriggerDescriptor descriptor;
442
+ descriptor.trigger_id = reader.readString();
443
+ descriptor.kind = reader.readString();
444
+ descriptor.source = reader.readString();
445
+ descriptor.protocol_id = reader.readString();
446
+ descriptor.default_interval_ms = reader.readU32();
447
+ descriptor.accepted_type_index_offset = reader.readU32();
448
+ descriptor.accepted_type_index_count = reader.readU32();
449
+ descriptor.description = reader.readString();
450
+ request.triggers.push_back(std::move(descriptor));
451
+ }
452
+
453
+ const std::uint32_t node_count = reader.readU32();
454
+ request.nodes.reserve(node_count);
455
+ for (std::uint32_t index = 0; index < node_count; ++index) {
456
+ NodeDescriptor descriptor;
457
+ descriptor.node_id = reader.readString();
458
+ descriptor.plugin_id = reader.readString();
459
+ descriptor.method_id = reader.readString();
460
+ descriptor.kind = reader.readString();
461
+ descriptor.drain_policy = reader.readString();
462
+ descriptor.time_slice_micros = reader.readU32();
463
+ descriptor.ingress_index_offset = reader.readU32();
464
+ descriptor.ingress_index_count = reader.readU32();
465
+ request.nodes.push_back(std::move(descriptor));
466
+ }
467
+
468
+ const std::uint32_t edge_count = reader.readU32();
469
+ request.edges.reserve(edge_count);
470
+ for (std::uint32_t index = 0; index < edge_count; ++index) {
471
+ EdgeDescriptor descriptor;
472
+ descriptor.edge_id = reader.readString();
473
+ descriptor.from_node_id = reader.readString();
474
+ descriptor.from_node_index = reader.readU32();
475
+ descriptor.from_port_id = reader.readString();
476
+ descriptor.to_node_id = reader.readString();
477
+ descriptor.to_node_index = reader.readU32();
478
+ descriptor.to_port_id = reader.readString();
479
+ descriptor.backpressure_policy = reader.readString();
480
+ descriptor.queue_depth = reader.readU32();
481
+ descriptor.accepted_type_index_offset = reader.readU32();
482
+ descriptor.accepted_type_index_count = reader.readU32();
483
+ descriptor.target_ingress_index = reader.readU32();
484
+ request.edges.push_back(std::move(descriptor));
485
+ }
486
+
487
+ const std::uint32_t trigger_binding_count = reader.readU32();
488
+ request.trigger_bindings.reserve(trigger_binding_count);
489
+ for (std::uint32_t index = 0; index < trigger_binding_count; ++index) {
490
+ TriggerBindingDescriptor descriptor;
491
+ descriptor.trigger_id = reader.readString();
492
+ descriptor.trigger_index = reader.readU32();
493
+ descriptor.target_node_id = reader.readString();
494
+ descriptor.target_node_index = reader.readU32();
495
+ descriptor.target_port_id = reader.readString();
496
+ descriptor.backpressure_policy = reader.readString();
497
+ descriptor.queue_depth = reader.readU32();
498
+ descriptor.target_ingress_index = reader.readU32();
499
+ request.trigger_bindings.push_back(std::move(descriptor));
500
+ }
501
+
502
+ const std::uint32_t ingress_count = reader.readU32();
503
+ request.ingress_descriptors.reserve(ingress_count);
504
+ for (std::uint32_t index = 0; index < ingress_count; ++index) {
505
+ IngressDescriptor descriptor;
506
+ descriptor.ingress_id = reader.readString();
507
+ descriptor.source_kind = reader.readString();
508
+ descriptor.source_index = reader.readU32();
509
+ descriptor.source_node_index = reader.readU32();
510
+ descriptor.source_port_id = reader.readString();
511
+ descriptor.target_node_index = reader.readU32();
512
+ descriptor.target_node_id = reader.readString();
513
+ descriptor.target_port_id = reader.readString();
514
+ descriptor.backpressure_policy = reader.readString();
515
+ descriptor.queue_depth = reader.readU32();
516
+ request.ingress_descriptors.push_back(std::move(descriptor));
517
+ }
518
+
519
+ const std::uint32_t external_interface_count = reader.readU32();
520
+ request.external_interfaces.reserve(external_interface_count);
521
+ for (std::uint32_t index = 0; index < external_interface_count; ++index) {
522
+ ExternalInterfaceDescriptor descriptor;
523
+ descriptor.interface_id = reader.readString();
524
+ descriptor.kind = reader.readString();
525
+ descriptor.direction = reader.readString();
526
+ descriptor.capability = reader.readString();
527
+ descriptor.resource = reader.readString();
528
+ descriptor.protocol_id = reader.readString();
529
+ descriptor.topic = reader.readString();
530
+ descriptor.path = reader.readString();
531
+ descriptor.required = reader.readBool();
532
+ descriptor.accepted_type_index_offset = reader.readU32();
533
+ descriptor.accepted_type_index_count = reader.readU32();
534
+ descriptor.description = reader.readString();
535
+ request.external_interfaces.push_back(std::move(descriptor));
536
+ }
537
+
538
+ const std::uint32_t dependency_count = reader.readU32();
539
+ request.dependencies.reserve(dependency_count);
540
+ for (std::uint32_t index = 0; index < dependency_count; ++index) {
541
+ SignedArtifactDependency dependency;
542
+ dependency.dependency_id = reader.readString();
543
+ dependency.plugin_id = reader.readString();
544
+ dependency.version = reader.readString();
545
+ dependency.sha256 = reader.readString();
546
+ dependency.signature = reader.readString();
547
+ dependency.signer_public_key = reader.readString();
548
+ dependency.entrypoint = reader.readString();
549
+ dependency.manifest_bytes_symbol = reader.readString();
550
+ dependency.manifest_size_symbol = reader.readString();
551
+ dependency.init_symbol = reader.readString();
552
+ dependency.destroy_symbol = reader.readString();
553
+ dependency.malloc_symbol = reader.readString();
554
+ dependency.free_symbol = reader.readString();
555
+ dependency.stream_invoke_symbol = reader.readString();
556
+ dependency.wasm_bytes = reader.readBytes();
557
+ dependency.manifest_bytes = reader.readBytes();
558
+ request.dependencies.push_back(std::move(dependency));
559
+ }
560
+
561
+ const std::uint32_t node_ingress_index_count = reader.readU32();
562
+ request.node_ingress_indices.reserve(node_ingress_index_count);
563
+ for (std::uint32_t index = 0; index < node_ingress_index_count; ++index) {
564
+ request.node_ingress_indices.push_back(reader.readU32());
565
+ }
566
+
567
+ if (reader.hasRemaining()) {
568
+ throw std::runtime_error("unexpected trailing bytes in generator request");
569
+ }
570
+
571
+ if (request.manifest_buffer.empty()) {
572
+ throw std::runtime_error("flow generator request requires manifest bytes");
573
+ }
574
+
575
+ return request;
576
+ }
577
+
578
+ std::string generateSource(const Request& request) {
579
+ std::vector<std::string> type_descriptor_records;
580
+ type_descriptor_records.reserve(request.type_descriptors.size());
581
+ for (const auto& descriptor : request.type_descriptors) {
582
+ std::ostringstream record;
583
+ record << " {\n"
584
+ << " " << cppStringLiteral(descriptor.schema_name) << ",\n"
585
+ << " " << cppStringLiteral(descriptor.file_identifier) << ",\n"
586
+ << " " << cppStringLiteral(descriptor.schema_hash_hex) << ",\n"
587
+ << " " << cppBoolLiteral(descriptor.accepts_any_flatbuffer)
588
+ << "\n"
589
+ << " }";
590
+ type_descriptor_records.push_back(record.str());
591
+ }
592
+
593
+ std::vector<std::string> trigger_records;
594
+ trigger_records.reserve(request.triggers.size());
595
+ for (const auto& trigger : request.triggers) {
596
+ std::ostringstream record;
597
+ record << " {\n"
598
+ << " " << cppStringLiteral(trigger.trigger_id) << ",\n"
599
+ << " " << cppStringLiteral(trigger.kind) << ",\n"
600
+ << " " << cppStringLiteral(trigger.source) << ",\n"
601
+ << " " << cppStringLiteral(trigger.protocol_id) << ",\n"
602
+ << " " << formatUnsigned(trigger.default_interval_ms) << ",\n"
603
+ << " " << formatUnsigned(trigger.accepted_type_index_offset)
604
+ << ",\n"
605
+ << " " << formatUnsigned(trigger.accepted_type_index_count)
606
+ << ",\n"
607
+ << " " << cppStringLiteral(trigger.description) << "\n"
608
+ << " }";
609
+ trigger_records.push_back(record.str());
610
+ }
611
+
612
+ std::vector<std::string> node_records;
613
+ node_records.reserve(request.nodes.size());
614
+ for (const auto& node : request.nodes) {
615
+ std::ostringstream record;
616
+ record << " {\n"
617
+ << " " << cppStringLiteral(node.node_id) << ",\n"
618
+ << " " << cppStringLiteral(node.plugin_id) << ",\n"
619
+ << " " << cppStringLiteral(node.method_id) << ",\n"
620
+ << " " << cppStringLiteral(node.kind) << ",\n"
621
+ << " " << cppStringLiteral(node.drain_policy) << ",\n"
622
+ << " " << formatUnsigned(node.time_slice_micros) << ",\n"
623
+ << " " << formatUnsigned(node.ingress_index_offset) << ",\n"
624
+ << " " << formatUnsigned(node.ingress_index_count) << "\n"
625
+ << " }";
626
+ node_records.push_back(record.str());
627
+ }
628
+
629
+ std::vector<std::string> node_dispatch_records;
630
+ node_dispatch_records.reserve(request.nodes.size());
631
+ for (std::size_t node_index = 0; node_index < request.nodes.size(); ++node_index) {
632
+ const auto& node = request.nodes[node_index];
633
+ std::uint32_t dependency_index = kInvalidIndex;
634
+ const SignedArtifactDependency* dependency = nullptr;
635
+ for (std::size_t candidate_index = 0; candidate_index < request.dependencies.size();
636
+ ++candidate_index) {
637
+ if (request.dependencies[candidate_index].plugin_id == node.plugin_id) {
638
+ dependency_index = static_cast<std::uint32_t>(candidate_index);
639
+ dependency = &request.dependencies[candidate_index];
640
+ break;
641
+ }
642
+ }
643
+
644
+ const std::string dependency_id =
645
+ dependency != nullptr ? dependency->dependency_id : "";
646
+ const std::string dispatch_model =
647
+ dependency == nullptr
648
+ ? "unresolved"
649
+ : (!dependency->stream_invoke_symbol.empty() ? "stream-invoke"
650
+ : "unresolved");
651
+
652
+ std::ostringstream record;
653
+ record << " {\n"
654
+ << " " << cppStringLiteral(node.node_id) << ",\n"
655
+ << " " << formatUnsigned(static_cast<std::uint32_t>(node_index))
656
+ << ",\n"
657
+ << " " << cppStringLiteral(dependency_id) << ",\n"
658
+ << " " << formatIndex(dependency_index) << ",\n"
659
+ << " " << cppStringLiteral(node.plugin_id) << ",\n"
660
+ << " " << cppStringLiteral(node.method_id) << ",\n"
661
+ << " " << cppStringLiteral(dispatch_model) << ",\n"
662
+ << " "
663
+ << cppStringLiteral(dependency != nullptr ? dependency->entrypoint : "")
664
+ << ",\n"
665
+ << " "
666
+ << cppStringLiteral(dependency != nullptr
667
+ ? dependency->manifest_bytes_symbol
668
+ : "")
669
+ << ",\n"
670
+ << " "
671
+ << cppStringLiteral(dependency != nullptr
672
+ ? dependency->manifest_size_symbol
673
+ : "")
674
+ << ",\n"
675
+ << " "
676
+ << cppStringLiteral(dependency != nullptr ? dependency->init_symbol
677
+ : "")
678
+ << ",\n"
679
+ << " "
680
+ << cppStringLiteral(dependency != nullptr
681
+ ? dependency->destroy_symbol
682
+ : "")
683
+ << ",\n"
684
+ << " "
685
+ << cppStringLiteral(dependency != nullptr ? dependency->malloc_symbol
686
+ : "")
687
+ << ",\n"
688
+ << " "
689
+ << cppStringLiteral(dependency != nullptr ? dependency->free_symbol
690
+ : "")
691
+ << ",\n"
692
+ << " "
693
+ << cppStringLiteral(dependency != nullptr
694
+ ? dependency->stream_invoke_symbol
695
+ : "")
696
+ << "\n"
697
+ << " }";
698
+ node_dispatch_records.push_back(record.str());
699
+ }
700
+
701
+ std::vector<std::string> edge_records;
702
+ edge_records.reserve(request.edges.size());
703
+ for (const auto& edge : request.edges) {
704
+ std::ostringstream record;
705
+ record << " {\n"
706
+ << " " << cppStringLiteral(edge.edge_id) << ",\n"
707
+ << " " << cppStringLiteral(edge.from_node_id) << ",\n"
708
+ << " " << formatIndex(edge.from_node_index) << ",\n"
709
+ << " " << cppStringLiteral(edge.from_port_id) << ",\n"
710
+ << " " << cppStringLiteral(edge.to_node_id) << ",\n"
711
+ << " " << formatIndex(edge.to_node_index) << ",\n"
712
+ << " " << cppStringLiteral(edge.to_port_id) << ",\n"
713
+ << " " << cppStringLiteral(edge.backpressure_policy) << ",\n"
714
+ << " " << formatUnsigned(edge.queue_depth) << ",\n"
715
+ << " " << formatUnsigned(edge.accepted_type_index_offset)
716
+ << ",\n"
717
+ << " " << formatUnsigned(edge.accepted_type_index_count)
718
+ << ",\n"
719
+ << " " << formatIndex(edge.target_ingress_index) << "\n"
720
+ << " }";
721
+ edge_records.push_back(record.str());
722
+ }
723
+
724
+ std::vector<std::string> trigger_binding_records;
725
+ trigger_binding_records.reserve(request.trigger_bindings.size());
726
+ for (const auto& binding : request.trigger_bindings) {
727
+ std::ostringstream record;
728
+ record << " {\n"
729
+ << " " << cppStringLiteral(binding.trigger_id) << ",\n"
730
+ << " " << formatIndex(binding.trigger_index) << ",\n"
731
+ << " " << cppStringLiteral(binding.target_node_id) << ",\n"
732
+ << " " << formatIndex(binding.target_node_index) << ",\n"
733
+ << " " << cppStringLiteral(binding.target_port_id) << ",\n"
734
+ << " " << cppStringLiteral(binding.backpressure_policy) << ",\n"
735
+ << " " << formatUnsigned(binding.queue_depth) << ",\n"
736
+ << " " << formatIndex(binding.target_ingress_index) << "\n"
737
+ << " }";
738
+ trigger_binding_records.push_back(record.str());
739
+ }
740
+
741
+ std::vector<std::string> ingress_records;
742
+ ingress_records.reserve(request.ingress_descriptors.size());
743
+ for (const auto& ingress : request.ingress_descriptors) {
744
+ std::ostringstream record;
745
+ record << " {\n"
746
+ << " " << cppStringLiteral(ingress.ingress_id) << ",\n"
747
+ << " " << cppStringLiteral(ingress.source_kind) << ",\n"
748
+ << " " << formatIndex(ingress.source_index) << ",\n"
749
+ << " " << formatIndex(ingress.source_node_index) << ",\n"
750
+ << " " << cppStringLiteral(ingress.source_port_id) << ",\n"
751
+ << " " << formatIndex(ingress.target_node_index) << ",\n"
752
+ << " " << cppStringLiteral(ingress.target_node_id) << ",\n"
753
+ << " " << cppStringLiteral(ingress.target_port_id) << ",\n"
754
+ << " " << cppStringLiteral(ingress.backpressure_policy) << ",\n"
755
+ << " " << formatUnsigned(ingress.queue_depth) << "\n"
756
+ << " }";
757
+ ingress_records.push_back(record.str());
758
+ }
759
+
760
+ std::vector<std::string> external_interface_records;
761
+ external_interface_records.reserve(request.external_interfaces.size());
762
+ for (const auto& external_interface : request.external_interfaces) {
763
+ std::ostringstream record;
764
+ record << " {\n"
765
+ << " " << cppStringLiteral(external_interface.interface_id)
766
+ << ",\n"
767
+ << " " << cppStringLiteral(external_interface.kind) << ",\n"
768
+ << " " << cppStringLiteral(external_interface.direction)
769
+ << ",\n"
770
+ << " " << cppStringLiteral(external_interface.capability)
771
+ << ",\n"
772
+ << " " << cppStringLiteral(external_interface.resource) << ",\n"
773
+ << " " << cppStringLiteral(external_interface.protocol_id)
774
+ << ",\n"
775
+ << " " << cppStringLiteral(external_interface.topic) << ",\n"
776
+ << " " << cppStringLiteral(external_interface.path) << ",\n"
777
+ << " " << cppBoolLiteral(external_interface.required) << ",\n"
778
+ << " " << formatUnsigned(external_interface.accepted_type_index_offset)
779
+ << ",\n"
780
+ << " " << formatUnsigned(external_interface.accepted_type_index_count)
781
+ << ",\n"
782
+ << " " << cppStringLiteral(external_interface.description) << "\n"
783
+ << " }";
784
+ external_interface_records.push_back(record.str());
785
+ }
786
+
787
+ std::vector<std::string> dependency_blocks;
788
+ std::vector<std::string> dependency_records;
789
+ dependency_blocks.reserve(request.dependencies.size() * 2);
790
+ dependency_records.reserve(request.dependencies.size());
791
+ for (std::size_t index = 0; index < request.dependencies.size(); ++index) {
792
+ const auto& dependency = request.dependencies[index];
793
+ const std::string dependency_name = sanitizeIdentifier(
794
+ !dependency.dependency_id.empty()
795
+ ? dependency.dependency_id
796
+ : (!dependency.plugin_id.empty()
797
+ ? dependency.plugin_id
798
+ : std::string("dependency_") + std::to_string(index)),
799
+ std::string("dependency_") + std::to_string(index));
800
+ const std::string wasm_symbol = "k" + dependency_name + "Wasm";
801
+ const std::string manifest_symbol = "k" + dependency_name + "Manifest";
802
+ dependency_blocks.push_back(renderByteArray(wasm_symbol, dependency.wasm_bytes));
803
+ if (!dependency.manifest_bytes.empty()) {
804
+ dependency_blocks.push_back(
805
+ renderByteArray(manifest_symbol, dependency.manifest_bytes));
806
+ }
807
+
808
+ std::ostringstream record;
809
+ record << " {\n"
810
+ << " " << cppStringLiteral(dependency.dependency_id) << ",\n"
811
+ << " " << cppStringLiteral(dependency.plugin_id) << ",\n"
812
+ << " " << cppStringLiteral(dependency.version) << ",\n"
813
+ << " " << cppStringLiteral(dependency.sha256) << ",\n"
814
+ << " " << cppStringLiteral(dependency.signature) << ",\n"
815
+ << " " << cppStringLiteral(dependency.signer_public_key) << ",\n"
816
+ << " " << cppStringLiteral(dependency.entrypoint) << ",\n"
817
+ << " " << cppStringLiteral(dependency.manifest_bytes_symbol)
818
+ << ",\n"
819
+ << " " << cppStringLiteral(dependency.manifest_size_symbol)
820
+ << ",\n"
821
+ << " " << cppStringLiteral(dependency.init_symbol) << ",\n"
822
+ << " " << cppStringLiteral(dependency.destroy_symbol) << ",\n"
823
+ << " " << cppStringLiteral(dependency.malloc_symbol) << ",\n"
824
+ << " " << cppStringLiteral(dependency.free_symbol) << ",\n"
825
+ << " "
826
+ << cppStringLiteral(dependency.stream_invoke_symbol) << ",\n"
827
+ << " " << wasm_symbol << ",\n"
828
+ << " sizeof(" << wasm_symbol << "),\n"
829
+ << " "
830
+ << (!dependency.manifest_bytes.empty() ? manifest_symbol : "nullptr")
831
+ << ",\n"
832
+ << " "
833
+ << (!dependency.manifest_bytes.empty()
834
+ ? "sizeof(" + manifest_symbol + ")"
835
+ : "0")
836
+ << "\n"
837
+ << " }";
838
+ dependency_records.push_back(record.str());
839
+ }
840
+
841
+ const std::string namespace_name =
842
+ request.namespace_name.empty() ? "sdn_flow_generated"
843
+ : request.namespace_name;
844
+
845
+ std::ostringstream out;
846
+ out << "// generated by the native sdn-flow C++ source generator\n";
847
+ out << "#include <cstddef>\n";
848
+ out << "#include <cstdint>\n\n";
849
+ out << "namespace " << namespace_name << " {\n\n";
850
+ out << "static constexpr std::uint32_t kInvalidIndex = 0xffffffffu;\n\n";
851
+ out << "struct SignedArtifactDependency {\n"
852
+ << " const char * dependency_id;\n"
853
+ << " const char * plugin_id;\n"
854
+ << " const char * version;\n"
855
+ << " const char * sha256;\n"
856
+ << " const char * signature;\n"
857
+ << " const char * signer_public_key;\n"
858
+ << " const char * entrypoint;\n"
859
+ << " const char * manifest_bytes_symbol;\n"
860
+ << " const char * manifest_size_symbol;\n"
861
+ << " const char * init_symbol;\n"
862
+ << " const char * destroy_symbol;\n"
863
+ << " const char * malloc_symbol;\n"
864
+ << " const char * free_symbol;\n"
865
+ << " const char * stream_invoke_symbol;\n"
866
+ << " const std::uint8_t * wasm_bytes;\n"
867
+ << " std::size_t wasm_size;\n"
868
+ << " const std::uint8_t * manifest_bytes;\n"
869
+ << " std::size_t manifest_size;\n"
870
+ << "};\n\n";
871
+ out << "struct FlowTypeDescriptor {\n"
872
+ << " const char * schema_name;\n"
873
+ << " const char * file_identifier;\n"
874
+ << " const char * schema_hash_hex;\n"
875
+ << " bool accepts_any_flatbuffer;\n"
876
+ << "};\n\n";
877
+ out << "struct FlowTriggerDescriptor {\n"
878
+ << " const char * trigger_id;\n"
879
+ << " const char * kind;\n"
880
+ << " const char * source;\n"
881
+ << " const char * protocol_id;\n"
882
+ << " std::uint32_t default_interval_ms;\n"
883
+ << " std::uint32_t accepted_type_index_offset;\n"
884
+ << " std::uint32_t accepted_type_index_count;\n"
885
+ << " const char * description;\n"
886
+ << "};\n\n";
887
+ out << "struct FlowNodeDescriptor {\n"
888
+ << " const char * node_id;\n"
889
+ << " const char * plugin_id;\n"
890
+ << " const char * method_id;\n"
891
+ << " const char * kind;\n"
892
+ << " const char * drain_policy;\n"
893
+ << " std::uint32_t time_slice_micros;\n"
894
+ << " std::uint32_t ingress_index_offset;\n"
895
+ << " std::uint32_t ingress_index_count;\n"
896
+ << "};\n\n";
897
+ out << "struct FlowNodeDispatchDescriptor {\n"
898
+ << " const char * node_id;\n"
899
+ << " std::uint32_t node_index;\n"
900
+ << " const char * dependency_id;\n"
901
+ << " std::uint32_t dependency_index;\n"
902
+ << " const char * plugin_id;\n"
903
+ << " const char * method_id;\n"
904
+ << " const char * dispatch_model;\n"
905
+ << " const char * entrypoint;\n"
906
+ << " const char * manifest_bytes_symbol;\n"
907
+ << " const char * manifest_size_symbol;\n"
908
+ << " const char * init_symbol;\n"
909
+ << " const char * destroy_symbol;\n"
910
+ << " const char * malloc_symbol;\n"
911
+ << " const char * free_symbol;\n"
912
+ << " const char * stream_invoke_symbol;\n"
913
+ << "};\n\n";
914
+ out << "struct FlowEdgeDescriptor {\n"
915
+ << " const char * edge_id;\n"
916
+ << " const char * from_node_id;\n"
917
+ << " std::uint32_t from_node_index;\n"
918
+ << " const char * from_port_id;\n"
919
+ << " const char * to_node_id;\n"
920
+ << " std::uint32_t to_node_index;\n"
921
+ << " const char * to_port_id;\n"
922
+ << " const char * backpressure_policy;\n"
923
+ << " std::uint32_t queue_depth;\n"
924
+ << " std::uint32_t accepted_type_index_offset;\n"
925
+ << " std::uint32_t accepted_type_index_count;\n"
926
+ << " std::uint32_t target_ingress_index;\n"
927
+ << "};\n\n";
928
+ out << "struct FlowTriggerBindingDescriptor {\n"
929
+ << " const char * trigger_id;\n"
930
+ << " std::uint32_t trigger_index;\n"
931
+ << " const char * target_node_id;\n"
932
+ << " std::uint32_t target_node_index;\n"
933
+ << " const char * target_port_id;\n"
934
+ << " const char * backpressure_policy;\n"
935
+ << " std::uint32_t queue_depth;\n"
936
+ << " std::uint32_t target_ingress_index;\n"
937
+ << "};\n\n";
938
+ out << "struct FlowIngressDescriptor {\n"
939
+ << " const char * ingress_id;\n"
940
+ << " const char * source_kind;\n"
941
+ << " std::uint32_t source_index;\n"
942
+ << " std::uint32_t source_node_index;\n"
943
+ << " const char * source_port_id;\n"
944
+ << " std::uint32_t target_node_index;\n"
945
+ << " const char * target_node_id;\n"
946
+ << " const char * target_port_id;\n"
947
+ << " const char * backpressure_policy;\n"
948
+ << " std::uint32_t queue_depth;\n"
949
+ << "};\n\n";
950
+ out << "struct FlowExternalInterfaceDescriptor {\n"
951
+ << " const char * interface_id;\n"
952
+ << " const char * kind;\n"
953
+ << " const char * direction;\n"
954
+ << " const char * capability;\n"
955
+ << " const char * resource;\n"
956
+ << " const char * protocol_id;\n"
957
+ << " const char * topic;\n"
958
+ << " const char * path;\n"
959
+ << " bool required;\n"
960
+ << " std::uint32_t accepted_type_index_offset;\n"
961
+ << " std::uint32_t accepted_type_index_count;\n"
962
+ << " const char * description;\n"
963
+ << "};\n\n";
964
+ out << "struct FlowFrameDescriptor {\n"
965
+ << " std::uint32_t ingress_index;\n"
966
+ << " std::uint32_t type_descriptor_index;\n"
967
+ << " const char * port_id;\n"
968
+ << " std::uint32_t alignment;\n"
969
+ << " std::uint32_t offset;\n"
970
+ << " std::uint32_t size;\n"
971
+ << " std::uint32_t stream_id;\n"
972
+ << " std::uint32_t sequence;\n"
973
+ << " std::uint64_t trace_token;\n"
974
+ << " bool end_of_stream;\n"
975
+ << " bool occupied;\n"
976
+ << "};\n\n";
977
+ out << "static_assert(sizeof(FlowFrameDescriptor) == 48u,\n"
978
+ << " \"FlowFrameDescriptor must match schemas/FlowRuntimeAbi.fbs\");\n";
979
+ out << "static_assert(alignof(FlowFrameDescriptor) == 8u,\n"
980
+ << " \"FlowFrameDescriptor alignment must match schemas/FlowRuntimeAbi.fbs\");\n";
981
+ out << "static_assert(offsetof(FlowFrameDescriptor, ingress_index) == 0u);\n";
982
+ out << "static_assert(offsetof(FlowFrameDescriptor, type_descriptor_index) == 4u);\n";
983
+ out << "static_assert(offsetof(FlowFrameDescriptor, port_id) == 8u);\n";
984
+ out << "static_assert(offsetof(FlowFrameDescriptor, alignment) == 12u);\n";
985
+ out << "static_assert(offsetof(FlowFrameDescriptor, offset) == 16u);\n";
986
+ out << "static_assert(offsetof(FlowFrameDescriptor, size) == 20u);\n";
987
+ out << "static_assert(offsetof(FlowFrameDescriptor, stream_id) == 24u);\n";
988
+ out << "static_assert(offsetof(FlowFrameDescriptor, sequence) == 28u);\n";
989
+ out << "static_assert(offsetof(FlowFrameDescriptor, trace_token) == 32u);\n";
990
+ out << "static_assert(offsetof(FlowFrameDescriptor, end_of_stream) == 40u);\n";
991
+ out << "static_assert(offsetof(FlowFrameDescriptor, occupied) == 41u);\n\n";
992
+ out << "struct FlowInvocationDescriptor {\n"
993
+ << " std::uint32_t node_index;\n"
994
+ << " std::uint32_t dispatch_descriptor_index;\n"
995
+ << " const char * plugin_id;\n"
996
+ << " const char * method_id;\n"
997
+ << " const FlowFrameDescriptor * frames;\n"
998
+ << " std::uint32_t frame_count;\n"
999
+ << "};\n\n";
1000
+ out << "static_assert(sizeof(FlowInvocationDescriptor) == 24u,\n"
1001
+ << " \"FlowInvocationDescriptor must match schemas/FlowRuntimeAbi.fbs\");\n";
1002
+ out << "static_assert(alignof(FlowInvocationDescriptor) == 4u,\n"
1003
+ << " \"FlowInvocationDescriptor alignment must match schemas/FlowRuntimeAbi.fbs\");\n";
1004
+ out << "static_assert(offsetof(FlowInvocationDescriptor, node_index) == 0u);\n";
1005
+ out << "static_assert(offsetof(FlowInvocationDescriptor, dispatch_descriptor_index) == 4u);\n";
1006
+ out << "static_assert(offsetof(FlowInvocationDescriptor, plugin_id) == 8u);\n";
1007
+ out << "static_assert(offsetof(FlowInvocationDescriptor, method_id) == 12u);\n";
1008
+ out << "static_assert(offsetof(FlowInvocationDescriptor, frames) == 16u);\n";
1009
+ out << "static_assert(offsetof(FlowInvocationDescriptor, frame_count) == 20u);\n\n";
1010
+ out << "static_assert(sizeof(FlowNodeDispatchDescriptor) == 60u,\n"
1011
+ << " \"FlowNodeDispatchDescriptor must match schemas/FlowRuntimeAbi.fbs\");\n";
1012
+ out << "static_assert(alignof(FlowNodeDispatchDescriptor) == 4u,\n"
1013
+ << " \"FlowNodeDispatchDescriptor alignment must match schemas/FlowRuntimeAbi.fbs\");\n";
1014
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, node_id) == 0u);\n";
1015
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, node_index) == 4u);\n";
1016
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, dependency_id) == 8u);\n";
1017
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, dependency_index) == 12u);\n";
1018
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, plugin_id) == 16u);\n";
1019
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, method_id) == 20u);\n";
1020
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, dispatch_model) == 24u);\n";
1021
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, entrypoint) == 28u);\n";
1022
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, manifest_bytes_symbol) == 32u);\n";
1023
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, manifest_size_symbol) == 36u);\n";
1024
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, init_symbol) == 40u);\n";
1025
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, destroy_symbol) == 44u);\n";
1026
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, malloc_symbol) == 48u);\n";
1027
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, free_symbol) == 52u);\n";
1028
+ out << "static_assert(offsetof(FlowNodeDispatchDescriptor, stream_invoke_symbol) == 56u);\n\n";
1029
+ out << "static_assert(sizeof(SignedArtifactDependency) == 72u,\n"
1030
+ << " \"SignedArtifactDependency must match schemas/FlowRuntimeAbi.fbs\");\n";
1031
+ out << "static_assert(alignof(SignedArtifactDependency) == 4u,\n"
1032
+ << " \"SignedArtifactDependency alignment must match schemas/FlowRuntimeAbi.fbs\");\n";
1033
+ out << "static_assert(offsetof(SignedArtifactDependency, dependency_id) == 0u);\n";
1034
+ out << "static_assert(offsetof(SignedArtifactDependency, plugin_id) == 4u);\n";
1035
+ out << "static_assert(offsetof(SignedArtifactDependency, version) == 8u);\n";
1036
+ out << "static_assert(offsetof(SignedArtifactDependency, sha256) == 12u);\n";
1037
+ out << "static_assert(offsetof(SignedArtifactDependency, signature) == 16u);\n";
1038
+ out << "static_assert(offsetof(SignedArtifactDependency, signer_public_key) == 20u);\n";
1039
+ out << "static_assert(offsetof(SignedArtifactDependency, entrypoint) == 24u);\n";
1040
+ out << "static_assert(offsetof(SignedArtifactDependency, manifest_bytes_symbol) == 28u);\n";
1041
+ out << "static_assert(offsetof(SignedArtifactDependency, manifest_size_symbol) == 32u);\n";
1042
+ out << "static_assert(offsetof(SignedArtifactDependency, init_symbol) == 36u);\n";
1043
+ out << "static_assert(offsetof(SignedArtifactDependency, destroy_symbol) == 40u);\n";
1044
+ out << "static_assert(offsetof(SignedArtifactDependency, malloc_symbol) == 44u);\n";
1045
+ out << "static_assert(offsetof(SignedArtifactDependency, free_symbol) == 48u);\n";
1046
+ out << "static_assert(offsetof(SignedArtifactDependency, stream_invoke_symbol) == 52u);\n";
1047
+ out << "static_assert(offsetof(SignedArtifactDependency, wasm_bytes) == 56u);\n";
1048
+ out << "static_assert(offsetof(SignedArtifactDependency, wasm_size) == 60u);\n";
1049
+ out << "static_assert(offsetof(SignedArtifactDependency, manifest_bytes) == 64u);\n";
1050
+ out << "static_assert(offsetof(SignedArtifactDependency, manifest_size) == 68u);\n\n";
1051
+ out << "struct FlowIngressRuntimeState {\n"
1052
+ << " std::uint64_t total_received;\n"
1053
+ << " std::uint64_t total_dropped;\n"
1054
+ << " std::uint32_t queued_frames;\n"
1055
+ << "};\n\n";
1056
+ out << "struct FlowNodeRuntimeState {\n"
1057
+ << " std::uint64_t invocation_count;\n"
1058
+ << " std::uint64_t consumed_frames;\n"
1059
+ << " std::uint32_t queued_frames;\n"
1060
+ << " std::uint32_t backlog_remaining;\n"
1061
+ << " std::uint32_t last_status;\n"
1062
+ << " bool ready;\n"
1063
+ << " bool yielded;\n"
1064
+ << "};\n\n";
1065
+ out << "struct FlowRuntimeDescriptor {\n"
1066
+ << " const char * program_id;\n"
1067
+ << " const char * program_name;\n"
1068
+ << " const char * program_version;\n"
1069
+ << " const char * program_description;\n"
1070
+ << " const char * execution_model;\n"
1071
+ << " const char * entrypoint;\n"
1072
+ << " const char * manifest_bytes_symbol;\n"
1073
+ << " const char * manifest_size_symbol;\n"
1074
+ << " const char * const * required_plugins;\n"
1075
+ << " std::size_t required_plugin_count;\n"
1076
+ << " const FlowTypeDescriptor * type_descriptors;\n"
1077
+ << " std::size_t type_descriptor_count;\n"
1078
+ << " const std::uint32_t * accepted_type_indices;\n"
1079
+ << " std::size_t accepted_type_index_count;\n"
1080
+ << " const FlowTriggerDescriptor * triggers;\n"
1081
+ << " std::size_t trigger_count;\n"
1082
+ << " const FlowNodeDescriptor * nodes;\n"
1083
+ << " std::size_t node_count;\n"
1084
+ << " const FlowNodeDispatchDescriptor * node_dispatch_descriptors;\n"
1085
+ << " std::size_t node_dispatch_descriptor_count;\n"
1086
+ << " const FlowEdgeDescriptor * edges;\n"
1087
+ << " std::size_t edge_count;\n"
1088
+ << " const FlowTriggerBindingDescriptor * trigger_bindings;\n"
1089
+ << " std::size_t trigger_binding_count;\n"
1090
+ << " const FlowIngressDescriptor * ingress_descriptors;\n"
1091
+ << " std::size_t ingress_count;\n"
1092
+ << " const std::uint32_t * node_ingress_indices;\n"
1093
+ << " std::size_t node_ingress_index_count;\n"
1094
+ << " const FlowExternalInterfaceDescriptor * external_interfaces;\n"
1095
+ << " std::size_t external_interface_count;\n"
1096
+ << " const SignedArtifactDependency * dependencies;\n"
1097
+ << " std::size_t dependency_count;\n"
1098
+ << " FlowFrameDescriptor * ingress_frame_descriptors;\n"
1099
+ << " std::size_t ingress_frame_descriptor_count;\n"
1100
+ << " FlowInvocationDescriptor * current_invocation_descriptor;\n"
1101
+ << " FlowIngressRuntimeState * ingress_runtime_states;\n"
1102
+ << " std::size_t ingress_runtime_state_count;\n"
1103
+ << " FlowNodeRuntimeState * node_runtime_states;\n"
1104
+ << " std::size_t node_runtime_state_count;\n"
1105
+ << "};\n\n";
1106
+ out << "static std::uint32_t min_u32(std::uint32_t left, std::uint32_t right) {\n"
1107
+ << " return left < right ? left : right;\n"
1108
+ << "}\n\n";
1109
+ out << "static bool string_equals(const char * left, const char * right) {\n"
1110
+ << " if (left == right) {\n"
1111
+ << " return true;\n"
1112
+ << " }\n"
1113
+ << " if (left == nullptr || right == nullptr) {\n"
1114
+ << " return false;\n"
1115
+ << " }\n"
1116
+ << " while (*left != '\\0' && *right != '\\0') {\n"
1117
+ << " if (*left != *right) {\n"
1118
+ << " return false;\n"
1119
+ << " }\n"
1120
+ << " ++left;\n"
1121
+ << " ++right;\n"
1122
+ << " }\n"
1123
+ << " return *left == *right;\n"
1124
+ << "}\n\n";
1125
+ out << "static void clear_frame_descriptor(FlowFrameDescriptor & descriptor) {\n"
1126
+ << " descriptor.ingress_index = kInvalidIndex;\n"
1127
+ << " descriptor.type_descriptor_index = kInvalidIndex;\n"
1128
+ << " descriptor.port_id = nullptr;\n"
1129
+ << " descriptor.alignment = 0;\n"
1130
+ << " descriptor.offset = 0;\n"
1131
+ << " descriptor.size = 0;\n"
1132
+ << " descriptor.stream_id = 0;\n"
1133
+ << " descriptor.sequence = 0;\n"
1134
+ << " descriptor.trace_token = 0;\n"
1135
+ << " descriptor.end_of_stream = false;\n"
1136
+ << " descriptor.occupied = false;\n"
1137
+ << "}\n\n";
1138
+ out << "static void clear_invocation_descriptor() {\n"
1139
+ << " kCurrentInvocationDescriptor.node_index = kInvalidIndex;\n"
1140
+ << " kCurrentInvocationDescriptor.dispatch_descriptor_index = kInvalidIndex;\n"
1141
+ << " kCurrentInvocationDescriptor.plugin_id = nullptr;\n"
1142
+ << " kCurrentInvocationDescriptor.method_id = nullptr;\n"
1143
+ << " kCurrentInvocationDescriptor.frames = kInvocationFrameBuffer;\n"
1144
+ << " kCurrentInvocationDescriptor.frame_count = 0;\n"
1145
+ << " for (std::size_t index = 0; index < "
1146
+ << (request.ingress_descriptors.empty() ? 1 : request.ingress_descriptors.size())
1147
+ << "; ++index) {\n"
1148
+ << " clear_frame_descriptor(kInvocationFrameBuffer[index]);\n"
1149
+ << " }\n"
1150
+ << "}\n\n";
1151
+
1152
+ out << renderByteArray("kFlowManifest", request.manifest_buffer) << "\n\n";
1153
+ out << renderStringPointerArray("kRequiredPlugins", request.required_plugins)
1154
+ << "\n\n";
1155
+ out << renderRecordArray("FlowTypeDescriptor", "kTypeDescriptors",
1156
+ type_descriptor_records)
1157
+ << "\n\n";
1158
+ out << renderIntegerArray("std::uint32_t", "kAcceptedTypeIndices",
1159
+ request.accepted_type_indices)
1160
+ << "\n\n";
1161
+ out << renderRecordArray("FlowTriggerDescriptor", "kTriggerDescriptors",
1162
+ trigger_records)
1163
+ << "\n\n";
1164
+ out << renderRecordArray("FlowNodeDescriptor", "kNodeDescriptors", node_records)
1165
+ << "\n\n";
1166
+ out << renderRecordArray("FlowNodeDispatchDescriptor",
1167
+ "kNodeDispatchDescriptors",
1168
+ node_dispatch_records)
1169
+ << "\n\n";
1170
+ out << renderRecordArray("FlowEdgeDescriptor", "kEdgeDescriptors", edge_records)
1171
+ << "\n\n";
1172
+ out << renderRecordArray("FlowTriggerBindingDescriptor",
1173
+ "kTriggerBindingDescriptors",
1174
+ trigger_binding_records)
1175
+ << "\n\n";
1176
+ out << renderRecordArray("FlowIngressDescriptor", "kIngressDescriptors",
1177
+ ingress_records)
1178
+ << "\n\n";
1179
+ out << renderIntegerArray("std::uint32_t", "kNodeIngressIndices",
1180
+ request.node_ingress_indices)
1181
+ << "\n\n";
1182
+ out << renderRecordArray("FlowExternalInterfaceDescriptor",
1183
+ "kExternalInterfaceDescriptors",
1184
+ external_interface_records)
1185
+ << "\n\n";
1186
+ out << renderMutableRecordArray("FlowFrameDescriptor",
1187
+ "kIngressFrameDescriptors",
1188
+ request.ingress_descriptors.size())
1189
+ << "\n\n";
1190
+ out << renderMutableRecordArray("FlowFrameDescriptor",
1191
+ "kInvocationFrameBuffer",
1192
+ request.ingress_descriptors.size())
1193
+ << "\n\n";
1194
+ out << "static FlowInvocationDescriptor kCurrentInvocationDescriptor = {};\n\n";
1195
+ out << renderMutableRecordArray("FlowIngressRuntimeState",
1196
+ "kIngressRuntimeStates",
1197
+ request.ingress_descriptors.size())
1198
+ << "\n\n";
1199
+ out << renderMutableRecordArray("FlowNodeRuntimeState", "kNodeRuntimeStates",
1200
+ request.nodes.size())
1201
+ << "\n\n";
1202
+ for (std::size_t index = 0; index < dependency_blocks.size(); ++index) {
1203
+ out << dependency_blocks[index];
1204
+ if (index + 1 < dependency_blocks.size()) {
1205
+ out << "\n\n";
1206
+ }
1207
+ }
1208
+ out << "\n\n";
1209
+ out << renderRecordArray("SignedArtifactDependency", "kDependencies",
1210
+ dependency_records)
1211
+ << "\n\n";
1212
+ out << "static const char kProgramId[] = "
1213
+ << cppStringLiteral(request.program_id) << ";\n";
1214
+ out << "static const char kProgramName[] = "
1215
+ << cppStringLiteral(request.program_name) << ";\n";
1216
+ out << "static const char kProgramVersion[] = "
1217
+ << cppStringLiteral(request.program_version) << ";\n";
1218
+ out << "static const char kProgramDescription[] = "
1219
+ << cppStringLiteral(request.program_description) << ";\n";
1220
+ out << "static const char kExecutionModel[] = \"compiled-cpp-wasm\";\n";
1221
+ out << "static const char kEntrypoint[] = \"main\";\n";
1222
+ out << "static const char kManifestBytesSymbol[] = "
1223
+ << "\"flow_get_manifest_flatbuffer\";\n";
1224
+ out << "static const char kManifestSizeSymbol[] = "
1225
+ << "\"flow_get_manifest_flatbuffer_size\";\n\n";
1226
+ out << "static void recompute_node_runtime_state(std::uint32_t node_index) {\n"
1227
+ << " if (node_index >= " << request.nodes.size() << ") {\n"
1228
+ << " return;\n"
1229
+ << " }\n\n"
1230
+ << " const FlowNodeDescriptor & node_descriptor = "
1231
+ "kNodeDescriptors[node_index];\n"
1232
+ << " FlowNodeRuntimeState & node_state = kNodeRuntimeStates[node_index];\n"
1233
+ << " std::uint32_t queued_frames = 0;\n"
1234
+ << " for (\n"
1235
+ << " std::uint32_t offset = 0;\n"
1236
+ << " offset < node_descriptor.ingress_index_count;\n"
1237
+ << " ++offset\n"
1238
+ << " ) {\n"
1239
+ << " const std::uint32_t ingress_index =\n"
1240
+ << " kNodeIngressIndices[node_descriptor.ingress_index_offset + "
1241
+ "offset];\n"
1242
+ << " queued_frames += kIngressRuntimeStates[ingress_index].queued_frames;\n"
1243
+ << " }\n"
1244
+ << " node_state.queued_frames = queued_frames;\n"
1245
+ << " node_state.ready = queued_frames > 0 || "
1246
+ "node_state.backlog_remaining > 0;\n"
1247
+ << "}\n\n";
1248
+ out << "static void recompute_all_node_runtime_state() {\n"
1249
+ << " for (std::uint32_t node_index = 0; node_index < "
1250
+ << request.nodes.size() << "; ++node_index) {\n"
1251
+ << " recompute_node_runtime_state(node_index);\n"
1252
+ << " }\n"
1253
+ << "}\n\n";
1254
+ out << "static void apply_backpressure(std::uint32_t ingress_index, "
1255
+ "std::uint32_t frame_count) {\n"
1256
+ << " if (ingress_index >= " << request.ingress_descriptors.size()
1257
+ << ") {\n"
1258
+ << " return;\n"
1259
+ << " }\n\n"
1260
+ << " FlowIngressRuntimeState & ingress_state = "
1261
+ "kIngressRuntimeStates[ingress_index];\n"
1262
+ << " const FlowIngressDescriptor & ingress_descriptor = "
1263
+ "kIngressDescriptors[ingress_index];\n"
1264
+ << " ingress_state.total_received += frame_count;\n\n"
1265
+ << " const bool bounded = ingress_descriptor.queue_depth > 0;\n"
1266
+ << " if (string_equals(ingress_descriptor.backpressure_policy, "
1267
+ "\"drop\")) {\n"
1268
+ << " std::uint32_t accepted = frame_count;\n"
1269
+ << " if (bounded) {\n"
1270
+ << " const std::uint32_t available =\n"
1271
+ << " ingress_descriptor.queue_depth > ingress_state.queued_frames\n"
1272
+ << " ? ingress_descriptor.queue_depth - "
1273
+ "ingress_state.queued_frames\n"
1274
+ << " : 0;\n"
1275
+ << " accepted = min_u32(frame_count, available);\n"
1276
+ << " ingress_state.total_dropped += frame_count - accepted;\n"
1277
+ << " }\n"
1278
+ << " ingress_state.queued_frames += accepted;\n"
1279
+ << " return;\n"
1280
+ << " }\n\n"
1281
+ << " if (\n"
1282
+ << " string_equals(ingress_descriptor.backpressure_policy, "
1283
+ "\"latest\") ||\n"
1284
+ << " string_equals(ingress_descriptor.backpressure_policy, "
1285
+ "\"coalesce\")\n"
1286
+ << " ) {\n"
1287
+ << " if (!bounded) {\n"
1288
+ << " ingress_state.queued_frames += frame_count;\n"
1289
+ << " return;\n"
1290
+ << " }\n"
1291
+ << " if (frame_count == 0) {\n"
1292
+ << " return;\n"
1293
+ << " }\n"
1294
+ << " if (ingress_state.queued_frames + frame_count > "
1295
+ "ingress_descriptor.queue_depth) {\n"
1296
+ << " ingress_state.total_dropped +=\n"
1297
+ << " static_cast<std::uint64_t>(ingress_state.queued_frames) +\n"
1298
+ << " static_cast<std::uint64_t>(frame_count) -\n"
1299
+ << " 1u;\n"
1300
+ << " ingress_state.queued_frames = 1u;\n"
1301
+ << " return;\n"
1302
+ << " }\n"
1303
+ << " ingress_state.queued_frames += frame_count;\n"
1304
+ << " return;\n"
1305
+ << " }\n\n"
1306
+ << " if (string_equals(ingress_descriptor.backpressure_policy, "
1307
+ "\"block-request\")) {\n"
1308
+ << " if (\n"
1309
+ << " bounded &&\n"
1310
+ << " ingress_state.queued_frames + frame_count > "
1311
+ "ingress_descriptor.queue_depth\n"
1312
+ << " ) {\n"
1313
+ << " ingress_state.total_dropped += frame_count;\n"
1314
+ << " return;\n"
1315
+ << " }\n"
1316
+ << " ingress_state.queued_frames += frame_count;\n"
1317
+ << " return;\n"
1318
+ << " }\n\n"
1319
+ << " if (\n"
1320
+ << " bounded &&\n"
1321
+ << " ingress_state.queued_frames + frame_count > "
1322
+ "ingress_descriptor.queue_depth\n"
1323
+ << " ) {\n"
1324
+ << " ingress_state.total_dropped +=\n"
1325
+ << " static_cast<std::uint64_t>(ingress_state.queued_frames) +\n"
1326
+ << " static_cast<std::uint64_t>(frame_count) -\n"
1327
+ << " static_cast<std::uint64_t>(ingress_descriptor.queue_depth);\n"
1328
+ << " ingress_state.queued_frames = ingress_descriptor.queue_depth;\n"
1329
+ << " return;\n"
1330
+ << " }\n\n"
1331
+ << " ingress_state.queued_frames += frame_count;\n"
1332
+ << "}\n\n";
1333
+ out << "static void stage_ingress_frame(\n"
1334
+ << " std::uint32_t ingress_index,\n"
1335
+ << " const FlowFrameDescriptor * frame\n"
1336
+ << ") {\n"
1337
+ << " if (frame == nullptr || ingress_index >= "
1338
+ << request.ingress_descriptors.size() << ") {\n"
1339
+ << " return;\n"
1340
+ << " }\n"
1341
+ << " kIngressFrameDescriptors[ingress_index] = *frame;\n"
1342
+ << " kIngressFrameDescriptors[ingress_index].ingress_index = ingress_index;\n"
1343
+ << " kIngressFrameDescriptors[ingress_index].port_id =\n"
1344
+ << " kIngressDescriptors[ingress_index].target_port_id;\n"
1345
+ << " kIngressFrameDescriptors[ingress_index].occupied = true;\n"
1346
+ << "}\n\n";
1347
+ out << "static std::uint32_t route_output_frame(\n"
1348
+ << " std::uint32_t node_index,\n"
1349
+ << " const FlowFrameDescriptor * frame\n"
1350
+ << ") {\n"
1351
+ << " if (frame == nullptr || frame->port_id == nullptr) {\n"
1352
+ << " return 0;\n"
1353
+ << " }\n"
1354
+ << " std::uint32_t routed = 0;\n"
1355
+ << " for (std::size_t edge_index = 0; edge_index < kRuntimeDescriptor.edge_count; ++edge_index) {\n"
1356
+ << " const FlowEdgeDescriptor & edge = kEdgeDescriptors[edge_index];\n"
1357
+ << " if (edge.from_node_index != node_index) {\n"
1358
+ << " continue;\n"
1359
+ << " }\n"
1360
+ << " if (!string_equals(edge.from_port_id, frame->port_id)) {\n"
1361
+ << " continue;\n"
1362
+ << " }\n"
1363
+ << " if (edge.target_ingress_index == kInvalidIndex) {\n"
1364
+ << " continue;\n"
1365
+ << " }\n"
1366
+ << " stage_ingress_frame(edge.target_ingress_index, frame);\n"
1367
+ << " apply_backpressure(edge.target_ingress_index, 1u);\n"
1368
+ << " if (edge.to_node_index != kInvalidIndex) {\n"
1369
+ << " recompute_node_runtime_state(edge.to_node_index);\n"
1370
+ << " }\n"
1371
+ << " routed += 1u;\n"
1372
+ << " }\n"
1373
+ << " return routed;\n"
1374
+ << "}\n\n";
1375
+ out << "static void populate_invocation_descriptor(\n"
1376
+ << " std::uint32_t node_index,\n"
1377
+ << " std::uint32_t frame_budget\n"
1378
+ << ") {\n"
1379
+ << " clear_invocation_descriptor();\n"
1380
+ << " if (node_index >= " << request.nodes.size() << ") {\n"
1381
+ << " return;\n"
1382
+ << " }\n"
1383
+ << " const FlowNodeDescriptor & node_descriptor = "
1384
+ "kNodeDescriptors[node_index];\n"
1385
+ << " kCurrentInvocationDescriptor.node_index = node_index;\n"
1386
+ << " kCurrentInvocationDescriptor.dispatch_descriptor_index = node_index;\n"
1387
+ << " kCurrentInvocationDescriptor.plugin_id = node_descriptor.plugin_id;\n"
1388
+ << " kCurrentInvocationDescriptor.method_id = node_descriptor.method_id;\n"
1389
+ << " const std::uint32_t budget = frame_budget == 0 ? 1u : frame_budget;\n"
1390
+ << " for (\n"
1391
+ << " std::uint32_t offset = 0;\n"
1392
+ << " offset < node_descriptor.ingress_index_count &&\n"
1393
+ << " kCurrentInvocationDescriptor.frame_count < budget;\n"
1394
+ << " ++offset\n"
1395
+ << " ) {\n"
1396
+ << " const std::uint32_t ingress_index =\n"
1397
+ << " kNodeIngressIndices[node_descriptor.ingress_index_offset + offset];\n"
1398
+ << " const FlowIngressRuntimeState & ingress_state =\n"
1399
+ << " kIngressRuntimeStates[ingress_index];\n"
1400
+ << " const FlowFrameDescriptor & ingress_frame =\n"
1401
+ << " kIngressFrameDescriptors[ingress_index];\n"
1402
+ << " if (ingress_state.queued_frames == 0 || !ingress_frame.occupied) {\n"
1403
+ << " continue;\n"
1404
+ << " }\n"
1405
+ << " kInvocationFrameBuffer[kCurrentInvocationDescriptor.frame_count] =\n"
1406
+ << " ingress_frame;\n"
1407
+ << " kCurrentInvocationDescriptor.frame_count += 1;\n"
1408
+ << " }\n"
1409
+ << "}\n\n";
1410
+
1411
+ out << "static FlowRuntimeDescriptor kRuntimeDescriptor = {\n"
1412
+ << " kProgramId,\n"
1413
+ << " kProgramName,\n"
1414
+ << " kProgramVersion,\n"
1415
+ << " kProgramDescription,\n"
1416
+ << " kExecutionModel,\n"
1417
+ << " kEntrypoint,\n"
1418
+ << " kManifestBytesSymbol,\n"
1419
+ << " kManifestSizeSymbol,\n"
1420
+ << " kRequiredPlugins,\n"
1421
+ << " " << request.required_plugins.size() << ",\n"
1422
+ << " kTypeDescriptors,\n"
1423
+ << " " << request.type_descriptors.size() << ",\n"
1424
+ << " kAcceptedTypeIndices,\n"
1425
+ << " " << request.accepted_type_indices.size() << ",\n"
1426
+ << " kTriggerDescriptors,\n"
1427
+ << " " << request.triggers.size() << ",\n"
1428
+ << " kNodeDescriptors,\n"
1429
+ << " " << request.nodes.size() << ",\n"
1430
+ << " kNodeDispatchDescriptors,\n"
1431
+ << " " << request.nodes.size() << ",\n"
1432
+ << " kEdgeDescriptors,\n"
1433
+ << " " << request.edges.size() << ",\n"
1434
+ << " kTriggerBindingDescriptors,\n"
1435
+ << " " << request.trigger_bindings.size() << ",\n"
1436
+ << " kIngressDescriptors,\n"
1437
+ << " " << request.ingress_descriptors.size() << ",\n"
1438
+ << " kNodeIngressIndices,\n"
1439
+ << " " << request.node_ingress_indices.size() << ",\n"
1440
+ << " kExternalInterfaceDescriptors,\n"
1441
+ << " " << request.external_interfaces.size() << ",\n"
1442
+ << " kDependencies,\n"
1443
+ << " " << request.dependencies.size() << ",\n"
1444
+ << " kIngressFrameDescriptors,\n"
1445
+ << " " << request.ingress_descriptors.size() << ",\n"
1446
+ << " &kCurrentInvocationDescriptor,\n"
1447
+ << " kIngressRuntimeStates,\n"
1448
+ << " " << request.ingress_descriptors.size() << ",\n"
1449
+ << " kNodeRuntimeStates,\n"
1450
+ << " " << request.nodes.size() << '\n'
1451
+ << "};\n\n";
1452
+ out << "} // namespace " << namespace_name << "\n\n";
1453
+ out << "extern \"C\" const std::uint8_t * flow_get_manifest_flatbuffer() {\n"
1454
+ << " return " << namespace_name << "::kFlowManifest;\n"
1455
+ << "}\n\n";
1456
+ out << "extern \"C\" std::size_t flow_get_manifest_flatbuffer_size() {\n"
1457
+ << " return sizeof(" << namespace_name << "::kFlowManifest);\n"
1458
+ << "}\n\n";
1459
+ out << "extern \"C\" __attribute__((import_module(\"sdn_flow_host\"), import_name(\"dispatch_current_invocation\")))\n"
1460
+ << "std::uint32_t sdn_flow_host_dispatch_current_invocation(std::uint32_t output_stream_cap);\n\n";
1461
+ out << "extern \"C\" const char * sdn_flow_get_program_id() {\n"
1462
+ << " return " << namespace_name << "::kProgramId;\n"
1463
+ << "}\n\n";
1464
+ out << "extern \"C\" const char * sdn_flow_get_program_name() {\n"
1465
+ << " return " << namespace_name << "::kProgramName;\n"
1466
+ << "}\n\n";
1467
+ out << "extern \"C\" const char * sdn_flow_get_program_version() {\n"
1468
+ << " return " << namespace_name << "::kProgramVersion;\n"
1469
+ << "}\n\n";
1470
+ out << "extern \"C\" const " << namespace_name
1471
+ << "::FlowTypeDescriptor * sdn_flow_get_type_descriptors() {\n"
1472
+ << " return " << namespace_name << "::kTypeDescriptors;\n"
1473
+ << "}\n\n";
1474
+ out << "extern \"C\" std::size_t sdn_flow_get_type_descriptor_count() {\n"
1475
+ << " return " << namespace_name
1476
+ << "::kRuntimeDescriptor.type_descriptor_count;\n"
1477
+ << "}\n\n";
1478
+ out << "extern \"C\" const std::uint32_t * sdn_flow_get_accepted_type_indices() {\n"
1479
+ << " return " << namespace_name << "::kAcceptedTypeIndices;\n"
1480
+ << "}\n\n";
1481
+ out << "extern \"C\" std::size_t sdn_flow_get_accepted_type_index_count() {\n"
1482
+ << " return " << namespace_name
1483
+ << "::kRuntimeDescriptor.accepted_type_index_count;\n"
1484
+ << "}\n\n";
1485
+ out << "extern \"C\" const " << namespace_name
1486
+ << "::FlowTriggerDescriptor * sdn_flow_get_trigger_descriptors() {\n"
1487
+ << " return " << namespace_name << "::kTriggerDescriptors;\n"
1488
+ << "}\n\n";
1489
+ out << "extern \"C\" std::size_t sdn_flow_get_trigger_descriptor_count() {\n"
1490
+ << " return " << namespace_name
1491
+ << "::kRuntimeDescriptor.trigger_count;\n"
1492
+ << "}\n\n";
1493
+ out << "extern \"C\" const " << namespace_name
1494
+ << "::FlowNodeDescriptor * sdn_flow_get_node_descriptors() {\n"
1495
+ << " return " << namespace_name << "::kNodeDescriptors;\n"
1496
+ << "}\n\n";
1497
+ out << "extern \"C\" std::size_t sdn_flow_get_node_descriptor_count() {\n"
1498
+ << " return " << namespace_name
1499
+ << "::kRuntimeDescriptor.node_count;\n"
1500
+ << "}\n\n";
1501
+ out << "extern \"C\" const " << namespace_name
1502
+ << "::FlowNodeDispatchDescriptor * sdn_flow_get_node_dispatch_descriptors() {\n"
1503
+ << " return " << namespace_name << "::kNodeDispatchDescriptors;\n"
1504
+ << "}\n\n";
1505
+ out << "extern \"C\" std::size_t sdn_flow_get_node_dispatch_descriptor_count() {\n"
1506
+ << " return " << namespace_name
1507
+ << "::kRuntimeDescriptor.node_dispatch_descriptor_count;\n"
1508
+ << "}\n\n";
1509
+ out << "extern \"C\" const " << namespace_name
1510
+ << "::FlowEdgeDescriptor * sdn_flow_get_edge_descriptors() {\n"
1511
+ << " return " << namespace_name << "::kEdgeDescriptors;\n"
1512
+ << "}\n\n";
1513
+ out << "extern \"C\" std::size_t sdn_flow_get_edge_descriptor_count() {\n"
1514
+ << " return " << namespace_name
1515
+ << "::kRuntimeDescriptor.edge_count;\n"
1516
+ << "}\n\n";
1517
+ out << "extern \"C\" const " << namespace_name
1518
+ << "::FlowTriggerBindingDescriptor * sdn_flow_get_trigger_binding_descriptors() {\n"
1519
+ << " return " << namespace_name << "::kTriggerBindingDescriptors;\n"
1520
+ << "}\n\n";
1521
+ out << "extern \"C\" std::size_t sdn_flow_get_trigger_binding_descriptor_count() {\n"
1522
+ << " return " << namespace_name
1523
+ << "::kRuntimeDescriptor.trigger_binding_count;\n"
1524
+ << "}\n\n";
1525
+ out << "extern \"C\" const " << namespace_name
1526
+ << "::SignedArtifactDependency * sdn_flow_get_dependency_descriptors() {\n"
1527
+ << " return " << namespace_name << "::kDependencies;\n"
1528
+ << "}\n\n";
1529
+ out << "extern \"C\" std::size_t sdn_flow_get_dependency_count() {\n"
1530
+ << " return " << namespace_name << "::kRuntimeDescriptor.dependency_count;\n"
1531
+ << "}\n\n";
1532
+ out << "extern \"C\" const " << namespace_name
1533
+ << "::FlowIngressDescriptor * sdn_flow_get_ingress_descriptors() {\n"
1534
+ << " return " << namespace_name << "::kIngressDescriptors;\n"
1535
+ << "}\n\n";
1536
+ out << "extern \"C\" std::size_t sdn_flow_get_ingress_descriptor_count() {\n"
1537
+ << " return " << namespace_name << "::kRuntimeDescriptor.ingress_count;\n"
1538
+ << "}\n\n";
1539
+ out << "extern \"C\" " << namespace_name
1540
+ << "::FlowFrameDescriptor * sdn_flow_get_ingress_frame_descriptors() {\n"
1541
+ << " return " << namespace_name << "::kIngressFrameDescriptors;\n"
1542
+ << "}\n\n";
1543
+ out << "extern \"C\" std::size_t sdn_flow_get_ingress_frame_descriptor_count() {\n"
1544
+ << " return " << namespace_name
1545
+ << "::kRuntimeDescriptor.ingress_frame_descriptor_count;\n"
1546
+ << "}\n\n";
1547
+ out << "extern \"C\" const std::uint32_t * sdn_flow_get_node_ingress_indices() {\n"
1548
+ << " return " << namespace_name << "::kNodeIngressIndices;\n"
1549
+ << "}\n\n";
1550
+ out << "extern \"C\" std::size_t sdn_flow_get_node_ingress_index_count() {\n"
1551
+ << " return " << namespace_name
1552
+ << "::kRuntimeDescriptor.node_ingress_index_count;\n"
1553
+ << "}\n\n";
1554
+ out << "extern \"C\" const " << namespace_name
1555
+ << "::FlowExternalInterfaceDescriptor * sdn_flow_get_external_interface_descriptors() {\n"
1556
+ << " return " << namespace_name
1557
+ << "::kExternalInterfaceDescriptors;\n"
1558
+ << "}\n\n";
1559
+ out << "extern \"C\" std::size_t sdn_flow_get_external_interface_descriptor_count() {\n"
1560
+ << " return " << namespace_name
1561
+ << "::kRuntimeDescriptor.external_interface_count;\n"
1562
+ << "}\n\n";
1563
+ out << "extern \"C\" " << namespace_name
1564
+ << "::FlowIngressRuntimeState * sdn_flow_get_ingress_runtime_states() {\n"
1565
+ << " return " << namespace_name << "::kIngressRuntimeStates;\n"
1566
+ << "}\n\n";
1567
+ out << "extern \"C\" std::size_t sdn_flow_get_ingress_runtime_state_count() {\n"
1568
+ << " return " << namespace_name
1569
+ << "::kRuntimeDescriptor.ingress_runtime_state_count;\n"
1570
+ << "}\n\n";
1571
+ out << "extern \"C\" " << namespace_name
1572
+ << "::FlowNodeRuntimeState * sdn_flow_get_node_runtime_states() {\n"
1573
+ << " return " << namespace_name << "::kNodeRuntimeStates;\n"
1574
+ << "}\n\n";
1575
+ out << "extern \"C\" std::size_t sdn_flow_get_node_runtime_state_count() {\n"
1576
+ << " return " << namespace_name
1577
+ << "::kRuntimeDescriptor.node_runtime_state_count;\n"
1578
+ << "}\n\n";
1579
+ out << "extern \"C\" " << namespace_name
1580
+ << "::FlowInvocationDescriptor * sdn_flow_get_current_invocation_descriptor() {\n"
1581
+ << " return " << namespace_name << "::kRuntimeDescriptor.current_invocation_descriptor;\n"
1582
+ << "}\n\n";
1583
+ out << "extern \"C\" std::uint32_t sdn_flow_prepare_node_invocation_descriptor(\n"
1584
+ << " std::uint32_t node_index,\n"
1585
+ << " std::uint32_t frame_budget\n"
1586
+ << ") {\n"
1587
+ << " " << namespace_name
1588
+ << "::populate_invocation_descriptor(node_index, frame_budget);\n"
1589
+ << " return static_cast<std::uint32_t>(\n"
1590
+ << " " << namespace_name
1591
+ << "::kCurrentInvocationDescriptor.frame_count\n"
1592
+ << " );\n"
1593
+ << "}\n\n";
1594
+ out << "extern \"C\" void sdn_flow_reset_runtime_state() {\n"
1595
+ << " " << namespace_name << "::clear_invocation_descriptor();\n"
1596
+ << " for (std::size_t index = 0; index < " << namespace_name
1597
+ << "::kRuntimeDescriptor.ingress_runtime_state_count; ++index) {\n"
1598
+ << " " << namespace_name
1599
+ << "::kIngressRuntimeStates[index].total_received = 0;\n"
1600
+ << " " << namespace_name
1601
+ << "::kIngressRuntimeStates[index].total_dropped = 0;\n"
1602
+ << " " << namespace_name
1603
+ << "::kIngressRuntimeStates[index].queued_frames = 0;\n"
1604
+ << " " << namespace_name
1605
+ << "::clear_frame_descriptor(" << namespace_name
1606
+ << "::kIngressFrameDescriptors[index]);\n"
1607
+ << " }\n"
1608
+ << " for (std::size_t index = 0; index < " << namespace_name
1609
+ << "::kRuntimeDescriptor.node_runtime_state_count; ++index) {\n"
1610
+ << " " << namespace_name
1611
+ << "::kNodeRuntimeStates[index].invocation_count = 0;\n"
1612
+ << " " << namespace_name
1613
+ << "::kNodeRuntimeStates[index].consumed_frames = 0;\n"
1614
+ << " " << namespace_name
1615
+ << "::kNodeRuntimeStates[index].queued_frames = 0;\n"
1616
+ << " " << namespace_name
1617
+ << "::kNodeRuntimeStates[index].backlog_remaining = 0;\n"
1618
+ << " " << namespace_name
1619
+ << "::kNodeRuntimeStates[index].last_status = 0;\n"
1620
+ << " " << namespace_name
1621
+ << "::kNodeRuntimeStates[index].ready = false;\n"
1622
+ << " " << namespace_name
1623
+ << "::kNodeRuntimeStates[index].yielded = false;\n"
1624
+ << " }\n"
1625
+ << "}\n\n";
1626
+ out << "extern \"C\" std::uint32_t sdn_flow_enqueue_trigger_frames("
1627
+ "std::uint32_t trigger_index, std::uint32_t frame_count) {\n"
1628
+ << " std::uint32_t routed_binding_count = 0;\n"
1629
+ << " for (std::size_t binding_index = 0; binding_index < "
1630
+ << namespace_name << "::kRuntimeDescriptor.trigger_binding_count; "
1631
+ "++binding_index) {\n"
1632
+ << " const " << namespace_name
1633
+ << "::FlowTriggerBindingDescriptor & binding =\n"
1634
+ << " " << namespace_name
1635
+ << "::kTriggerBindingDescriptors[binding_index];\n"
1636
+ << " if (binding.trigger_index != trigger_index) {\n"
1637
+ << " continue;\n"
1638
+ << " }\n"
1639
+ << " if (binding.target_ingress_index == " << namespace_name
1640
+ << "::kInvalidIndex) {\n"
1641
+ << " continue;\n"
1642
+ << " }\n"
1643
+ << " " << namespace_name
1644
+ << "::apply_backpressure(binding.target_ingress_index, frame_count);\n"
1645
+ << " if (binding.target_node_index != " << namespace_name
1646
+ << "::kInvalidIndex) {\n"
1647
+ << " " << namespace_name
1648
+ << "::recompute_node_runtime_state(binding.target_node_index);\n"
1649
+ << " }\n"
1650
+ << " routed_binding_count += 1;\n"
1651
+ << " }\n"
1652
+ << " return routed_binding_count;\n"
1653
+ << "}\n\n";
1654
+ out << "extern \"C\" std::uint32_t sdn_flow_enqueue_trigger_frame(\n"
1655
+ << " std::uint32_t trigger_index,\n"
1656
+ << " const " << namespace_name << "::FlowFrameDescriptor * frame\n"
1657
+ << ") {\n"
1658
+ << " if (frame == nullptr) {\n"
1659
+ << " return 0;\n"
1660
+ << " }\n"
1661
+ << " std::uint32_t routed_binding_count = 0;\n"
1662
+ << " for (std::size_t binding_index = 0; binding_index < "
1663
+ << namespace_name << "::kRuntimeDescriptor.trigger_binding_count; "
1664
+ "++binding_index) {\n"
1665
+ << " const " << namespace_name
1666
+ << "::FlowTriggerBindingDescriptor & binding =\n"
1667
+ << " " << namespace_name
1668
+ << "::kTriggerBindingDescriptors[binding_index];\n"
1669
+ << " if (binding.trigger_index != trigger_index) {\n"
1670
+ << " continue;\n"
1671
+ << " }\n"
1672
+ << " if (binding.target_ingress_index == " << namespace_name
1673
+ << "::kInvalidIndex) {\n"
1674
+ << " continue;\n"
1675
+ << " }\n"
1676
+ << " " << namespace_name
1677
+ << "::stage_ingress_frame(binding.target_ingress_index, frame);\n"
1678
+ << " " << namespace_name
1679
+ << "::apply_backpressure(binding.target_ingress_index, 1u);\n"
1680
+ << " if (binding.target_node_index != " << namespace_name
1681
+ << "::kInvalidIndex) {\n"
1682
+ << " " << namespace_name
1683
+ << "::recompute_node_runtime_state(binding.target_node_index);\n"
1684
+ << " }\n"
1685
+ << " routed_binding_count += 1;\n"
1686
+ << " }\n"
1687
+ << " return routed_binding_count;\n"
1688
+ << "}\n\n";
1689
+ out << "extern \"C\" std::uint32_t sdn_flow_enqueue_edge_frames("
1690
+ "std::uint32_t edge_index, std::uint32_t frame_count) {\n"
1691
+ << " if (edge_index >= " << namespace_name
1692
+ << "::kRuntimeDescriptor.edge_count) {\n"
1693
+ << " return 0;\n"
1694
+ << " }\n"
1695
+ << " const " << namespace_name
1696
+ << "::FlowEdgeDescriptor & edge = " << namespace_name
1697
+ << "::kEdgeDescriptors[edge_index];\n"
1698
+ << " if (edge.target_ingress_index == " << namespace_name
1699
+ << "::kInvalidIndex) {\n"
1700
+ << " return 0;\n"
1701
+ << " }\n"
1702
+ << " " << namespace_name
1703
+ << "::apply_backpressure(edge.target_ingress_index, frame_count);\n"
1704
+ << " if (edge.to_node_index != " << namespace_name
1705
+ << "::kInvalidIndex) {\n"
1706
+ << " " << namespace_name
1707
+ << "::recompute_node_runtime_state(edge.to_node_index);\n"
1708
+ << " }\n"
1709
+ << " return 1;\n"
1710
+ << "}\n\n";
1711
+ out << "extern \"C\" std::uint32_t sdn_flow_enqueue_edge_frame(\n"
1712
+ << " std::uint32_t edge_index,\n"
1713
+ << " const " << namespace_name << "::FlowFrameDescriptor * frame\n"
1714
+ << ") {\n"
1715
+ << " if (frame == nullptr || edge_index >= " << namespace_name
1716
+ << "::kRuntimeDescriptor.edge_count) {\n"
1717
+ << " return 0;\n"
1718
+ << " }\n"
1719
+ << " const " << namespace_name
1720
+ << "::FlowEdgeDescriptor & edge = " << namespace_name
1721
+ << "::kEdgeDescriptors[edge_index];\n"
1722
+ << " if (edge.target_ingress_index == " << namespace_name
1723
+ << "::kInvalidIndex) {\n"
1724
+ << " return 0;\n"
1725
+ << " }\n"
1726
+ << " " << namespace_name
1727
+ << "::stage_ingress_frame(edge.target_ingress_index, frame);\n"
1728
+ << " " << namespace_name
1729
+ << "::apply_backpressure(edge.target_ingress_index, 1u);\n"
1730
+ << " if (edge.to_node_index != " << namespace_name
1731
+ << "::kInvalidIndex) {\n"
1732
+ << " " << namespace_name
1733
+ << "::recompute_node_runtime_state(edge.to_node_index);\n"
1734
+ << " }\n"
1735
+ << " return 1;\n"
1736
+ << "}\n\n";
1737
+ out << "extern \"C\" std::uint32_t sdn_flow_get_ready_node_index() {\n"
1738
+ << " " << namespace_name << "::recompute_all_node_runtime_state();\n"
1739
+ << " for (std::uint32_t node_index = 0; node_index < " << namespace_name
1740
+ << "::kRuntimeDescriptor.node_count; ++node_index) {\n"
1741
+ << " if (" << namespace_name
1742
+ << "::kNodeRuntimeStates[node_index].ready) {\n"
1743
+ << " return node_index;\n"
1744
+ << " }\n"
1745
+ << " }\n"
1746
+ << " return " << namespace_name << "::kInvalidIndex;\n"
1747
+ << "}\n\n";
1748
+ out << "extern \"C\" std::uint32_t sdn_flow_begin_node_invocation("
1749
+ "std::uint32_t node_index, std::uint32_t frame_budget) {\n"
1750
+ << " if (node_index >= " << namespace_name
1751
+ << "::kRuntimeDescriptor.node_count) {\n"
1752
+ << " return 0;\n"
1753
+ << " }\n\n"
1754
+ << " " << namespace_name
1755
+ << "::populate_invocation_descriptor(node_index, frame_budget);\n"
1756
+ << " " << namespace_name
1757
+ << "::FlowNodeRuntimeState & node_state =\n"
1758
+ << " " << namespace_name << "::kNodeRuntimeStates[node_index];\n"
1759
+ << " const " << namespace_name
1760
+ << "::FlowNodeDescriptor & node_descriptor =\n"
1761
+ << " " << namespace_name << "::kNodeDescriptors[node_index];\n"
1762
+ << " const std::uint32_t budget = frame_budget == 0 ? 1u : frame_budget;\n"
1763
+ << " std::uint32_t consumed = 0;\n\n"
1764
+ << " for (\n"
1765
+ << " std::uint32_t offset = 0;\n"
1766
+ << " offset < node_descriptor.ingress_index_count && consumed < budget;\n"
1767
+ << " ++offset\n"
1768
+ << " ) {\n"
1769
+ << " const std::uint32_t ingress_index =\n"
1770
+ << " " << namespace_name
1771
+ << "::kNodeIngressIndices[node_descriptor.ingress_index_offset + offset];\n"
1772
+ << " " << namespace_name
1773
+ << "::FlowIngressRuntimeState & ingress_state =\n"
1774
+ << " " << namespace_name
1775
+ << "::kIngressRuntimeStates[ingress_index];\n"
1776
+ << " if (ingress_state.queued_frames == 0) {\n"
1777
+ << " continue;\n"
1778
+ << " }\n"
1779
+ << " const std::uint32_t taken =\n"
1780
+ << " " << namespace_name
1781
+ << "::min_u32(ingress_state.queued_frames, budget - consumed);\n"
1782
+ << " ingress_state.queued_frames -= taken;\n"
1783
+ << " consumed += taken;\n"
1784
+ << " }\n\n"
1785
+ << " if (consumed > 0) {\n"
1786
+ << " node_state.invocation_count += 1;\n"
1787
+ << " node_state.consumed_frames += consumed;\n"
1788
+ << " }\n"
1789
+ << " node_state.backlog_remaining = 0;\n"
1790
+ << " node_state.yielded = false;\n"
1791
+ << " " << namespace_name
1792
+ << "::recompute_node_runtime_state(node_index);\n"
1793
+ << " return consumed;\n"
1794
+ << "}\n\n";
1795
+ out << "extern \"C\" void sdn_flow_complete_node_invocation(\n"
1796
+ << " std::uint32_t node_index,\n"
1797
+ << " std::uint32_t status_code,\n"
1798
+ << " std::uint32_t backlog_remaining,\n"
1799
+ << " bool yielded\n"
1800
+ << ") {\n"
1801
+ << " if (node_index >= " << namespace_name
1802
+ << "::kRuntimeDescriptor.node_count) {\n"
1803
+ << " return;\n"
1804
+ << " }\n"
1805
+ << " " << namespace_name
1806
+ << "::FlowNodeRuntimeState & node_state =\n"
1807
+ << " " << namespace_name << "::kNodeRuntimeStates[node_index];\n"
1808
+ << " node_state.last_status = status_code;\n"
1809
+ << " node_state.backlog_remaining = backlog_remaining;\n"
1810
+ << " node_state.yielded = yielded;\n"
1811
+ << " " << namespace_name << "::clear_invocation_descriptor();\n"
1812
+ << " " << namespace_name
1813
+ << "::recompute_node_runtime_state(node_index);\n"
1814
+ << "}\n\n";
1815
+ out << "extern \"C\" std::uint32_t sdn_flow_apply_node_invocation_result(\n"
1816
+ << " std::uint32_t node_index,\n"
1817
+ << " std::uint32_t status_code,\n"
1818
+ << " std::uint32_t backlog_remaining,\n"
1819
+ << " bool yielded,\n"
1820
+ << " const " << namespace_name << "::FlowFrameDescriptor * output_frames,\n"
1821
+ << " std::uint32_t output_frame_count\n"
1822
+ << ") {\n"
1823
+ << " if (node_index >= " << namespace_name
1824
+ << "::kRuntimeDescriptor.node_count) {\n"
1825
+ << " return 0;\n"
1826
+ << " }\n"
1827
+ << " std::uint32_t routed = 0;\n"
1828
+ << " if (output_frames != nullptr) {\n"
1829
+ << " for (std::uint32_t index = 0; index < output_frame_count; ++index) {\n"
1830
+ << " routed += " << namespace_name
1831
+ << "::route_output_frame(node_index, &output_frames[index]);\n"
1832
+ << " }\n"
1833
+ << " }\n"
1834
+ << " sdn_flow_complete_node_invocation(\n"
1835
+ << " node_index,\n"
1836
+ << " status_code,\n"
1837
+ << " backlog_remaining,\n"
1838
+ << " yielded\n"
1839
+ << " );\n"
1840
+ << " return routed;\n"
1841
+ << "}\n\n";
1842
+ out << "extern \"C\" std::uint32_t sdn_flow_dispatch_next_ready_node_with_host(\n"
1843
+ << " std::uint32_t frame_budget,\n"
1844
+ << " std::uint32_t output_stream_cap\n"
1845
+ << ") {\n"
1846
+ << " const std::uint32_t node_index = sdn_flow_get_ready_node_index();\n"
1847
+ << " if (node_index == " << namespace_name << "::kInvalidIndex) {\n"
1848
+ << " return " << namespace_name << "::kInvalidIndex;\n"
1849
+ << " }\n"
1850
+ << " sdn_flow_begin_node_invocation(node_index, frame_budget);\n"
1851
+ << " sdn_flow_host_dispatch_current_invocation(output_stream_cap);\n"
1852
+ << " return node_index;\n"
1853
+ << "}\n\n";
1854
+ out << "extern \"C\" std::uint32_t sdn_flow_drain_with_host_dispatch(\n"
1855
+ << " std::uint32_t frame_budget,\n"
1856
+ << " std::uint32_t output_stream_cap,\n"
1857
+ << " std::uint32_t max_iterations\n"
1858
+ << ") {\n"
1859
+ << " const std::uint32_t iteration_limit = max_iterations == 0 ? 1024u : max_iterations;\n"
1860
+ << " std::uint32_t executed = 0;\n"
1861
+ << " while (executed < iteration_limit) {\n"
1862
+ << " const std::uint32_t node_index =\n"
1863
+ << " sdn_flow_dispatch_next_ready_node_with_host(frame_budget, output_stream_cap);\n"
1864
+ << " if (node_index == " << namespace_name << "::kInvalidIndex) {\n"
1865
+ << " break;\n"
1866
+ << " }\n"
1867
+ << " executed += 1;\n"
1868
+ << " }\n"
1869
+ << " return executed;\n"
1870
+ << "}\n\n";
1871
+ out << "extern \"C\" const " << namespace_name
1872
+ << "::FlowRuntimeDescriptor * sdn_flow_get_runtime_descriptor() {\n"
1873
+ << " return &" << namespace_name << "::kRuntimeDescriptor;\n"
1874
+ << "}\n\n";
1875
+ out << "static bool sdn_flow_arg_equals(const char * value, const char * expected) {\n"
1876
+ << " return " << namespace_name << "::string_equals(value, expected);\n"
1877
+ << "}\n\n";
1878
+ out << "static bool sdn_flow_arg_starts_with(const char * value, const char * prefix) {\n"
1879
+ << " if (value == nullptr || prefix == nullptr) {\n"
1880
+ << " return false;\n"
1881
+ << " }\n"
1882
+ << " while (*prefix != '\\0') {\n"
1883
+ << " if (*value == '\\0' || *value != *prefix) {\n"
1884
+ << " return false;\n"
1885
+ << " }\n"
1886
+ << " ++value;\n"
1887
+ << " ++prefix;\n"
1888
+ << " }\n"
1889
+ << " return true;\n"
1890
+ << "}\n\n";
1891
+ out << "static std::uint32_t sdn_flow_parse_u32(const char * value, std::uint32_t fallback) {\n"
1892
+ << " if (value == nullptr || *value == '\\0') {\n"
1893
+ << " return fallback;\n"
1894
+ << " }\n"
1895
+ << " std::uint64_t parsed = 0;\n"
1896
+ << " while (*value != '\\0') {\n"
1897
+ << " if (*value < '0' || *value > '9') {\n"
1898
+ << " return fallback;\n"
1899
+ << " }\n"
1900
+ << " parsed = parsed * 10u + static_cast<std::uint64_t>(*value - '0');\n"
1901
+ << " if (parsed > 0xffffffffu) {\n"
1902
+ << " return fallback;\n"
1903
+ << " }\n"
1904
+ << " ++value;\n"
1905
+ << " }\n"
1906
+ << " return static_cast<std::uint32_t>(parsed);\n"
1907
+ << "}\n\n";
1908
+ out << "int main(int argc, char ** argv) {\n"
1909
+ << " std::uint32_t frame_budget = 1u;\n"
1910
+ << " std::uint32_t output_stream_cap = 16u;\n"
1911
+ << " std::uint32_t max_iterations = 1024u;\n"
1912
+ << " for (int index = 1; index < argc; ++index) {\n"
1913
+ << " const char * argument = argv[index];\n"
1914
+ << " if (sdn_flow_arg_equals(argument, \"--reset\")) {\n"
1915
+ << " sdn_flow_reset_runtime_state();\n"
1916
+ << " continue;\n"
1917
+ << " }\n"
1918
+ << " if (sdn_flow_arg_starts_with(argument, \"--frame-budget=\")) {\n"
1919
+ << " frame_budget = sdn_flow_parse_u32(argument + 15, frame_budget);\n"
1920
+ << " continue;\n"
1921
+ << " }\n"
1922
+ << " if (sdn_flow_arg_starts_with(argument, \"--output-stream-cap=\")) {\n"
1923
+ << " output_stream_cap = sdn_flow_parse_u32(argument + 20, output_stream_cap);\n"
1924
+ << " continue;\n"
1925
+ << " }\n"
1926
+ << " if (sdn_flow_arg_starts_with(argument, \"--max-iterations=\")) {\n"
1927
+ << " max_iterations = sdn_flow_parse_u32(argument + 17, max_iterations);\n"
1928
+ << " continue;\n"
1929
+ << " }\n"
1930
+ << " }\n"
1931
+ << " sdn_flow_drain_with_host_dispatch(\n"
1932
+ << " frame_budget,\n"
1933
+ << " output_stream_cap,\n"
1934
+ << " max_iterations\n"
1935
+ << " );\n"
1936
+ << " return 0;\n"
1937
+ << "}\n";
1938
+ return out.str();
1939
+ }
1940
+
1941
+ } // namespace
1942
+
1943
+ int main(int argc, char** argv) {
1944
+ try {
1945
+ if (argc < 3) {
1946
+ std::cerr << "usage: flow-source-generator <request.bin> <output.cpp>\n";
1947
+ return 64;
1948
+ }
1949
+ BinaryReader reader(readFileBytes(argv[1]));
1950
+ const Request request = readRequest(reader);
1951
+ const std::string output = generateSource(request);
1952
+ writeFileString(argv[2], output);
1953
+ return 0;
1954
+ } catch (const std::exception& error) {
1955
+ std::cerr << "flow source generator failed: " << error.what() << '\n';
1956
+ return 1;
1957
+ }
1958
+ }