Branch data Line data Source code
1 : : // Copyright 2026 HPActor Contributors
2 : : //
3 : : // Licensed under the Apache License, Version 2.0 (the "License");
4 : : // you may not use this file except in compliance with the License.
5 : : // You may obtain a copy of the License at
6 : : //
7 : : // http://www.apache.org/licenses/LICENSE-2.0
8 : : //
9 : : // Unless required by applicable law or agreed to in writing, software
10 : : // distributed under the License is distributed on an "AS IS" BASIS,
11 : : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : // See the License for the specific language governing permissions and
13 : : // limitations under the License.
14 : :
15 : : #include <hpactor/config/binary_format.hpp>
16 : : #include <hpactor/config/binary_serializer.hpp>
17 : :
18 : : #include <cstring>
19 : : #include <unordered_map>
20 : :
21 : : namespace hpactor::config {
22 : :
23 : : namespace {
24 : :
25 : : // Assign Dst from Src when the types are directly compatible; silently skip
26 : : // otherwise. Catches mismatches like std::string = uint32_t (string is
27 : : // assignable from char = int, but that's not what we want for binary offset
28 : : // fields).
29 : : template <typename Dst, typename Src>
30 : 33 : void assign_or_skip(Dst& dst, const Src& src) {
31 : : if constexpr (std::is_assignable_v<Dst&, const Src&> &&
32 : : !(std::is_arithmetic_v<Src> && std::is_same_v<Dst, std::string>)) {
33 : 30 : dst = src;
34 : : }
35 : 33 : }
36 : :
37 : : class BinaryWriter {
38 : : public:
39 : 3 : BinaryWriter() {
40 : 3 : buf_.reserve(4096);
41 : : // Reserve offset 0 as sentinel for "no string"
42 : 3 : string_table_.push_back(0);
43 : 3 : }
44 : :
45 : 39 : uint32_t add_string(const std::string& s) {
46 : 39 : if (s.empty())
47 : 11 : return 0;
48 : 28 : auto it = string_map_.find(s);
49 : 28 : if (it != string_map_.end())
50 : 3 : return it->second;
51 : 25 : uint32_t offset = static_cast<uint32_t>(string_table_.size());
52 : 25 : string_table_.insert(string_table_.end(), s.begin(), s.end());
53 : 25 : string_table_.push_back('\0');
54 : 25 : string_map_[s] = offset;
55 : 25 : return offset;
56 : : }
57 : :
58 : 13 : template <typename T> uint32_t write(const T& val) {
59 : 13 : uint32_t offset = static_cast<uint32_t>(buf_.size());
60 : 13 : const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&val);
61 : 13 : buf_.insert(buf_.end(), ptr, ptr + sizeof(T));
62 : 13 : return offset;
63 : : }
64 : :
65 : 0 : uint32_t write_bytes(const uint8_t* data, size_t len) {
66 : 0 : uint32_t offset = static_cast<uint32_t>(buf_.size());
67 : 0 : buf_.insert(buf_.end(), data, data + len);
68 : 0 : return offset;
69 : : }
70 : :
71 : 7 : uint32_t current_offset() const {
72 : 7 : return static_cast<uint32_t>(buf_.size());
73 : : }
74 : :
75 : 3 : void finalize(std::vector<uint8_t>& out) {
76 : : // Append string table
77 : 3 : uint32_t string_table_offset = static_cast<uint32_t>(buf_.size());
78 : 3 : uint32_t string_table_size = static_cast<uint32_t>(string_table_.size());
79 : 3 : buf_.insert(buf_.end(), string_table_.begin(), string_table_.end());
80 : :
81 : : // Update header with final offsets
82 : 3 : BinaryHeader* hdr = reinterpret_cast<BinaryHeader*>(buf_.data());
83 : 3 : hdr->string_table_offset = string_table_offset;
84 : 3 : hdr->string_table_size = string_table_size;
85 : :
86 : 3 : out = std::move(buf_);
87 : 3 : }
88 : :
89 : : private:
90 : : std::vector<uint8_t> buf_;
91 : : std::vector<uint8_t> string_table_;
92 : : std::unordered_map<std::string, uint32_t> string_map_;
93 : : };
94 : :
95 : : } // anonymous namespace
96 : :
97 : 3 : std::vector<uint8_t> serialize_topology(const TopologyModel& model) {
98 : 3 : BinaryWriter w;
99 : :
100 : : // Reserve header space (filled at end)
101 : 3 : uint32_t header_offset = w.write(BinaryHeader{});
102 : : (void)header_offset;
103 : :
104 : : // SystemDef
105 : 3 : BinarySystemDef bsys{};
106 : :
107 : : // Shared scalar fields (generated from system_toml_fields.def)
108 : : // NOLINTBEGIN(cppcoreguidelines-macro-usage)
109 : : #define HPACTOR_SYSTEM_TOML_FIELD(name, type, toml, def) \
110 : : assign_or_skip(bsys.name, model.system.name);
111 : : #include <hpactor/config/system_toml_fields.def>
112 : : #undef HPACTOR_SYSTEM_TOML_FIELD
113 : : // NOLINTEND(cppcoreguidelines-macro-usage)
114 : :
115 : : // SystemDef-only fields (not in the shared X-macro table)
116 : 3 : bsys.default_mailbox_size = model.system.default_mailbox_size;
117 : 3 : bsys.reserved_pad = 0;
118 : 3 : bsys.use_coroutines = model.system.use_coroutines ? 1 : 0;
119 : :
120 : : // String table fields (override X-macro-generated assignments)
121 : 3 : bsys.version_offset = w.add_string(model.system.version);
122 : 3 : bsys.http_bind_host = w.add_string(model.system.http_bind_host);
123 : : // Tracing
124 : 3 : bsys.tracing_enabled = model.system.tracing.enabled ? 1 : 0;
125 : 3 : bsys.tracing_propagate_unsampled =
126 : 3 : model.system.tracing.propagate_unsampled ? 1 : 0;
127 : 3 : bsys.tracing_ring_buffer_capacity = model.system.tracing.ring_buffer_capacity;
128 : 3 : bsys.tracing_sampler = static_cast<uint32_t>(model.system.tracing.sampler);
129 : 3 : bsys.tracing_exporter = static_cast<uint32_t>(model.system.tracing.exporter);
130 : 3 : bsys.tracing_sample_ratio = model.system.tracing.sample_ratio;
131 : 3 : bsys.tracing_export_interval_ms =
132 : 3 : static_cast<uint32_t>(model.system.tracing.export_interval.count());
133 : 3 : bsys.tracing_max_export_batch_size = model.system.tracing.max_export_batch_size;
134 : 3 : bsys.tracing_max_tracestate_len = model.system.tracing.max_tracestate_len;
135 : 3 : bsys.tracing_pad = 0;
136 : 3 : bsys.tracing_flags = 0;
137 : 3 : bsys.tracing_service_name_offset =
138 : 3 : w.add_string(model.system.tracing.service_name);
139 : 3 : bsys.tracing_otlp_endpoint_offset =
140 : 3 : w.add_string(model.system.tracing.otlp_endpoint);
141 : 3 : bsys.tracing_json_file_path_offset =
142 : 3 : w.add_string(model.system.tracing.json_file_path);
143 : 3 : uint32_t system_offset = w.write(bsys);
144 : :
145 : : // Dispatchers
146 : 3 : uint32_t dispatcher_count = static_cast<uint32_t>(model.dispatchers.size());
147 : 3 : uint32_t dispatchers_offset = w.current_offset();
148 : :
149 : 3 : for (const auto& d : model.dispatchers) {
150 : 0 : BinaryDispatcherDef bd{};
151 : 0 : bd.name_offset = w.add_string(d.name);
152 : 0 : bd.threads = d.threads;
153 : 0 : bd.cpu_affinity_count = static_cast<uint16_t>(d.cpu_affinity.size());
154 : 0 : if (!d.cpu_affinity.empty()) {
155 : 0 : bd.cpu_affinity_offset =
156 : 0 : w.write_bytes(d.cpu_affinity.data(), d.cpu_affinity.size());
157 : : }
158 : 0 : w.write(bd);
159 : : }
160 : :
161 : : // Actors
162 : 3 : uint32_t actor_count = static_cast<uint32_t>(model.actors.size());
163 : 3 : uint32_t actors_offset = w.current_offset();
164 : :
165 : 8 : for (const auto& a : model.actors) {
166 : 5 : BinaryActorDef ba{};
167 : 5 : ba.id_offset = w.add_string(a.id);
168 : 5 : ba.behavior_offset = w.add_string(a.behavior);
169 : 5 : ba.supervisor_offset = w.add_string(a.supervisor);
170 : 5 : ba.dispatcher_offset = w.add_string(a.dispatcher);
171 : 5 : ba.dispatch_policy = static_cast<uint8_t>(a.dispatch_policy);
172 : 5 : ba.mailbox_capacity = a.mailbox_capacity;
173 : 5 : ba.slab_class_bytes = a.resources.slab_class_bytes;
174 : 5 : ba.max_memory_kb = a.resources.max_memory_kb;
175 : 5 : ba.args_count = static_cast<uint16_t>(a.args.size());
176 : : // args table follows immediately after this actor def
177 : 5 : ba.args_offset = a.args.empty()
178 : 6 : ? 0
179 : 1 : : w.current_offset() +
180 : : static_cast<uint32_t>(sizeof(BinaryActorDef));
181 : 5 : w.write(ba);
182 : : // Write args table right after the actor def
183 : 7 : for (const auto& [k, v] : a.args) {
184 : 2 : BinaryKeyValue bkv{};
185 : 2 : bkv.key_offset = w.add_string(k);
186 : 2 : bkv.value_offset = w.add_string(v);
187 : 2 : w.write(bkv);
188 : : }
189 : : }
190 : :
191 : : // Finalize: append string table, update header
192 : 3 : std::vector<uint8_t> result;
193 : 3 : w.finalize(result);
194 : :
195 : : // Patch header
196 : 3 : BinaryHeader* hdr = reinterpret_cast<BinaryHeader*>(result.data());
197 : 3 : hdr->magic = TOPOLOGY_BINARY_MAGIC;
198 : 3 : hdr->version = 1;
199 : 3 : hdr->system_offset = system_offset;
200 : 3 : hdr->dispatcher_count = dispatcher_count;
201 : 3 : hdr->dispatchers_offset = dispatchers_offset;
202 : 3 : hdr->actor_count = actor_count;
203 : 3 : hdr->actors_offset = actors_offset;
204 : :
205 : 3 : return result;
206 : 3 : }
207 : :
208 : : } // namespace hpactor::config
|