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_loader.hpp>
16 : :
17 : : #include <fcntl.h>
18 : : #include <sys/mman.h>
19 : : #include <sys/stat.h>
20 : : #include <unistd.h>
21 : :
22 : : namespace hpactor::config {
23 : :
24 : : // -----------------------------------------------------------------------------
25 : : // Binary format types (must match binary_format.hpp)
26 : : // -----------------------------------------------------------------------------
27 : : namespace {
28 : :
29 : : constexpr uint32_t MAGIC = 0x48504154; // "HPAT"
30 : :
31 : : struct RawHeader {
32 : : uint32_t magic;
33 : : uint32_t version;
34 : : uint32_t system_offset;
35 : : uint32_t dispatcher_count;
36 : : uint32_t dispatchers_offset;
37 : : uint32_t actor_count;
38 : : uint32_t actors_offset;
39 : : uint32_t string_table_offset;
40 : : uint32_t string_table_size;
41 : : };
42 : :
43 : : struct RawSystemDef {
44 : : uint32_t scheduler_threads;
45 : : uint32_t max_queue_depth;
46 : : uint32_t default_mailbox_size;
47 : : uint32_t enable_network;
48 : : uint16_t tcp_port;
49 : : uint16_t reserved_pad;
50 : : uint32_t spawn_timeout_ms;
51 : : uint32_t enable_http_gateway;
52 : : uint16_t http_port;
53 : : uint32_t http_max_connections;
54 : : uint32_t http_max_request_size;
55 : : uint32_t http_reply_timeout_ms;
56 : : uint32_t use_coroutines;
57 : : uint32_t version_offset;
58 : : union {
59 : : uint32_t http_bind_host; // X-macro compatible name
60 : : uint32_t http_bind_host_offset; // string table offset (canonical)
61 : : };
62 : : uint32_t tracing_enabled;
63 : : uint32_t tracing_propagate_unsampled;
64 : : uint32_t tracing_ring_buffer_capacity;
65 : : uint32_t tracing_sampler;
66 : : uint32_t tracing_exporter;
67 : : double tracing_sample_ratio;
68 : : uint32_t tracing_export_interval_ms;
69 : : uint32_t tracing_max_export_batch_size;
70 : : uint16_t tracing_max_tracestate_len;
71 : : uint16_t tracing_pad;
72 : : uint32_t tracing_flags;
73 : : uint32_t tracing_service_name_offset;
74 : : uint32_t tracing_otlp_endpoint_offset;
75 : : uint32_t tracing_json_file_path_offset;
76 : : };
77 : :
78 : : struct RawDispatcher {
79 : : uint32_t name_offset;
80 : : uint16_t threads;
81 : : uint16_t cpu_affinity_count;
82 : : uint32_t cpu_affinity_offset;
83 : : };
84 : :
85 : : struct RawActor {
86 : : uint32_t id_offset;
87 : : uint32_t behavior_offset;
88 : : uint32_t supervisor_offset;
89 : : uint32_t dispatcher_offset;
90 : : uint8_t dispatch_policy;
91 : : uint32_t mailbox_capacity;
92 : : uint32_t slab_class_bytes;
93 : : uint32_t max_memory_kb;
94 : : uint16_t args_count;
95 : : uint32_t args_offset;
96 : : };
97 : :
98 : : struct RawKV {
99 : : uint32_t key_offset;
100 : : uint32_t value_offset;
101 : : };
102 : :
103 : 39 : inline const char* str_at(const uint8_t* str_table, uint32_t offset) {
104 : 39 : return offset ? reinterpret_cast<const char*>(str_table + offset) : "";
105 : : }
106 : :
107 : : // Assign Dst from Src when the types are directly compatible; silently skip
108 : : // otherwise. Catches mismatches like std::string = uint32_t (string is
109 : : // assignable from char = int, but that's not what we want for binary offset
110 : : // fields).
111 : : template <typename Dst, typename Src>
112 : 33 : void assign_or_skip(Dst& dst, const Src& src) {
113 : : if constexpr (std::is_assignable_v<Dst&, const Src&> &&
114 : : !(std::is_arithmetic_v<Src> && std::is_same_v<Dst, std::string>)) {
115 : 30 : dst = static_cast<Dst>(src);
116 : : }
117 : 33 : }
118 : :
119 : : } // anonymous namespace
120 : :
121 : : // -----------------------------------------------------------------------------
122 : : // load_binary_topology — mmap a .bin file into a TopologyModel
123 : : // -----------------------------------------------------------------------------
124 : 3 : result<TopologyModel> load_binary_topology(const std::string& path) {
125 : 3 : int fd = open(path.c_str(), O_RDONLY);
126 : 3 : if (fd < 0) {
127 : 0 : return result<TopologyModel>::make(error(errors::unknown));
128 : : }
129 : :
130 : : struct stat st;
131 : 3 : if (fstat(fd, &st) < 0) {
132 : 0 : close(fd);
133 : 0 : return result<TopologyModel>::make(error(errors::unknown));
134 : : }
135 : :
136 : 3 : size_t file_size = static_cast<size_t>(st.st_size);
137 : 3 : void* mapped = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
138 : 3 : close(fd);
139 : :
140 : 3 : if (mapped == MAP_FAILED) {
141 : 0 : return result<TopologyModel>::make(error(errors::unknown));
142 : : }
143 : :
144 : 3 : const uint8_t* base = static_cast<const uint8_t*>(mapped);
145 : 3 : const RawHeader* hdr = reinterpret_cast<const RawHeader*>(base);
146 : :
147 : 3 : if (hdr->magic != MAGIC) {
148 : 0 : munmap(mapped, file_size);
149 : 0 : return result<TopologyModel>::make(error(errors::unknown));
150 : : }
151 : :
152 : 3 : const uint8_t* str_table = base + hdr->string_table_offset;
153 : 3 : TopologyModel model;
154 : :
155 : : // System config
156 : 3 : const RawSystemDef* rsys =
157 : 3 : reinterpret_cast<const RawSystemDef*>(base + hdr->system_offset);
158 : 3 : model.system.version = str_at(str_table, rsys->version_offset);
159 : :
160 : : // Shared scalar fields (generated from system_toml_fields.def)
161 : : // NOLINTBEGIN(cppcoreguidelines-macro-usage)
162 : : #define HPACTOR_SYSTEM_TOML_FIELD(name, type, toml, def) \
163 : : assign_or_skip(model.system.name, rsys->name);
164 : : #include <hpactor/config/system_toml_fields.def>
165 : : #undef HPACTOR_SYSTEM_TOML_FIELD
166 : : // NOLINTEND(cppcoreguidelines-macro-usage)
167 : :
168 : : // SystemDef-only fields (not in the shared X-macro table)
169 : 3 : model.system.default_mailbox_size = rsys->default_mailbox_size;
170 : 3 : model.system.use_coroutines = rsys->use_coroutines != 0;
171 : :
172 : : // String-override: assign_or_skip skips string fields (string != uint32_t),
173 : : // so apply the string-table lookup manually.
174 : 3 : model.system.http_bind_host = str_at(str_table, rsys->http_bind_host_offset);
175 : :
176 : : // Tracing config (zero-filled for older binary versions = disabled)
177 : 3 : model.system.tracing.enabled = rsys->tracing_enabled != 0;
178 : 3 : model.system.tracing.propagate_unsampled =
179 : 3 : rsys->tracing_propagate_unsampled != 0;
180 : 3 : model.system.tracing.ring_buffer_capacity = rsys->tracing_ring_buffer_capacity;
181 : 3 : model.system.tracing.sampler =
182 : 3 : static_cast<hpactor::tracing::SamplerKind>(rsys->tracing_sampler);
183 : 3 : model.system.tracing.exporter =
184 : 3 : static_cast<hpactor::tracing::TraceExporterKind>(rsys->tracing_exporter);
185 : 3 : model.system.tracing.sample_ratio = rsys->tracing_sample_ratio;
186 : 3 : model.system.tracing.export_interval =
187 : 3 : std::chrono::milliseconds(rsys->tracing_export_interval_ms);
188 : 3 : model.system.tracing.max_export_batch_size = rsys->tracing_max_export_batch_size;
189 : 3 : model.system.tracing.max_tracestate_len = rsys->tracing_max_tracestate_len;
190 : : model.system.tracing.service_name =
191 : 3 : str_at(str_table, rsys->tracing_service_name_offset);
192 : : model.system.tracing.otlp_endpoint =
193 : 3 : str_at(str_table, rsys->tracing_otlp_endpoint_offset);
194 : : model.system.tracing.json_file_path =
195 : 3 : str_at(str_table, rsys->tracing_json_file_path_offset);
196 : :
197 : : // Dispatchers
198 : 3 : const RawDispatcher* dispatchers =
199 : 3 : reinterpret_cast<const RawDispatcher*>(base + hdr->dispatchers_offset);
200 : 3 : for (uint32_t i = 0; i < hdr->dispatcher_count; ++i) {
201 : 0 : DispatcherDef def;
202 : 0 : def.name = str_at(str_table, dispatchers[i].name_offset);
203 : 0 : def.threads = dispatchers[i].threads;
204 : 0 : if (dispatchers[i].cpu_affinity_count > 0 &&
205 : 0 : dispatchers[i].cpu_affinity_offset) {
206 : 0 : const uint8_t* aff = base + dispatchers[i].cpu_affinity_offset;
207 : 0 : def.cpu_affinity.assign(aff, aff + dispatchers[i].cpu_affinity_count);
208 : : }
209 : 0 : model.dispatchers.push_back(std::move(def));
210 : 0 : }
211 : :
212 : : // Actors
213 : 3 : const RawActor* actors =
214 : 3 : reinterpret_cast<const RawActor*>(base + hdr->actors_offset);
215 : 8 : for (uint32_t i = 0; i < hdr->actor_count; ++i) {
216 : 5 : const RawActor& ra = actors[i];
217 : 5 : ActorDef def;
218 : 5 : def.id = str_at(str_table, ra.id_offset);
219 : 5 : def.behavior = str_at(str_table, ra.behavior_offset);
220 : 5 : def.supervisor = str_at(str_table, ra.supervisor_offset);
221 : 5 : def.dispatcher = str_at(str_table, ra.dispatcher_offset);
222 : 5 : def.dispatch_policy = static_cast<DispatchPolicy>(ra.dispatch_policy);
223 : 5 : def.mailbox_capacity = ra.mailbox_capacity;
224 : 5 : def.resources.slab_class_bytes = ra.slab_class_bytes;
225 : 5 : def.resources.max_memory_kb = ra.max_memory_kb;
226 : :
227 : 5 : if (ra.args_count > 0 && ra.args_offset) {
228 : 1 : const RawKV* kvs =
229 : 1 : reinterpret_cast<const RawKV*>(base + ra.args_offset);
230 : 3 : for (uint16_t j = 0; j < ra.args_count; ++j) {
231 : 6 : def.args[str_at(str_table, kvs[j].key_offset)] =
232 : 4 : str_at(str_table, kvs[j].value_offset);
233 : : }
234 : : }
235 : :
236 : 5 : model.actors.push_back(std::move(def));
237 : 5 : }
238 : :
239 : 3 : munmap(mapped, file_size);
240 : 3 : return result<TopologyModel>::make(std::move(model));
241 : 3 : }
242 : :
243 : : } // namespace hpactor::config
|