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 : : #pragma once
16 : :
17 : : #include <arpa/inet.h>
18 : : #include <array>
19 : : #include <atomic>
20 : : #include <chrono>
21 : : #include <cstdint>
22 : : #include <cstring>
23 : : #include <netinet/in.h>
24 : : #include <string>
25 : : #include <string_view>
26 : : #include <variant>
27 : : #include <vector>
28 : :
29 : : #include <hpactor/adt/id.hpp>
30 : : #include <hpactor/adt/stream_buffer.hpp>
31 : : #include <hpactor/adt/tags.hpp>
32 : : #include <vector>
33 : :
34 : : namespace hpactor {
35 : :
36 : : // constexpr network-to-host byte-order conversion (ntohs is not constexpr)
37 : 183 : inline constexpr uint16_t net_to_host_u16(uint16_t net_val) noexcept {
38 : : #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
39 : 183 : return __builtin_bswap16(net_val);
40 : : #else
41 : : return net_val;
42 : : #endif
43 : : }
44 : :
45 : : // -----------------------------------------------------------------------------
46 : : // ActorId - unique identifier for an actor instance
47 : : // -----------------------------------------------------------------------------
48 : : using ActorId = Id<ActorTag>;
49 : :
50 : : // -----------------------------------------------------------------------------
51 : : // Protocol - network protocol family
52 : : // -----------------------------------------------------------------------------
53 : : enum class Protocol { IPv4, IPv6 };
54 : :
55 : : // -----------------------------------------------------------------------------
56 : : // DispatchPolicy — how an actor is dispatched to a scheduler worker
57 : : // -----------------------------------------------------------------------------
58 : : enum class DispatchPolicy : uint8_t {
59 : : Cooperative = 0,
60 : : DedicatedThread,
61 : : DedicatedPool,
62 : : };
63 : :
64 : : // -----------------------------------------------------------------------------
65 : : // Ipv4Endpoint - IPv4 address and port (network byte order)
66 : : // -----------------------------------------------------------------------------
67 : : struct Ipv4Endpoint {
68 : : uint32_t addr; // Network byte order (big-endian)
69 : : uint16_t port_nw; // Network byte order
70 : :
71 : 130 : constexpr Ipv4Endpoint() noexcept : addr(0), port_nw(0) {}
72 : 1243724 : constexpr Ipv4Endpoint(uint32_t a, uint16_t p) noexcept
73 : 1243724 : : addr(a), port_nw(p) {}
74 : :
75 : 179 : [[nodiscard]] constexpr uint16_t port() const noexcept {
76 : 179 : return net_to_host_u16(port_nw);
77 : : }
78 : 1 : [[nodiscard]] constexpr bool is_ipv4() const noexcept {
79 : 1 : return true;
80 : : }
81 : 1 : [[nodiscard]] constexpr bool is_ipv6() const noexcept {
82 : 1 : return false;
83 : : }
84 : : [[nodiscard]] constexpr bool is_loopback() const noexcept;
85 : : [[nodiscard]] constexpr bool is_private_network() const noexcept;
86 : : [[nodiscard]] constexpr bool is_unspecified() const noexcept;
87 : :
88 : : // For socket operations
89 : : constexpr socklen_t sockaddr_length() const noexcept {
90 : : return sizeof(sockaddr_in);
91 : : }
92 : : void to_sockaddr(sockaddr_in* out) const noexcept;
93 : :
94 : 132 : constexpr bool operator==(const Ipv4Endpoint& other) const noexcept {
95 : 132 : return addr == other.addr && port_nw == other.port_nw;
96 : : }
97 : 1 : constexpr bool operator!=(const Ipv4Endpoint& other) const noexcept {
98 : 1 : return !(*this == other);
99 : : }
100 : : };
101 : :
102 : 39 : [[nodiscard]] constexpr bool Ipv4Endpoint::is_loopback() const noexcept {
103 : : // Network byte order: 127.0.0.1 = 0x7F000001
104 : : // MSB (byte 0 in network order) = 0x7F
105 : : // On little-endian, addr value is still 0x7F000001, so MSB = (addr >> 24)
106 : : // On big-endian, MSB = (addr >> 24) = 0x7F
107 : : // Both give same result
108 : 39 : return (addr >> 24) == 0x7F;
109 : : }
110 : :
111 : 4 : [[nodiscard]] constexpr bool Ipv4Endpoint::is_private_network() const noexcept {
112 : 4 : auto b1 = static_cast<uint8_t>((addr >> 24) & 0xFF);
113 : 4 : uint32_t rest = addr & 0xFFFF0000;
114 : 5 : return b1 == 10 || (b1 == 172 && ((addr >> 16) & 0xFF & 0xF0) == 0x10) ||
115 : 5 : (b1 == 192 && rest == 0xC0A80000);
116 : : }
117 : :
118 : 0 : [[nodiscard]] constexpr bool Ipv4Endpoint::is_unspecified() const noexcept {
119 : 0 : return addr == 0;
120 : : }
121 : :
122 : 1 : inline void Ipv4Endpoint::to_sockaddr(sockaddr_in* out) const noexcept {
123 : 1 : out->sin_family = AF_INET;
124 : 1 : out->sin_port = port_nw;
125 : 1 : out->sin_addr.s_addr = addr;
126 : 1 : std::memset(out->sin_zero, 0, sizeof(out->sin_zero));
127 : 1 : }
128 : :
129 : : // -----------------------------------------------------------------------------
130 : : // Ipv6Endpoint - IPv6 address and port (network byte order)
131 : : // -----------------------------------------------------------------------------
132 : : struct Ipv6Endpoint {
133 : : std::array<uint8_t, 16> addr; // Network byte order
134 : : uint16_t port_nw; // Network byte order
135 : :
136 : : constexpr Ipv6Endpoint() noexcept : addr{}, port_nw(0) {}
137 : 5 : constexpr Ipv6Endpoint(std::array<uint8_t, 16> a, uint16_t p) noexcept
138 : 5 : : addr(a), port_nw(p) {}
139 : :
140 : 4 : [[nodiscard]] constexpr uint16_t port() const noexcept {
141 : 4 : return net_to_host_u16(port_nw);
142 : : }
143 : 1 : [[nodiscard]] constexpr bool is_ipv4() const noexcept {
144 : 1 : return false;
145 : : }
146 : 1 : [[nodiscard]] constexpr bool is_ipv6() const noexcept {
147 : 1 : return true;
148 : : }
149 : : [[nodiscard]] bool is_loopback() const noexcept;
150 : : [[nodiscard]] bool is_private_network() const noexcept;
151 : : [[nodiscard]] bool is_unspecified() const noexcept;
152 : :
153 : : // For socket operations
154 : : constexpr socklen_t sockaddr_length() const noexcept {
155 : : return sizeof(sockaddr_in6);
156 : : }
157 : : void to_sockaddr(sockaddr_in6* out) const noexcept;
158 : :
159 : 0 : bool operator==(const Ipv6Endpoint& other) const noexcept {
160 : 0 : return addr == other.addr && port_nw == other.port_nw;
161 : : }
162 : 0 : bool operator!=(const Ipv6Endpoint& other) const noexcept {
163 : 0 : return !(*this == other);
164 : : }
165 : : };
166 : :
167 : 3 : inline bool Ipv6Endpoint::is_loopback() const noexcept {
168 : 48 : for (std::size_t i = 0; i < 15; ++i)
169 : 45 : if (addr[i] != 0)
170 : 0 : return false;
171 : 3 : return addr[15] == 1;
172 : : }
173 : :
174 : : inline bool Ipv6Endpoint::is_private_network() const noexcept {
175 : : return (addr[0] & 0xFE) == 0xFC;
176 : : }
177 : :
178 : : inline bool Ipv6Endpoint::is_unspecified() const noexcept {
179 : : for (std::size_t i = 0; i < 16; ++i)
180 : : if (addr[i] != 0)
181 : : return false;
182 : : return true;
183 : : }
184 : :
185 : 1 : inline void Ipv6Endpoint::to_sockaddr(sockaddr_in6* out) const noexcept {
186 : 1 : out->sin6_family = AF_INET6;
187 : 1 : out->sin6_port = port_nw;
188 : 1 : std::memcpy(out->sin6_addr.s6_addr, addr.data(), 16);
189 : 1 : out->sin6_flowinfo = 0;
190 : 1 : out->sin6_scope_id = 0;
191 : 1 : }
192 : :
193 : : // -----------------------------------------------------------------------------
194 : : // EndPoint - variant over IPv4/IPv6
195 : : // -----------------------------------------------------------------------------
196 : : using EndPoint = std::variant<Ipv4Endpoint, Ipv6Endpoint>;
197 : :
198 : : // -----------------------------------------------------------------------------
199 : : // LocalEndpoint - loopback endpoint for local actor communication
200 : : // -----------------------------------------------------------------------------
201 : : inline constexpr Ipv4Endpoint LocalEndpoint{0x7F000001, 0}; // 127.0.0.1:0 in
202 : : // network byte
203 : : // order
204 : :
205 : : // to_sockaddr free function for variant
206 : : inline void to_sockaddr(const EndPoint& ep, sockaddr* out, socklen_t* len) {
207 : : if (auto* ipv4 = std::get_if<Ipv4Endpoint>(&ep)) {
208 : : if (*len >= sizeof(sockaddr_in)) {
209 : : ipv4->to_sockaddr(reinterpret_cast<sockaddr_in*>(out));
210 : : *len = sizeof(sockaddr_in);
211 : : }
212 : : } else if (auto* ipv6 = std::get_if<Ipv6Endpoint>(&ep)) {
213 : : if (*len >= sizeof(sockaddr_in6)) {
214 : : ipv6->to_sockaddr(reinterpret_cast<sockaddr_in6*>(out));
215 : : *len = sizeof(sockaddr_in6);
216 : : }
217 : : }
218 : : }
219 : :
220 : : // -----------------------------------------------------------------------------
221 : : // EndPoint operations (implemented in cpp)
222 : : // -----------------------------------------------------------------------------
223 : : namespace endpoint_ops {
224 : : [[nodiscard]] Protocol protocol(const EndPoint& ep);
225 : : [[nodiscard]] int address_family(const EndPoint& ep);
226 : : [[nodiscard]] std::string to_string(const EndPoint& ep);
227 : : [[nodiscard]] EndPoint parse_endpoint(std::string_view node_id);
228 : : } // namespace endpoint_ops
229 : :
230 : : // -----------------------------------------------------------------------------
231 : : // ActorType - type identifier for an actor
232 : : // -----------------------------------------------------------------------------
233 : : using ActorType = uint32_t;
234 : : constexpr ActorType InvalidActorType = 0;
235 : :
236 : : // -----------------------------------------------------------------------------
237 : : // incarnation_type - version counter for actor lifecycle
238 : : // -----------------------------------------------------------------------------
239 : : using incarnation_type = uint64_t;
240 : :
241 : : // -----------------------------------------------------------------------------
242 : : // MessageId - unique identifier for a message
243 : : // -----------------------------------------------------------------------------
244 : : using MessageId = Id<MessageTag>;
245 : :
246 : 17 : inline MessageId generate_message_id() {
247 : : static std::atomic<uint64_t> next_id_{1};
248 : 17 : return MessageId(next_id_.fetch_add(1));
249 : : }
250 : :
251 : : // -----------------------------------------------------------------------------
252 : : // error - error code wrapper (no exceptions in hot path)
253 : : // -----------------------------------------------------------------------------
254 : : class error {
255 : : public:
256 : 1077 : error() = default;
257 : :
258 : 62 : explicit error(uint32_t code, std::string msg = {})
259 : 62 : : code_(code), message_(std::move(msg)) {}
260 : :
261 : 9 : uint32_t code() const {
262 : 9 : return code_;
263 : : }
264 : 2 : const std::string& message() const {
265 : 2 : return message_;
266 : : }
267 : :
268 : 4 : bool ok() const {
269 : 4 : return code_ == 0;
270 : : }
271 : 2 : explicit operator bool() const {
272 : 2 : return !ok();
273 : : }
274 : :
275 : : private:
276 : : uint32_t code_ = 0;
277 : : std::string message_;
278 : : };
279 : :
280 : : // -----------------------------------------------------------------------------
281 : : // errors namespace - canonical error codes
282 : : // -----------------------------------------------------------------------------
283 : : namespace errors {
284 : : constexpr uint32_t unknown = 1;
285 : : constexpr uint32_t actor_down = 2;
286 : : constexpr uint32_t actor_not_found = 3;
287 : : constexpr uint32_t mailbox_full = 4;
288 : : constexpr uint32_t timeout = 5;
289 : : constexpr uint32_t invalid_argument = 6;
290 : :
291 : : // HTTP protocol errors
292 : : constexpr uint32_t http_parse_error = 2001;
293 : : constexpr uint32_t http_connect_failed = 2002;
294 : : constexpr uint32_t http_timeout = 2003;
295 : :
296 : : constexpr uint32_t user = 1000;
297 : : } // namespace errors
298 : :
299 : : // -----------------------------------------------------------------------------
300 : : // result<T> - return type for message handlers
301 : : // -----------------------------------------------------------------------------
302 : : template <typename T> class result {
303 : : public:
304 : 167 : static result<T> make(T&& value) {
305 : 167 : return result<T>(std::move(value));
306 : : }
307 : 18 : static result<T> make(class error err) {
308 : 18 : return result<T>(std::move(err));
309 : : }
310 : :
311 : 165 : bool has_value() const {
312 : 165 : return has_value_;
313 : : }
314 : 168 : T& value() {
315 : 168 : return std::get<0>(value_);
316 : : }
317 : 8 : const class error& error() const {
318 : 8 : return std::get<1>(value_);
319 : : }
320 : :
321 : : private:
322 : 167 : result(T&& val) : has_value_(true), value_(std::move(val)) {}
323 : 18 : result(class error err) : has_value_(false), value_(err) {}
324 : :
325 : : bool has_value_;
326 : : std::variant<T, class error> value_;
327 : : };
328 : :
329 : : template <> class result<void> {
330 : : public:
331 : 1069 : static result<void> make() {
332 : 1069 : return result<void>();
333 : : }
334 : 3 : static result<void> make(class error err) {
335 : 3 : return result<void>(std::move(err));
336 : : }
337 : :
338 : 957 : bool has_value() const {
339 : 957 : return has_value_;
340 : : }
341 : : void value() const {} // No-op for void
342 : 2 : const class error& error() const {
343 : 2 : return error_;
344 : : }
345 : :
346 : : private:
347 : 1069 : result() : has_value_(true) {}
348 : 3 : result(class error err) : has_value_(false), error_(std::move(err)) {}
349 : :
350 : : bool has_value_;
351 : : class error error_;
352 : : };
353 : :
354 : : // -----------------------------------------------------------------------------
355 : : // Clock - for time-based operations
356 : : // -----------------------------------------------------------------------------
357 : : class Clock {
358 : : public:
359 : : using time_point = std::chrono::steady_clock::time_point;
360 : : using duration = std::chrono::milliseconds;
361 : 33 : time_point now() const {
362 : 33 : return std::chrono::steady_clock::now();
363 : : }
364 : : };
365 : :
366 : : // -----------------------------------------------------------------------------
367 : : // AlarmHandle - opaque handle for alarms
368 : : // -----------------------------------------------------------------------------
369 : : using AlarmHandle = Id<AlarmTag>;
370 : :
371 : : // -----------------------------------------------------------------------------
372 : : // Trace identifiers - W3C/OpenTelemetry-compatible distributed tracing IDs
373 : : // -----------------------------------------------------------------------------
374 : : struct TraceId {
375 : : std::array<uint8_t, 16> bytes{};
376 : :
377 : 24 : bool valid() const noexcept {
378 : 164 : for (uint8_t b : bytes) {
379 : 159 : if (b != 0) {
380 : 19 : return true;
381 : : }
382 : : }
383 : 5 : return false;
384 : : }
385 : :
386 : 5 : bool operator==(const TraceId& other) const noexcept {
387 : 5 : return bytes == other.bytes;
388 : : }
389 : : };
390 : :
391 : : struct SpanId {
392 : : std::array<uint8_t, 8> bytes{};
393 : :
394 : 21 : bool valid() const noexcept {
395 : 69 : for (uint8_t b : bytes) {
396 : 67 : if (b != 0) {
397 : 19 : return true;
398 : : }
399 : : }
400 : 2 : return false;
401 : : }
402 : :
403 : 3 : bool operator==(const SpanId& other) const noexcept {
404 : 3 : return bytes == other.bytes;
405 : : }
406 : : };
407 : :
408 : : struct TraceFlags {
409 : : static constexpr uint8_t kSampled = 0x01;
410 : :
411 : : uint8_t value = 0;
412 : :
413 : 10 : bool sampled() const noexcept {
414 : 10 : return (value & kSampled) != 0;
415 : : }
416 : :
417 : 8 : void set_sampled(bool enabled) noexcept {
418 : 8 : if (enabled) {
419 : 7 : value = static_cast<uint8_t>(value | kSampled);
420 : : } else {
421 : 1 : value = static_cast<uint8_t>(value & ~kSampled);
422 : : }
423 : 8 : }
424 : : };
425 : :
426 : : struct TraceContext {
427 : : TraceId trace_id;
428 : : SpanId span_id;
429 : : TraceFlags flags;
430 : : uint8_t version = 0;
431 : : uint16_t tracestate_len = 0;
432 : : std::array<char, 256> tracestate{};
433 : :
434 : 13 : bool valid() const noexcept {
435 : 13 : return trace_id.valid() && span_id.valid();
436 : : }
437 : :
438 : 10 : bool sampled() const noexcept {
439 : 10 : return flags.sampled();
440 : : }
441 : :
442 : 2 : std::string_view tracestate_view() const noexcept {
443 : 2 : return {tracestate.data(), tracestate_len};
444 : : }
445 : :
446 : 4 : void clear() noexcept {
447 : 4 : trace_id = TraceId{};
448 : 4 : span_id = SpanId{};
449 : 4 : flags = TraceFlags{};
450 : 4 : version = 0;
451 : 4 : tracestate_len = 0;
452 : 4 : tracestate.fill('\0');
453 : 4 : }
454 : : };
455 : :
456 : : // -----------------------------------------------------------------------------
457 : : // StreamBuffer - byte buffer type
458 : : // -----------------------------------------------------------------------------
459 : : using adt::StreamBuffer;
460 : :
461 : : // -----------------------------------------------------------------------------
462 : : // TypeTag - type identifier for serialization (replaces RTTI)
463 : : //
464 : : // Range layout:
465 : : // 0x00000000 – 0x000000FF System messages (256 slots)
466 : : // 0x00000100 – 0x00000FFF Reserved for future system expansion
467 : : // 0x00001000 – 0x00FFFFFF Application-defined messages (~16M slots)
468 : : //
469 : : // System sub-ranges:
470 : : // 0x00 – 0x0F Core system (lifecycle: Down, Exit, Link, Unlink, Monitor,
471 : : // Demonitor) 0x10 – 0x1F Spawn protocol (SpawnRequest, SpawnResponse,
472 : : // Error) 0x20 – 0x2F HTTP protocol (HttpRequest, HttpResponse) 0x30 – 0x3F
473 : : // TOML bootstrap (SystemInit) 0x40 – 0x4F Metrics (MetricsRequest,
474 : : // MetricsResponse) 0x50 – 0x5F CLI interactive (Inspect, Kill, List, Stats)
475 : : // 0x60 – 0x6F Async I/O (IoCompletion)
476 : : // 0x70 – 0xFF Reserved for future system use
477 : : //
478 : : // Application sub-ranges (examples):
479 : : // 0x00001000 – 0x00001FFF Auth subsystem
480 : : // 0x00002000 – 0x00002FFF Chat subsystem
481 : : // 0x00003000 – 0x00003FFF Database subsystem
482 : : // ...
483 : : // -----------------------------------------------------------------------------
484 : : enum class TypeTag : uint32_t {
485 : : // ---- System message range
486 : : // ------------------------------------------------
487 : : Invalid = 0x00000000,
488 : :
489 : : // Core system (0x00 – 0x0F)
490 : : DownMsg = 0x01,
491 : : ExitMsg = 0x02,
492 : : LinkMsg = 0x03,
493 : : UnlinkMsg = 0x04,
494 : : MonitorMsg = 0x0A,
495 : : DemonitorMsg = 0x0B,
496 : :
497 : : // Spawn protocol (0x10 – 0x1F)
498 : : SpawnRequestTag = 0x10,
499 : : SpawnResponseTag = 0x11,
500 : : ErrorMsg = 0x12,
501 : :
502 : : // HTTP protocol (0x20 – 0x2F)
503 : : HttpRequestTag = 0x20,
504 : : HttpResponseTag = 0x21,
505 : :
506 : : // TOML config bootstrapping (0x30 – 0x3F)
507 : : SystemInitTag = 0x30,
508 : :
509 : : // Metrics subsystem (0x40 – 0x4F)
510 : : MetricsRequestTag = 0x40,
511 : : MetricsResponseTag = 0x41,
512 : :
513 : : // CLI interactive subsystem (0x50 – 0x5F)
514 : : InspectStateRequestTag = 0x50,
515 : : InspectStateResponseTag = 0x51,
516 : : KillRequestTag = 0x52,
517 : : KillResponseTag = 0x53,
518 : : ListActorsRequestTag = 0x54,
519 : : ListActorsResponseTag = 0x55,
520 : : SystemStatsRequestTag = 0x56,
521 : : SystemStatsResponseTag = 0x57,
522 : : MemoryStatsRequestTag = 0x58,
523 : : MemoryStatsResponseTag = 0x59,
524 : : TopologyShowRequestTag = 0x5A,
525 : : TopologyShowResponseTag = 0x5B,
526 : : TopologyRestartRequestTag = 0x5C,
527 : : TopologyRestartResponseTag = 0x5D,
528 : :
529 : : // Async I/O (0x60 – 0x6F)
530 : : IoCompletionTag = 0x60,
531 : :
532 : : // Backpressure control (0x70 – 0x7F)
533 : : BackpressureSignalTag = 0x70,
534 : :
535 : : // ---- Application range
536 : : // ---------------------------------------------------
537 : : User = 0x00001000,
538 : : };
539 : :
540 : : } // namespace hpactor
541 : :
542 : : // -----------------------------------------------------------------------------
543 : : // std::hash specializations (must be in namespace std)
544 : : // -----------------------------------------------------------------------------
545 : : template <> struct std::hash<hpactor::Ipv4Endpoint> {
546 : 112 : std::size_t operator()(const hpactor::Ipv4Endpoint& ep) const noexcept {
547 : 112 : std::size_t h = std::hash<uint32_t>{}(ep.addr);
548 : 112 : h ^= std::hash<uint16_t>{}(ep.port()) + 0x9e3779b9 + (h << 6) + (h >> 2);
549 : 112 : return h;
550 : : }
551 : : };
552 : :
553 : : template <> struct std::hash<hpactor::Ipv6Endpoint> {
554 : 0 : std::size_t operator()(const hpactor::Ipv6Endpoint& ep) const noexcept {
555 : 0 : std::size_t h = 0;
556 : 0 : for (uint8_t b : ep.addr) {
557 : 0 : h ^= std::hash<uint8_t>{}(b) + 0x9e3779b9 + (h << 6) + (h >> 2);
558 : : }
559 : 0 : h ^= std::hash<uint16_t>{}(ep.port()) + 0x9e3779b9 + (h << 6) + (h >> 2);
560 : 0 : return h;
561 : : }
562 : : };
563 : :
564 : : template <> struct std::hash<hpactor::EndPoint> {
565 : 110 : std::size_t operator()(const hpactor::EndPoint& ep) const noexcept {
566 : 110 : if (auto* ipv4 = std::get_if<hpactor::Ipv4Endpoint>(&ep)) {
567 : 110 : return std::hash<hpactor::Ipv4Endpoint>{}(*ipv4);
568 : : }
569 : 0 : return std::hash<hpactor::Ipv6Endpoint>{}(
570 : 0 : std::get<hpactor::Ipv6Endpoint>(ep));
571 : : }
572 : : };
|