@zlink-systems/zlink 0.17.3

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 (199) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +198 -0
  3. package/README.typedoc.md +56 -0
  4. package/THIRD_PARTY_NOTICES.md +84 -0
  5. package/binding.gyp +27 -0
  6. package/dist/index.d.mts +3 -0
  7. package/dist/index.d.ts +24 -0
  8. package/dist/index.js +117 -0
  9. package/dist/index.mjs +4 -0
  10. package/dist/zlink/contracts/core/buffer_like.d.ts +2 -0
  11. package/dist/zlink/contracts/core/buffer_like.js +3 -0
  12. package/dist/zlink/contracts/core/context.d.ts +103 -0
  13. package/dist/zlink/contracts/core/context.js +11 -0
  14. package/dist/zlink/contracts/core/index.d.ts +3 -0
  15. package/dist/zlink/contracts/core/index.js +20 -0
  16. package/dist/zlink/contracts/core/routing_id.d.ts +28 -0
  17. package/dist/zlink/contracts/core/routing_id.js +120 -0
  18. package/dist/zlink/contracts/errors/errors.d.ts +44 -0
  19. package/dist/zlink/contracts/errors/errors.js +89 -0
  20. package/dist/zlink/contracts/errors/index.d.ts +2 -0
  21. package/dist/zlink/contracts/errors/index.js +29 -0
  22. package/dist/zlink/contracts/errors/results.d.ts +106 -0
  23. package/dist/zlink/contracts/errors/results.js +102 -0
  24. package/dist/zlink/contracts/eventing/index.d.ts +3 -0
  25. package/dist/zlink/contracts/eventing/index.js +20 -0
  26. package/dist/zlink/contracts/eventing/monitor.d.ts +157 -0
  27. package/dist/zlink/contracts/eventing/monitor.js +82 -0
  28. package/dist/zlink/contracts/eventing/poller.d.ts +76 -0
  29. package/dist/zlink/contracts/eventing/poller.js +3 -0
  30. package/dist/zlink/contracts/eventing/timer.d.ts +38 -0
  31. package/dist/zlink/contracts/eventing/timer.js +3 -0
  32. package/dist/zlink/contracts/index.d.ts +5 -0
  33. package/dist/zlink/contracts/index.js +22 -0
  34. package/dist/zlink/contracts/messaging/handlers.d.ts +5 -0
  35. package/dist/zlink/contracts/messaging/handlers.js +3 -0
  36. package/dist/zlink/contracts/messaging/index.d.ts +8 -0
  37. package/dist/zlink/contracts/messaging/index.js +15 -0
  38. package/dist/zlink/contracts/messaging/message.d.ts +68 -0
  39. package/dist/zlink/contracts/messaging/message.js +280 -0
  40. package/dist/zlink/contracts/messaging/message_parts_envelope.d.ts +14 -0
  41. package/dist/zlink/contracts/messaging/message_parts_envelope.js +56 -0
  42. package/dist/zlink/contracts/messaging/operations.d.ts +54 -0
  43. package/dist/zlink/contracts/messaging/operations.js +3 -0
  44. package/dist/zlink/contracts/messaging/received.d.ts +41 -0
  45. package/dist/zlink/contracts/messaging/received.js +130 -0
  46. package/dist/zlink/contracts/messaging/stream_packet.d.ts +14 -0
  47. package/dist/zlink/contracts/messaging/stream_packet.js +33 -0
  48. package/dist/zlink/contracts/messaging/subscription_event.d.ts +12 -0
  49. package/dist/zlink/contracts/messaging/subscription_event.js +20 -0
  50. package/dist/zlink/contracts/messaging/topic_message.d.ts +14 -0
  51. package/dist/zlink/contracts/messaging/topic_message.js +36 -0
  52. package/dist/zlink/contracts/sockets/dealer_socket.d.ts +29 -0
  53. package/dist/zlink/contracts/sockets/dealer_socket.js +3 -0
  54. package/dist/zlink/contracts/sockets/index.d.ts +10 -0
  55. package/dist/zlink/contracts/sockets/index.js +15 -0
  56. package/dist/zlink/contracts/sockets/pair_socket.d.ts +20 -0
  57. package/dist/zlink/contracts/sockets/pair_socket.js +3 -0
  58. package/dist/zlink/contracts/sockets/pubsub_sockets.d.ts +40 -0
  59. package/dist/zlink/contracts/sockets/pubsub_sockets.js +3 -0
  60. package/dist/zlink/contracts/sockets/router_socket.d.ts +32 -0
  61. package/dist/zlink/contracts/sockets/router_socket.js +3 -0
  62. package/dist/zlink/contracts/sockets/socket.d.ts +58 -0
  63. package/dist/zlink/contracts/sockets/socket.js +3 -0
  64. package/dist/zlink/contracts/sockets/socket_constants.d.ts +88 -0
  65. package/dist/zlink/contracts/sockets/socket_constants.js +67 -0
  66. package/dist/zlink/contracts/sockets/socket_options.d.ts +105 -0
  67. package/dist/zlink/contracts/sockets/socket_options.js +3 -0
  68. package/dist/zlink/contracts/sockets/stream_socket.d.ts +31 -0
  69. package/dist/zlink/contracts/sockets/stream_socket.js +3 -0
  70. package/dist/zlink/runtime/buffers/message_conversion.d.ts +6 -0
  71. package/dist/zlink/runtime/buffers/message_conversion.js +49 -0
  72. package/dist/zlink/runtime/core/context.d.ts +43 -0
  73. package/dist/zlink/runtime/core/context.js +150 -0
  74. package/dist/zlink/runtime/core/context_options.d.ts +19 -0
  75. package/dist/zlink/runtime/core/context_options.js +23 -0
  76. package/dist/zlink/runtime/core/routing_id.d.ts +3 -0
  77. package/dist/zlink/runtime/core/routing_id.js +30 -0
  78. package/dist/zlink/runtime/core/routing_id_conversion.d.ts +2 -0
  79. package/dist/zlink/runtime/core/routing_id_conversion.js +11 -0
  80. package/dist/zlink/runtime/core/runtime_info.d.ts +8 -0
  81. package/dist/zlink/runtime/core/runtime_info.js +39 -0
  82. package/dist/zlink/runtime/errors/error_mapping.d.ts +4 -0
  83. package/dist/zlink/runtime/errors/error_mapping.js +132 -0
  84. package/dist/zlink/runtime/errors/error_state.d.ts +1 -0
  85. package/dist/zlink/runtime/errors/error_state.js +15 -0
  86. package/dist/zlink/runtime/errors/native_errors.d.ts +16 -0
  87. package/dist/zlink/runtime/errors/native_errors.js +86 -0
  88. package/dist/zlink/runtime/eventing/counters.d.ts +16 -0
  89. package/dist/zlink/runtime/eventing/counters.js +33 -0
  90. package/dist/zlink/runtime/eventing/index.d.ts +6 -0
  91. package/dist/zlink/runtime/eventing/index.js +22 -0
  92. package/dist/zlink/runtime/eventing/monitor_event_state.d.ts +3 -0
  93. package/dist/zlink/runtime/eventing/monitor_event_state.js +19 -0
  94. package/dist/zlink/runtime/eventing/monitor_raw.d.ts +50 -0
  95. package/dist/zlink/runtime/eventing/monitor_raw.js +3 -0
  96. package/dist/zlink/runtime/eventing/monitor_socket.d.ts +9 -0
  97. package/dist/zlink/runtime/eventing/monitor_socket.js +39 -0
  98. package/dist/zlink/runtime/eventing/monitor_status.d.ts +3 -0
  99. package/dist/zlink/runtime/eventing/monitor_status.js +52 -0
  100. package/dist/zlink/runtime/eventing/poll_events.d.ts +20 -0
  101. package/dist/zlink/runtime/eventing/poll_events.js +92 -0
  102. package/dist/zlink/runtime/eventing/poller.d.ts +34 -0
  103. package/dist/zlink/runtime/eventing/poller.js +275 -0
  104. package/dist/zlink/runtime/eventing/thread.d.ts +6 -0
  105. package/dist/zlink/runtime/eventing/thread.js +52 -0
  106. package/dist/zlink/runtime/eventing/timer.d.ts +8 -0
  107. package/dist/zlink/runtime/eventing/timer.js +45 -0
  108. package/dist/zlink/runtime/handles/native_handle.d.ts +7 -0
  109. package/dist/zlink/runtime/handles/native_handle.js +24 -0
  110. package/dist/zlink/runtime/messaging/completion_owner.d.ts +86 -0
  111. package/dist/zlink/runtime/messaging/completion_owner.js +570 -0
  112. package/dist/zlink/runtime/messaging/message_materializer.d.ts +36 -0
  113. package/dist/zlink/runtime/messaging/message_materializer.js +199 -0
  114. package/dist/zlink/runtime/messaging/message_parts_state.d.ts +4 -0
  115. package/dist/zlink/runtime/messaging/message_parts_state.js +20 -0
  116. package/dist/zlink/runtime/messaging/message_snapshot.d.ts +13 -0
  117. package/dist/zlink/runtime/messaging/message_snapshot.js +42 -0
  118. package/dist/zlink/runtime/messaging/received_operations.d.ts +5 -0
  119. package/dist/zlink/runtime/messaging/received_operations.js +46 -0
  120. package/dist/zlink/runtime/messaging/received_state.d.ts +13 -0
  121. package/dist/zlink/runtime/messaging/received_state.js +29 -0
  122. package/dist/zlink/runtime/messaging/request_executor.d.ts +2 -0
  123. package/dist/zlink/runtime/messaging/request_executor.js +8 -0
  124. package/dist/zlink/runtime/messaging/send_operation_base.d.ts +13 -0
  125. package/dist/zlink/runtime/messaging/send_operation_base.js +52 -0
  126. package/dist/zlink/runtime/messaging/subscription_event_state.d.ts +4 -0
  127. package/dist/zlink/runtime/messaging/subscription_event_state.js +17 -0
  128. package/dist/zlink/runtime/messaging/topic_message_state.d.ts +11 -0
  129. package/dist/zlink/runtime/messaging/topic_message_state.js +75 -0
  130. package/dist/zlink/runtime/native/binding.d.ts +6 -0
  131. package/dist/zlink/runtime/native/binding.js +3 -0
  132. package/dist/zlink/runtime/native/binding_context.d.ts +14 -0
  133. package/dist/zlink/runtime/native/binding_context.js +3 -0
  134. package/dist/zlink/runtime/native/binding_core.d.ts +21 -0
  135. package/dist/zlink/runtime/native/binding_core.js +3 -0
  136. package/dist/zlink/runtime/native/binding_eventing.d.ts +40 -0
  137. package/dist/zlink/runtime/native/binding_eventing.js +3 -0
  138. package/dist/zlink/runtime/native/binding_socket.d.ts +52 -0
  139. package/dist/zlink/runtime/native/binding_socket.js +3 -0
  140. package/dist/zlink/runtime/native/binding_types.d.ts +8 -0
  141. package/dist/zlink/runtime/native/binding_types.js +3 -0
  142. package/dist/zlink/runtime/native/native.d.ts +2 -0
  143. package/dist/zlink/runtime/native/native.js +40 -0
  144. package/dist/zlink/runtime/native/native_load_paths.d.ts +16 -0
  145. package/dist/zlink/runtime/native/native_load_paths.js +165 -0
  146. package/dist/zlink/runtime/options/byte_values.d.ts +3 -0
  147. package/dist/zlink/runtime/options/byte_values.js +27 -0
  148. package/dist/zlink/runtime/options/option_mapping.d.ts +72 -0
  149. package/dist/zlink/runtime/options/option_mapping.js +41 -0
  150. package/dist/zlink/runtime/options/validation.d.ts +1 -0
  151. package/dist/zlink/runtime/options/validation.js +17 -0
  152. package/dist/zlink/runtime/public_bridge.d.ts +6 -0
  153. package/dist/zlink/runtime/public_bridge.js +23 -0
  154. package/dist/zlink/runtime/sockets/dealer_socket.d.ts +16 -0
  155. package/dist/zlink/runtime/sockets/dealer_socket.js +56 -0
  156. package/dist/zlink/runtime/sockets/index.d.ts +19 -0
  157. package/dist/zlink/runtime/sockets/index.js +27 -0
  158. package/dist/zlink/runtime/sockets/pair_socket.d.ts +7 -0
  159. package/dist/zlink/runtime/sockets/pair_socket.js +12 -0
  160. package/dist/zlink/runtime/sockets/pub_socket.d.ts +7 -0
  161. package/dist/zlink/runtime/sockets/pub_socket.js +12 -0
  162. package/dist/zlink/runtime/sockets/router_socket.d.ts +19 -0
  163. package/dist/zlink/runtime/sockets/router_socket.js +70 -0
  164. package/dist/zlink/runtime/sockets/socket_base.d.ts +21 -0
  165. package/dist/zlink/runtime/sockets/socket_base.js +102 -0
  166. package/dist/zlink/runtime/sockets/socket_operation_builders.d.ts +38 -0
  167. package/dist/zlink/runtime/sockets/socket_operation_builders.js +93 -0
  168. package/dist/zlink/runtime/sockets/socket_operations.d.ts +40 -0
  169. package/dist/zlink/runtime/sockets/socket_operations.js +177 -0
  170. package/dist/zlink/runtime/sockets/socket_options.d.ts +104 -0
  171. package/dist/zlink/runtime/sockets/socket_options.js +259 -0
  172. package/dist/zlink/runtime/sockets/socket_submit_errors.d.ts +3 -0
  173. package/dist/zlink/runtime/sockets/socket_submit_errors.js +13 -0
  174. package/dist/zlink/runtime/sockets/stream_socket.d.ts +15 -0
  175. package/dist/zlink/runtime/sockets/stream_socket.js +93 -0
  176. package/dist/zlink/runtime/sockets/sub_socket.d.ts +7 -0
  177. package/dist/zlink/runtime/sockets/sub_socket.js +12 -0
  178. package/dist/zlink/runtime/sockets/xpub_socket.d.ts +10 -0
  179. package/dist/zlink/runtime/sockets/xpub_socket.js +39 -0
  180. package/dist/zlink/runtime/sockets/xsub_socket.d.ts +7 -0
  181. package/dist/zlink/runtime/sockets/xsub_socket.js +12 -0
  182. package/native/src/addon.cc +11 -0
  183. package/native/src/addon_common_api.h +57 -0
  184. package/native/src/addon_core.cc +3974 -0
  185. package/native/src/addon_core_api.h +110 -0
  186. package/native/src/addon_core_options.cc +119 -0
  187. package/native/src/addon_core_options.h +9 -0
  188. package/native/src/addon_exports.cc +151 -0
  189. package/native/src/addon_exports.h +7 -0
  190. package/native/src/addon_message_parts.h +67 -0
  191. package/native/src/addon_message_values.h +345 -0
  192. package/native/src/addon_monitor_status_values.h +71 -0
  193. package/native/src/addon_submit_results.h +30 -0
  194. package/package.json +74 -0
  195. package/prebuilds/linux-x64/libzlink.so.0.17.3 +0 -0
  196. package/prebuilds/linux-x64/zlink.node +0 -0
  197. package/provenance/core-package-provenance.json +73 -0
  198. package/scripts/install.js +37 -0
  199. package/scripts/resolve_core.js +86 -0
@@ -0,0 +1,3974 @@
1
+ /* SPDX-License-Identifier: MPL-2.0 */
2
+
3
+ #include "addon_core_api.h"
4
+
5
+ #include <uv.h>
6
+ #include "addon_core_options.h"
7
+ #include "addon_monitor_status_values.h"
8
+ #include "addon_message_values.h"
9
+ #include "addon_message_parts.h"
10
+ #include "addon_submit_results.h"
11
+ #include <errno.h>
12
+ #include <atomic>
13
+ #include <chrono>
14
+ #include <condition_variable>
15
+ #include <cstring>
16
+ #include <limits>
17
+ #include <memory>
18
+ #include <mutex>
19
+ #include <new>
20
+ #include <string>
21
+ #include <thread>
22
+ #include <unordered_map>
23
+ #include <vector>
24
+
25
+ namespace
26
+ {
27
+
28
+ static std::atomic<uint64_t> g_completion_close_count (0);
29
+ static const size_t k_inline_message_part_count = 8;
30
+
31
+ // Most public records contain one or two parts. Keep those native staging
32
+ // messages in the N-API call frame and allocate only for an uncommon larger
33
+ // multipart record. This storage never closes messages implicitly: callers
34
+ // must either close() the still-owned staging records or release() them after
35
+ // Core has consumed every slot.
36
+ class small_msg_storage_t
37
+ {
38
+ public:
39
+ small_msg_storage_t () : heap_size_ (0), heap_capacity_ (0), stack_size_ (0) {}
40
+
41
+ bool prepare (napi_env env, size_t count)
42
+ {
43
+ release ();
44
+ if (count <= k_inline_message_part_count) {
45
+ stack_size_ = count;
46
+ return true;
47
+ }
48
+ if (count > std::numeric_limits<size_t>::max () / sizeof (zlink_msg_t)) {
49
+ napi_throw_error (env, NULL, "message parts allocation failed");
50
+ return false;
51
+ }
52
+ heap_.reset (new (std::nothrow) zlink_msg_t[count]);
53
+ if (!heap_) {
54
+ napi_throw_error (env, NULL, "message parts allocation failed");
55
+ return false;
56
+ }
57
+ heap_size_ = count;
58
+ heap_capacity_ = count;
59
+ return true;
60
+ }
61
+
62
+ bool append_move (zlink_msg_t *part)
63
+ {
64
+ if (!part)
65
+ return false;
66
+ if (!heap_ && stack_size_ < k_inline_message_part_count) {
67
+ zlink_msg_t *slot = &stack_[stack_size_];
68
+ if (zlink_msg_init (slot) != 0)
69
+ return false;
70
+ if (zlink_msg_move (slot, part) != 0) {
71
+ zlink_msg_close (slot);
72
+ return false;
73
+ }
74
+ ++stack_size_;
75
+ return true;
76
+ }
77
+
78
+ if (!heap_) {
79
+ const size_t capacity = k_inline_message_part_count * 2;
80
+ std::unique_ptr<zlink_msg_t[]> candidate (
81
+ new (std::nothrow) zlink_msg_t[capacity]);
82
+ if (!candidate)
83
+ return false;
84
+ for (size_t index = 0; index < stack_size_; ++index) {
85
+ if (zlink_msg_init (&candidate[index]) != 0) {
86
+ for (size_t built = 0; built < index; ++built)
87
+ zlink_msg_close (&candidate[built]);
88
+ for (size_t remaining = index; remaining < stack_size_; ++remaining)
89
+ zlink_msg_close (&stack_[remaining]);
90
+ stack_size_ = 0;
91
+ return false;
92
+ }
93
+ if (zlink_msg_move (&candidate[index], &stack_[index]) != 0) {
94
+ for (size_t built = 0; built <= index; ++built)
95
+ zlink_msg_close (&candidate[built]);
96
+ for (size_t remaining = index; remaining < stack_size_; ++remaining)
97
+ zlink_msg_close (&stack_[remaining]);
98
+ stack_size_ = 0;
99
+ return false;
100
+ }
101
+ }
102
+ heap_size_ = stack_size_;
103
+ heap_capacity_ = capacity;
104
+ stack_size_ = 0;
105
+ heap_ = std::move (candidate);
106
+ }
107
+
108
+ if (heap_size_ == heap_capacity_) {
109
+ if (heap_capacity_ > std::numeric_limits<size_t>::max () / 2
110
+ || heap_capacity_ * 2
111
+ > std::numeric_limits<size_t>::max () / sizeof (zlink_msg_t))
112
+ return false;
113
+ const size_t capacity = heap_capacity_ * 2;
114
+ std::unique_ptr<zlink_msg_t[]> candidate (
115
+ new (std::nothrow) zlink_msg_t[capacity]);
116
+ if (!candidate)
117
+ return false;
118
+ for (size_t index = 0; index < heap_size_; ++index) {
119
+ if (zlink_msg_init (&candidate[index]) != 0) {
120
+ for (size_t built = 0; built < index; ++built)
121
+ zlink_msg_close (&candidate[built]);
122
+ for (size_t remaining = index; remaining < heap_size_; ++remaining)
123
+ zlink_msg_close (&heap_[remaining]);
124
+ heap_.reset ();
125
+ heap_size_ = 0;
126
+ heap_capacity_ = 0;
127
+ return false;
128
+ }
129
+ if (zlink_msg_move (&candidate[index], &heap_[index]) != 0) {
130
+ for (size_t built = 0; built <= index; ++built)
131
+ zlink_msg_close (&candidate[built]);
132
+ for (size_t remaining = index; remaining < heap_size_; ++remaining)
133
+ zlink_msg_close (&heap_[remaining]);
134
+ heap_.reset ();
135
+ heap_size_ = 0;
136
+ heap_capacity_ = 0;
137
+ return false;
138
+ }
139
+ }
140
+ heap_ = std::move (candidate);
141
+ heap_capacity_ = capacity;
142
+ }
143
+
144
+ if (zlink_msg_init (&heap_[heap_size_]) != 0)
145
+ return false;
146
+ if (zlink_msg_move (&heap_[heap_size_], part) != 0) {
147
+ zlink_msg_close (&heap_[heap_size_]);
148
+ return false;
149
+ }
150
+ ++heap_size_;
151
+ return true;
152
+ }
153
+
154
+ zlink_msg_t *data () { return heap_ ? heap_.get () : stack_; }
155
+ const zlink_msg_t *data () const { return heap_ ? heap_.get () : stack_; }
156
+ size_t size () const { return heap_ ? heap_size_ : stack_size_; }
157
+ zlink_msg_t &operator[] (size_t index) { return data ()[index]; }
158
+
159
+ void close ()
160
+ {
161
+ if (size () > 0)
162
+ zlink_multipart_close (data (), size ());
163
+ release ();
164
+ }
165
+
166
+ void release ()
167
+ {
168
+ heap_.reset ();
169
+ heap_size_ = 0;
170
+ heap_capacity_ = 0;
171
+ stack_size_ = 0;
172
+ }
173
+
174
+ private:
175
+ zlink_msg_t stack_[k_inline_message_part_count];
176
+ std::unique_ptr<zlink_msg_t[]> heap_;
177
+ size_t heap_size_;
178
+ size_t heap_capacity_;
179
+ size_t stack_size_;
180
+ };
181
+
182
+ inline int collect_recv_parts (void *socket,
183
+ zlink_msg_t *first_part,
184
+ zlink_part_flag_t has_more,
185
+ small_msg_storage_t *parts)
186
+ {
187
+ if (!parts) {
188
+ if (first_part)
189
+ zlink_msg_close (first_part);
190
+ errno = EFAULT;
191
+ return ZLINK_RECV_INTERNAL_ERROR;
192
+ }
193
+ if (!parts->append_move (first_part)) {
194
+ if (first_part)
195
+ zlink_msg_close (first_part);
196
+ errno = ENOMEM;
197
+ return ZLINK_RECV_INTERNAL_ERROR;
198
+ }
199
+ while (has_more) {
200
+ const zlink_routing_id_t *source_rid = NULL;
201
+ zlink_msg_t next_part;
202
+ if (zlink_msg_init (&next_part) != 0) {
203
+ parts->close ();
204
+ return ZLINK_RECV_INTERNAL_ERROR;
205
+ }
206
+ zlink_part_flag_t more = ZLINK_PART_FINAL;
207
+ const int rc = zlink_recv_part (
208
+ socket, &source_rid, &next_part, &more, ZLINK_RECV_FLAGS_DONTWAIT);
209
+ if (rc != ZLINK_RECV_OK) {
210
+ zlink_msg_close (&next_part);
211
+ parts->close ();
212
+ return rc;
213
+ }
214
+ if (!parts->append_move (&next_part)) {
215
+ zlink_msg_close (&next_part);
216
+ parts->close ();
217
+ errno = ENOMEM;
218
+ return ZLINK_RECV_INTERNAL_ERROR;
219
+ }
220
+ has_more = more;
221
+ }
222
+ return ZLINK_RECV_OK;
223
+ }
224
+
225
+ struct held_routed_multipart_test_t
226
+ {
227
+ held_routed_multipart_test_t ()
228
+ : socket (NULL), opened (false), finish (false), open_result (-1),
229
+ open_errno (0), final_result (-1), final_errno (0)
230
+ {
231
+ memset (&routing_id, 0, sizeof (routing_id));
232
+ }
233
+
234
+ void *socket;
235
+ zlink_routing_id_t routing_id;
236
+ std::mutex mutex;
237
+ std::condition_variable condition;
238
+ bool opened;
239
+ bool finish;
240
+ int open_result;
241
+ int open_errno;
242
+ int final_result;
243
+ int final_errno;
244
+ std::thread worker;
245
+ };
246
+
247
+ struct send_close_stress_counts_t
248
+ {
249
+ send_close_stress_counts_t ()
250
+ : attempts (0), single_attempts (0), multipart_attempts (0),
251
+ submitted (0), rejected_einval (0), shutdown (0),
252
+ backpressured (0), other_submit (0), received_records (0),
253
+ bad_records (0), bad_first_parts (0), bad_mixed_parts (0),
254
+ bad_part_counts (0), bad_next_part_results (0),
255
+ close_ok (0), close_busy (0),
256
+ close_shutdown (0), close_other (0)
257
+ {
258
+ }
259
+
260
+ std::atomic<uint64_t> attempts;
261
+ std::atomic<uint64_t> single_attempts;
262
+ std::atomic<uint64_t> multipart_attempts;
263
+ std::atomic<uint64_t> submitted;
264
+ std::atomic<uint64_t> rejected_einval;
265
+ std::atomic<uint64_t> shutdown;
266
+ std::atomic<uint64_t> backpressured;
267
+ std::atomic<uint64_t> other_submit;
268
+ std::atomic<uint64_t> received_records;
269
+ std::atomic<uint64_t> bad_records;
270
+ std::atomic<uint64_t> bad_first_parts;
271
+ std::atomic<uint64_t> bad_mixed_parts;
272
+ std::atomic<uint64_t> bad_part_counts;
273
+ std::atomic<uint64_t> bad_next_part_results;
274
+ std::atomic<uint64_t> close_ok;
275
+ std::atomic<uint64_t> close_busy;
276
+ std::atomic<uint64_t> close_shutdown;
277
+ std::atomic<uint64_t> close_other;
278
+ };
279
+
280
+ struct stress_part_payload_t
281
+ {
282
+ uint32_t magic;
283
+ uint32_t sender;
284
+ uint32_t sequence;
285
+ uint32_t part_index;
286
+ uint32_t part_count;
287
+ uint32_t checksum;
288
+ };
289
+
290
+ static const uint32_t k_stress_payload_magic = 0x5a4c4e4b;
291
+
292
+ zlink_socket_type_t translate_socket_type (int32_t type)
293
+ {
294
+ switch (type) {
295
+ case ZLINK_SOCKET_PAIR:
296
+ return ZLINK_SOCKET_PAIR;
297
+ case ZLINK_SOCKET_PUB:
298
+ return ZLINK_SOCKET_PUB;
299
+ case ZLINK_SOCKET_SUB:
300
+ return ZLINK_SOCKET_SUB;
301
+ case ZLINK_SOCKET_DEALER:
302
+ return ZLINK_SOCKET_DEALER;
303
+ case ZLINK_SOCKET_ROUTER:
304
+ return ZLINK_SOCKET_ROUTER;
305
+ case ZLINK_SOCKET_XPUB:
306
+ return ZLINK_SOCKET_XPUB;
307
+ case ZLINK_SOCKET_XSUB:
308
+ return ZLINK_SOCKET_XSUB;
309
+ case ZLINK_SOCKET_STREAM:
310
+ return ZLINK_SOCKET_STREAM;
311
+ default:
312
+ return static_cast<zlink_socket_type_t> (type);
313
+ }
314
+ }
315
+
316
+ bool init_msg_from_bytes (zlink_msg_t *msg, const void *data, size_t len)
317
+ {
318
+ if (zlink_msg_init_size (msg, len) != 0)
319
+ return false;
320
+ if (len > 0 && data)
321
+ memcpy (zlink_msg_data (msg), data, len);
322
+ return true;
323
+ }
324
+
325
+ int subscribe_parts (void *sock,
326
+ zlink_routing_id_t *routing_id,
327
+ char *topic,
328
+ size_t topic_capacity,
329
+ size_t *topic_len,
330
+ small_msg_storage_t *parts,
331
+ int32_t flags)
332
+ {
333
+ const zlink_routing_id_t *source_rid = NULL;
334
+ zlink_msg_t first_part;
335
+ if (zlink_msg_init (&first_part) != 0)
336
+ return ZLINK_RECV_INTERNAL_ERROR;
337
+ zlink_part_flag_t has_more = ZLINK_PART_FINAL;
338
+
339
+ copy_routing_id (routing_id, NULL);
340
+ if (parts)
341
+ parts->release ();
342
+
343
+ int rc = zlink_subscribe_part (sock, &source_rid, topic, topic_capacity, topic_len, &first_part,
344
+ &has_more, static_cast<zlink_recv_flags_t> (flags));
345
+ if (rc != ZLINK_RECV_OK) {
346
+ zlink_msg_close (&first_part);
347
+ return rc;
348
+ }
349
+
350
+ copy_routing_id (routing_id, source_rid);
351
+ if (!parts) {
352
+ zlink_msg_close (&first_part);
353
+ errno = EFAULT;
354
+ return ZLINK_RECV_INTERNAL_ERROR;
355
+ }
356
+ parts->release ();
357
+ if (!parts->append_move (&first_part)) {
358
+ zlink_msg_close (&first_part);
359
+ errno = ENOMEM;
360
+ return ZLINK_RECV_INTERNAL_ERROR;
361
+ }
362
+ while (has_more) {
363
+ const zlink_routing_id_t *next_source_rid = NULL;
364
+ char next_topic[256];
365
+ size_t next_topic_len = 0;
366
+ zlink_msg_t next_part;
367
+ if (zlink_msg_init (&next_part) != 0) {
368
+ parts->close ();
369
+ return ZLINK_RECV_INTERNAL_ERROR;
370
+ }
371
+ zlink_part_flag_t more = ZLINK_PART_FINAL;
372
+ rc = zlink_subscribe_part (sock, &next_source_rid, next_topic, sizeof (next_topic),
373
+ &next_topic_len, &next_part, &more, ZLINK_RECV_FLAGS_DONTWAIT);
374
+ if (rc != ZLINK_RECV_OK) {
375
+ zlink_msg_close (&next_part);
376
+ parts->close ();
377
+ return rc;
378
+ }
379
+ if (!parts->append_move (&next_part)) {
380
+ zlink_msg_close (&next_part);
381
+ parts->close ();
382
+ errno = ENOMEM;
383
+ return ZLINK_RECV_INTERNAL_ERROR;
384
+ }
385
+ has_more = more;
386
+ }
387
+ return ZLINK_RECV_OK;
388
+ }
389
+
390
+ int send_parts (void *sock, zlink_msg_t *parts, size_t part_count, zlink_send_flags_t flags)
391
+ {
392
+ return submit_msg_parts (parts, part_count, [sock, flags] (zlink_msg_t *part,
393
+ zlink_part_flag_t part_flag,
394
+ bool) {
395
+ return zlink_send_part (sock, part, flags, part_flag, NULL, NULL);
396
+ });
397
+ }
398
+
399
+ void run_held_routed_multipart_test (held_routed_multipart_test_t *state)
400
+ {
401
+ static const char first_payload[] = "held-first";
402
+ static const char final_payload[] = "held-final";
403
+ zlink_msg_t first;
404
+ zlink_msg_t final;
405
+ const bool first_initialized = init_msg_from_bytes (
406
+ &first, first_payload, sizeof (first_payload) - 1u);
407
+ const bool final_initialized = init_msg_from_bytes (
408
+ &final, final_payload, sizeof (final_payload) - 1u);
409
+
410
+ if (first_initialized && final_initialized) {
411
+ state->open_result = zlink_send_part_rid (
412
+ state->socket, &state->routing_id, &first,
413
+ ZLINK_SEND_FLAGS_NONE, ZLINK_PART_MORE, NULL, NULL);
414
+ state->open_errno = state->open_result == ZLINK_SUBMIT_OK ? 0 : zlink_errno ();
415
+ } else {
416
+ state->open_result = ZLINK_SUBMIT_OUT_OF_MEMORY;
417
+ state->open_errno = zlink_errno ();
418
+ }
419
+
420
+ {
421
+ std::lock_guard<std::mutex> lock (state->mutex);
422
+ state->opened = true;
423
+ }
424
+ state->condition.notify_one ();
425
+
426
+ {
427
+ std::unique_lock<std::mutex> lock (state->mutex);
428
+ state->condition.wait_for (
429
+ lock, std::chrono::seconds (5), [state] { return state->finish; });
430
+ }
431
+
432
+ if (state->open_result == ZLINK_SUBMIT_OK) {
433
+ state->final_result = zlink_send_part_rid (
434
+ state->socket, &state->routing_id, &final,
435
+ ZLINK_SEND_FLAGS_NONE, ZLINK_PART_FINAL, NULL, NULL);
436
+ state->final_errno = state->final_result == ZLINK_SUBMIT_OK ? 0 : zlink_errno ();
437
+ }
438
+
439
+ if (state->open_result != ZLINK_SUBMIT_OK
440
+ || state->final_result != ZLINK_SUBMIT_OK) {
441
+ if (first_initialized)
442
+ zlink_msg_close (&first);
443
+ if (final_initialized)
444
+ zlink_msg_close (&final);
445
+ }
446
+ }
447
+
448
+ uint32_t stress_payload_checksum (const stress_part_payload_t &payload)
449
+ {
450
+ return payload.magic ^ payload.sender ^ payload.sequence
451
+ ^ payload.part_index ^ payload.part_count;
452
+ }
453
+
454
+ bool init_stress_part (zlink_msg_t *part,
455
+ uint32_t sender,
456
+ uint32_t sequence,
457
+ uint32_t part_index,
458
+ uint32_t part_count)
459
+ {
460
+ stress_part_payload_t payload;
461
+ payload.magic = k_stress_payload_magic;
462
+ payload.sender = sender;
463
+ payload.sequence = sequence;
464
+ payload.part_index = part_index;
465
+ payload.part_count = part_count;
466
+ payload.checksum = stress_payload_checksum (payload);
467
+ return init_msg_from_bytes (part, &payload, sizeof (payload));
468
+ }
469
+
470
+ bool read_stress_part (zlink_msg_t *part, stress_part_payload_t *payload)
471
+ {
472
+ if (!part || !payload || zlink_msg_size (part) != sizeof (*payload))
473
+ return false;
474
+ memcpy (payload, zlink_msg_data (part), sizeof (*payload));
475
+ return payload->magic == k_stress_payload_magic
476
+ && payload->checksum == stress_payload_checksum (*payload);
477
+ }
478
+
479
+ void run_send_close_stress_sender (void *sender,
480
+ uint32_t sender_index,
481
+ uint32_t iterations,
482
+ uint64_t total_attempts,
483
+ std::atomic<uint32_t> *ready,
484
+ std::atomic<bool> *start,
485
+ std::atomic<bool> *close_finished,
486
+ send_close_stress_counts_t *counts)
487
+ {
488
+ ready->fetch_add (1, std::memory_order_release);
489
+ while (!start->load (std::memory_order_acquire))
490
+ std::this_thread::yield ();
491
+
492
+ for (uint32_t sequence = 0; sequence < iterations; ++sequence) {
493
+ const uint32_t part_count = (sequence & 1u) == 0 ? 1u : 3u;
494
+ if (part_count == 1)
495
+ counts->single_attempts.fetch_add (1, std::memory_order_relaxed);
496
+ else
497
+ counts->multipart_attempts.fetch_add (1, std::memory_order_relaxed);
498
+ const uint64_t attempt_number =
499
+ counts->attempts.fetch_add (1, std::memory_order_release) + 1u;
500
+
501
+ // Hold one known submit outside Core until close has sealed the
502
+ // public handle. This makes the post-close ESHUTDOWN edge observable
503
+ // without depending on scheduler timing or send throughput.
504
+ if (attempt_number == total_attempts) {
505
+ while (!close_finished->load (std::memory_order_acquire))
506
+ std::this_thread::yield ();
507
+ }
508
+
509
+ zlink_msg_t parts[3];
510
+ uint32_t initialized = 0;
511
+ for (; initialized < part_count; ++initialized) {
512
+ if (!init_stress_part (&parts[initialized], sender_index, sequence,
513
+ initialized, part_count))
514
+ break;
515
+ }
516
+ if (initialized != part_count) {
517
+ zlink_multipart_close (parts, initialized);
518
+ counts->other_submit.fetch_add (1, std::memory_order_relaxed);
519
+ continue;
520
+ }
521
+
522
+ const int result = send_parts (
523
+ sender, parts, part_count, ZLINK_SEND_FLAGS_DONTWAIT);
524
+ const int native_errno = result == ZLINK_SUBMIT_OK ? 0 : zlink_errno ();
525
+ if (result == ZLINK_SUBMIT_OK) {
526
+ counts->submitted.fetch_add (1, std::memory_order_relaxed);
527
+ } else if ((result == ZLINK_SUBMIT_INVALID_ARGUMENT
528
+ || result == ZLINK_SUBMIT_INVALID_STATE)
529
+ && native_errno == EINVAL) {
530
+ counts->rejected_einval.fetch_add (1, std::memory_order_relaxed);
531
+ } else if (native_errno == ESHUTDOWN) {
532
+ counts->shutdown.fetch_add (1, std::memory_order_relaxed);
533
+ } else if (result == ZLINK_SUBMIT_BACKPRESSURED
534
+ || native_errno == EAGAIN) {
535
+ counts->backpressured.fetch_add (1, std::memory_order_relaxed);
536
+ } else {
537
+ counts->other_submit.fetch_add (1, std::memory_order_relaxed);
538
+ }
539
+ }
540
+ }
541
+
542
+ void run_send_close_stress_receiver (void *receiver,
543
+ std::atomic<bool> *senders_done,
544
+ send_close_stress_counts_t *counts)
545
+ {
546
+ uint32_t empty_after_done = 0;
547
+ while (!senders_done->load (std::memory_order_acquire)
548
+ || empty_after_done < 10000u) {
549
+ zlink_msg_t part;
550
+ if (zlink_msg_init (&part) != ZLINK_CONFIG_OK) {
551
+ counts->bad_records.fetch_add (1, std::memory_order_relaxed);
552
+ return;
553
+ }
554
+ const zlink_routing_id_t *source_routing_id = NULL;
555
+ zlink_part_flag_t has_more = ZLINK_PART_FINAL;
556
+ const int result = zlink_recv_part (
557
+ receiver, &source_routing_id, &part, &has_more,
558
+ ZLINK_RECV_FLAGS_DONTWAIT);
559
+ const int native_errno = result == ZLINK_RECV_OK ? 0 : zlink_errno ();
560
+ if (result == ZLINK_RECV_NO_DATA || native_errno == EAGAIN) {
561
+ zlink_msg_close (&part);
562
+ if (senders_done->load (std::memory_order_acquire))
563
+ ++empty_after_done;
564
+ std::this_thread::yield ();
565
+ continue;
566
+ }
567
+ empty_after_done = 0;
568
+ if (result != ZLINK_RECV_OK) {
569
+ zlink_msg_close (&part);
570
+ counts->bad_records.fetch_add (1, std::memory_order_relaxed);
571
+ continue;
572
+ }
573
+
574
+ stress_part_payload_t first_payload = {};
575
+ bool valid = read_stress_part (&part, &first_payload)
576
+ && first_payload.part_index == 0;
577
+ if (!valid)
578
+ counts->bad_first_parts.fetch_add (1, std::memory_order_relaxed);
579
+ zlink_msg_close (&part);
580
+ uint32_t received_part_count = 1;
581
+ while (has_more) {
582
+ if (zlink_msg_init (&part) != ZLINK_CONFIG_OK) {
583
+ valid = false;
584
+ break;
585
+ }
586
+ const int part_result = zlink_recv_part (
587
+ receiver, &source_routing_id, &part, &has_more,
588
+ ZLINK_RECV_FLAGS_DONTWAIT);
589
+ stress_part_payload_t payload;
590
+ if (part_result != ZLINK_RECV_OK) {
591
+ counts->bad_next_part_results.fetch_add (1, std::memory_order_relaxed);
592
+ valid = false;
593
+ } else if (!read_stress_part (&part, &payload)
594
+ || payload.sender != first_payload.sender
595
+ || payload.sequence != first_payload.sequence
596
+ || payload.part_count != first_payload.part_count
597
+ || payload.part_index != received_part_count) {
598
+ counts->bad_mixed_parts.fetch_add (1, std::memory_order_relaxed);
599
+ valid = false;
600
+ }
601
+ zlink_msg_close (&part);
602
+ ++received_part_count;
603
+ if (part_result != ZLINK_RECV_OK)
604
+ break;
605
+ }
606
+ if (received_part_count != first_payload.part_count) {
607
+ counts->bad_part_counts.fetch_add (1, std::memory_order_relaxed);
608
+ valid = false;
609
+ }
610
+ counts->received_records.fetch_add (1, std::memory_order_relaxed);
611
+ if (!valid)
612
+ counts->bad_records.fetch_add (1, std::memory_order_relaxed);
613
+ }
614
+ }
615
+
616
+ void run_send_close_stress_closer (void *sender,
617
+ uint64_t close_after_attempts,
618
+ std::atomic<bool> *close_finished,
619
+ send_close_stress_counts_t *counts)
620
+ {
621
+ while (counts->attempts.load (std::memory_order_acquire)
622
+ < close_after_attempts)
623
+ std::this_thread::yield ();
624
+
625
+ for (;;) {
626
+ const int result = zlink_close (sender);
627
+ if (result == ZLINK_CLOSE_OK) {
628
+ counts->close_ok.fetch_add (1, std::memory_order_relaxed);
629
+ break;
630
+ }
631
+ if (result == ZLINK_CLOSE_BUSY) {
632
+ counts->close_busy.fetch_add (1, std::memory_order_relaxed);
633
+ std::this_thread::yield ();
634
+ continue;
635
+ }
636
+ if (result == ZLINK_CLOSE_SHUTDOWN) {
637
+ counts->close_shutdown.fetch_add (1, std::memory_order_relaxed);
638
+ break;
639
+ }
640
+ counts->close_other.fetch_add (1, std::memory_order_relaxed);
641
+ break;
642
+ }
643
+ close_finished->store (true, std::memory_order_release);
644
+ }
645
+
646
+ int publish_parts (
647
+ void *sock, const char *topic, zlink_msg_t *parts, size_t part_count, zlink_send_flags_t flags)
648
+ {
649
+ return submit_msg_parts (parts, part_count, [sock, topic, flags] (zlink_msg_t *part,
650
+ zlink_part_flag_t part_flag,
651
+ bool) {
652
+ return zlink_publish_part (sock, topic, part, flags, part_flag);
653
+ });
654
+ }
655
+
656
+ int reply_parts_016 (void *router,
657
+ const zlink_routing_id_t *peer_rid,
658
+ uint64_t reply_token,
659
+ zlink_msg_t *parts,
660
+ size_t part_count)
661
+ {
662
+ return submit_msg_parts (parts, part_count, [router, peer_rid, reply_token] (
663
+ zlink_msg_t *part,
664
+ zlink_part_flag_t part_flag, bool) {
665
+ return zlink_reply_part (
666
+ router, peer_rid, reply_token, part, part_flag);
667
+ });
668
+ }
669
+
670
+ napi_value create_recv_message_value (napi_env env,
671
+ const zlink_routing_id_t &routing_id,
672
+ zlink_msg_t *parts,
673
+ size_t part_count,
674
+ napi_value routing_id_storage = NULL)
675
+ {
676
+ // Hot path: this is an internal raw shape consumed by the TypeScript
677
+ // materializer, not a public Received object. Do not add unused per-
678
+ // message fields here; each property set is paid on every recv().
679
+ if (part_count > 1 && routing_id.size == 0) {
680
+ // Plain multipart receives need only their owned payload Buffers.
681
+ // Returning the array directly avoids an envelope and one snapshot
682
+ // object per part while preserving the public Message[] shape.
683
+ napi_value parts_array;
684
+ napi_create_array_with_length (env, part_count, &parts_array);
685
+ for (size_t i = 0; i < part_count; ++i) {
686
+ napi_value data = create_received_message_buffer (env, &parts[i]);
687
+ if (!data)
688
+ return NULL;
689
+ napi_set_element (env, parts_array, static_cast<uint32_t> (i), data);
690
+ }
691
+ return parts_array;
692
+ }
693
+
694
+ napi_value obj;
695
+ napi_create_object (env, &obj);
696
+
697
+ if (part_count == 1) {
698
+ // A one-part receive needs neither a parts array nor a part snapshot.
699
+ // Allocate the payload as a JS-owned Buffer immediately. This keeps
700
+ // data() and close() on the JS side for every payload size.
701
+ napi_value data = create_received_message_buffer (env, &parts[0]);
702
+ if (!data)
703
+ return NULL;
704
+ napi_set_named_property (env, obj, "data", data);
705
+ if (routing_id.size > 0) {
706
+ napi_value rid = create_routing_id_value_reusing (
707
+ env, routing_id, routing_id_storage);
708
+ napi_set_named_property (env, obj, "routingId", rid);
709
+ }
710
+ return obj;
711
+ }
712
+
713
+ napi_value parts_array;
714
+ napi_create_array_with_length (env, part_count, &parts_array);
715
+ // Routing metadata belongs to the received message, not each part.
716
+ // The JS materializer freezes this shared object before exposing it.
717
+ napi_value properties = create_message_properties_snapshot (
718
+ env, &routing_id, NULL, false);
719
+ for (size_t i = 0; i < part_count; ++i) {
720
+ napi_value part = create_message_snapshot_value (env, NULL, &parts[i]);
721
+ if (!part)
722
+ return NULL;
723
+ if (properties)
724
+ napi_set_named_property (env, part, "properties", properties);
725
+ napi_set_element (env, parts_array, static_cast<uint32_t> (i), part);
726
+ }
727
+
728
+ napi_set_named_property (env, obj, "parts", parts_array);
729
+ if (routing_id.size > 0) {
730
+ napi_value rid = create_routing_id_value_reusing (
731
+ env, routing_id, routing_id_storage);
732
+ napi_set_named_property (env, obj, "routingId", rid);
733
+ }
734
+ return obj;
735
+ }
736
+
737
+ napi_value create_router_recv_message_value (napi_env env,
738
+ const zlink_routing_id_t &routing_id,
739
+ uint64_t reply_token,
740
+ zlink_msg_t *parts,
741
+ size_t part_count,
742
+ bool prefer_managed_single_part,
743
+ napi_value routing_id_storage)
744
+ {
745
+ napi_value obj;
746
+ if (part_count == 1 && !prefer_managed_single_part) {
747
+ napi_create_object (env, &obj);
748
+ napi_value native_message = move_message_to_native_frame_value (env, &parts[0]);
749
+ if (!native_message)
750
+ return NULL;
751
+ napi_set_named_property (env, obj, "nativeMessage", native_message);
752
+ napi_value rid = create_routing_id_value_reusing (
753
+ env, routing_id, routing_id_storage);
754
+ napi_set_named_property (env, obj, "routingId", rid);
755
+ } else {
756
+ obj = create_recv_message_value (
757
+ env, routing_id, parts, part_count, routing_id_storage);
758
+ }
759
+ if (!obj)
760
+ return NULL;
761
+ if (reply_token != 0) {
762
+ napi_value value;
763
+ napi_create_bigint_uint64 (env, reply_token, &value);
764
+ napi_set_named_property (env, obj, "replyToken", value);
765
+ }
766
+ return obj;
767
+ }
768
+
769
+ int router_recv_message_value (napi_env env,
770
+ void *router,
771
+ int32_t flags,
772
+ bool prefer_managed_single_part,
773
+ napi_value routing_id_storage,
774
+ napi_value *out)
775
+ {
776
+ const zlink_routing_id_t *peer_rid_ptr = NULL;
777
+ zlink_routing_id_t peer_rid;
778
+ uint64_t reply_token = 0;
779
+ zlink_msg_t first_part;
780
+ if (zlink_msg_init (&first_part) != 0)
781
+ return ZLINK_RECV_INTERNAL_ERROR;
782
+ zlink_part_flag_t has_more = ZLINK_PART_FINAL;
783
+ const int rc = zlink_router_recv_part (
784
+ router, &peer_rid_ptr, &reply_token, &first_part, &has_more,
785
+ static_cast<zlink_recv_flags_t> (flags));
786
+ if (rc != ZLINK_RECV_OK) {
787
+ zlink_msg_close (&first_part);
788
+ return rc;
789
+ }
790
+ copy_routing_id (&peer_rid, peer_rid_ptr);
791
+ if (!has_more) {
792
+ *out = create_router_recv_message_value (
793
+ env, peer_rid, reply_token, &first_part, 1,
794
+ prefer_managed_single_part, routing_id_storage);
795
+ zlink_msg_close (&first_part);
796
+ return *out ? ZLINK_RECV_OK : ZLINK_RECV_INTERNAL_ERROR;
797
+ }
798
+
799
+ small_msg_storage_t parts;
800
+ if (!parts.append_move (&first_part)) {
801
+ zlink_msg_close (&first_part);
802
+ errno = ENOMEM;
803
+ return ZLINK_RECV_INTERNAL_ERROR;
804
+ }
805
+ while (has_more) {
806
+ const zlink_routing_id_t *next_peer_rid = NULL;
807
+ uint64_t next_reply_token = 0;
808
+ zlink_msg_t next_part;
809
+ if (zlink_msg_init (&next_part) != 0) {
810
+ parts.close ();
811
+ return ZLINK_RECV_INTERNAL_ERROR;
812
+ }
813
+ zlink_part_flag_t more = ZLINK_PART_FINAL;
814
+ const int next_rc = zlink_router_recv_part (
815
+ router, &next_peer_rid, &next_reply_token, &next_part, &more,
816
+ ZLINK_RECV_FLAGS_DONTWAIT);
817
+ if (next_rc != ZLINK_RECV_OK) {
818
+ zlink_msg_close (&next_part);
819
+ parts.close ();
820
+ return next_rc;
821
+ }
822
+ if (!parts.append_move (&next_part)) {
823
+ zlink_msg_close (&next_part);
824
+ parts.close ();
825
+ errno = ENOMEM;
826
+ return ZLINK_RECV_INTERNAL_ERROR;
827
+ }
828
+ has_more = more;
829
+ }
830
+ *out = create_router_recv_message_value (
831
+ env, peer_rid, reply_token, parts.data (), parts.size (),
832
+ prefer_managed_single_part, routing_id_storage);
833
+ parts.close ();
834
+ return *out ? ZLINK_RECV_OK : ZLINK_RECV_INTERNAL_ERROR;
835
+ }
836
+
837
+ napi_value create_subscription_event_value (napi_env env,
838
+ const zlink_routing_id_t &routing_id,
839
+ int subscribed,
840
+ const char *topic,
841
+ size_t topic_len)
842
+ {
843
+ napi_value obj;
844
+ napi_create_object (env, &obj);
845
+
846
+ napi_value rid = create_routing_id_value (env, routing_id);
847
+ napi_value topic_value;
848
+ napi_create_string_utf8 (env, topic ? topic : "", topic ? topic_len : 0, &topic_value);
849
+ napi_value subscribed_value;
850
+ napi_get_boolean (env, subscribed != 0, &subscribed_value);
851
+
852
+ napi_set_named_property (env, obj, "routingId", rid);
853
+ napi_set_named_property (env, obj, "topic", topic_value);
854
+ napi_set_named_property (env, obj, "subscribed", subscribed_value);
855
+ return obj;
856
+ }
857
+
858
+ napi_value create_socket_monitor_event_value (napi_env env, const zlink_monitor_event_t &event)
859
+ {
860
+ napi_value obj;
861
+ napi_create_object (env, &obj);
862
+
863
+ napi_value value;
864
+ napi_create_int64 (env, static_cast<int64_t> (event.event), &value);
865
+ napi_set_named_property (env, obj, "event", value);
866
+
867
+ napi_create_bigint_uint64 (env, event.value, &value);
868
+ napi_set_named_property (env, obj, "value", value);
869
+
870
+ napi_value routing_id = create_routing_id_value (env, event.routing_id);
871
+ napi_set_named_property (env, obj, "routingId", routing_id);
872
+
873
+ napi_value local;
874
+ napi_create_string_utf8 (env, event.local_addr, NAPI_AUTO_LENGTH, &local);
875
+ napi_set_named_property (env, obj, "local", local);
876
+
877
+ napi_value remote;
878
+ napi_create_string_utf8 (env, event.remote_addr, NAPI_AUTO_LENGTH, &remote);
879
+ napi_set_named_property (env, obj, "remote", remote);
880
+
881
+ napi_value bigint;
882
+ napi_create_bigint_uint64 (env, event.connection_id, &bigint);
883
+ napi_set_named_property (env, obj, "connectionId", bigint);
884
+
885
+ napi_create_uint32 (env, event.transport_lane, &value);
886
+ napi_set_named_property (env, obj, "transportLane", value);
887
+
888
+ napi_create_uint32 (env, event.flags, &value);
889
+ napi_set_named_property (env, obj, "flags", value);
890
+
891
+ return obj;
892
+ }
893
+
894
+ napi_value create_subscribed_value (napi_env env,
895
+ const zlink_routing_id_t &routing_id,
896
+ const char *topic,
897
+ size_t topic_len,
898
+ zlink_msg_t *parts,
899
+ size_t part_count)
900
+ {
901
+ napi_value obj;
902
+
903
+ if (part_count == 1 && routing_id.size == 0) {
904
+ napi_value data = create_received_message_buffer (env, &parts[0]);
905
+ if (!data)
906
+ return NULL;
907
+ napi_value topic_value;
908
+ napi_create_string_utf8 (env, topic ? topic : "", topic ? topic_len : 0, &topic_value);
909
+ napi_create_array_with_length (env, 2, &obj);
910
+ napi_set_element (env, obj, 0, data);
911
+ napi_set_element (env, obj, 1, topic_value);
912
+ return obj;
913
+ }
914
+
915
+ napi_create_object (env, &obj);
916
+ if (part_count == 1) {
917
+ napi_value data = create_received_message_buffer (env, &parts[0]);
918
+ if (!data)
919
+ return NULL;
920
+ napi_set_named_property (env, obj, "data", data);
921
+ } else {
922
+ napi_value parts_array;
923
+ napi_create_array_with_length (env, part_count, &parts_array);
924
+ for (size_t i = 0; i < part_count; ++i) {
925
+ napi_value part = create_message_snapshot_value (env, &routing_id, &parts[i]);
926
+ napi_set_element (env, parts_array, static_cast<uint32_t> (i), part);
927
+ }
928
+ napi_set_named_property (env, obj, "parts", parts_array);
929
+ }
930
+
931
+ napi_value topic_value;
932
+ napi_create_string_utf8 (env, topic ? topic : "", topic ? topic_len : 0, &topic_value);
933
+
934
+ if (routing_id.size > 0) {
935
+ // Hot path: plain SUB messages normally have no source routing id.
936
+ // Leaving the internal raw field absent preserves the public null
937
+ // routing id after TypeScript materialization and avoids creating a
938
+ // per-message JS null property on the common receive path.
939
+ napi_value rid = create_routing_id_value (env, routing_id);
940
+ napi_set_named_property (env, obj, "routingId", rid);
941
+ }
942
+ napi_set_named_property (env, obj, "topic", topic_value);
943
+ return obj;
944
+ }
945
+
946
+ } // namespace
947
+
948
+ napi_value throw_last_error (napi_env env, const char *prefix)
949
+ {
950
+ int err = zlink_errno ();
951
+ const char *msg = zlink_strerror (err);
952
+ char buf[256];
953
+ snprintf (buf, sizeof (buf), "%s: %s", prefix, msg ? msg : "error");
954
+ napi_throw_error (env, NULL, buf);
955
+ return NULL;
956
+ }
957
+
958
+ napi_value throw_submit_error (napi_env env, const char *prefix, int result)
959
+ {
960
+ int err = zlink_errno ();
961
+ const char *msg = zlink_strerror (err);
962
+ char buf[256];
963
+ snprintf (buf, sizeof (buf), "%s: %s", prefix, msg ? msg : "error");
964
+
965
+ napi_value message;
966
+ napi_value error;
967
+ napi_create_string_utf8 (env, buf, NAPI_AUTO_LENGTH, &message);
968
+ napi_create_error (env, NULL, message, &error);
969
+ set_int64_property (env, error, "nativeResult", result);
970
+ napi_throw (env, error);
971
+ return NULL;
972
+ }
973
+
974
+ std::string get_string (napi_env env, napi_value val)
975
+ {
976
+ size_t len = 0;
977
+ napi_get_value_string_utf8 (env, val, NULL, 0, &len);
978
+ std::string out (len, '\0');
979
+ napi_get_value_string_utf8 (env, val, out.data (), len + 1, &len);
980
+ return out;
981
+ }
982
+
983
+ const char *get_c_string_arg (
984
+ napi_env env, napi_value val, char *stack_buf, size_t stack_buf_size, std::string *heap_buf)
985
+ {
986
+ size_t len = 0;
987
+ napi_get_value_string_utf8 (env, val, NULL, 0, &len);
988
+ if (len + 1 <= stack_buf_size) {
989
+ napi_get_value_string_utf8 (env, val, stack_buf, stack_buf_size, &len);
990
+ return stack_buf;
991
+ }
992
+ heap_buf->assign (len, '\0');
993
+ napi_get_value_string_utf8 (env, val, heap_buf->data (), len + 1, &len);
994
+ return heap_buf->c_str ();
995
+ }
996
+
997
+ bool get_uint64_like (napi_env env, napi_value value, uint64_t *out);
998
+
999
+ static void close_built_msg_vector (std::vector<zlink_msg_t> *parts, size_t built)
1000
+ {
1001
+ if (!parts)
1002
+ return;
1003
+ for (size_t i = 0; i < built; ++i)
1004
+ zlink_msg_close (&(*parts)[i]);
1005
+ parts->clear ();
1006
+ }
1007
+
1008
+ static void close_built_msg_storage (small_msg_storage_t *parts, size_t built)
1009
+ {
1010
+ if (!parts)
1011
+ return;
1012
+ for (size_t index = 0; index < built; ++index)
1013
+ zlink_msg_close (&(*parts)[index]);
1014
+ parts->release ();
1015
+ }
1016
+
1017
+ bool build_msg_vector (napi_env env, napi_value arr, std::vector<zlink_msg_t> *out)
1018
+ {
1019
+ uint32_t len = 0;
1020
+ if (napi_get_array_length (env, arr, &len) != napi_ok) {
1021
+ napi_throw_type_error (env, NULL, "parts must be an array");
1022
+ return false;
1023
+ }
1024
+ out->clear ();
1025
+ out->resize (len);
1026
+ size_t built = 0;
1027
+ for (uint32_t i = 0; i < len; i++) {
1028
+ napi_value val;
1029
+ if (napi_get_element (env, arr, i, &val) != napi_ok) {
1030
+ for (size_t j = 0; j < built; j++)
1031
+ zlink_msg_close (&(*out)[j]);
1032
+ napi_throw_type_error (env, NULL, "parts element read failed");
1033
+ return false;
1034
+ }
1035
+ if (!init_msg_from_value (env, val, &(*out)[i])) {
1036
+ close_built_msg_vector (out, built);
1037
+ return false;
1038
+ }
1039
+ built++;
1040
+ }
1041
+ return true;
1042
+ }
1043
+
1044
+ bool build_msg_vector_or_single (napi_env env, napi_value value, std::vector<zlink_msg_t> *out)
1045
+ {
1046
+ bool is_array = false;
1047
+ if (napi_is_array (env, value, &is_array) == napi_ok && is_array)
1048
+ return build_msg_vector (env, value, out);
1049
+
1050
+ out->clear ();
1051
+ out->resize (1);
1052
+ if (!init_msg_from_value (env, value, &(*out)[0])) {
1053
+ out->clear ();
1054
+ return false;
1055
+ }
1056
+ return true;
1057
+ }
1058
+
1059
+ bool build_msg_vector (napi_env env, napi_value arr, small_msg_storage_t *out)
1060
+ {
1061
+ uint32_t len = 0;
1062
+ if (napi_get_array_length (env, arr, &len) != napi_ok) {
1063
+ napi_throw_type_error (env, NULL, "parts must be an array");
1064
+ return false;
1065
+ }
1066
+ if (!out->prepare (env, len))
1067
+ return false;
1068
+ size_t built = 0;
1069
+ for (uint32_t index = 0; index < len; ++index) {
1070
+ napi_value value;
1071
+ if (napi_get_element (env, arr, index, &value) != napi_ok) {
1072
+ close_built_msg_storage (out, built);
1073
+ napi_throw_type_error (env, NULL, "parts element read failed");
1074
+ return false;
1075
+ }
1076
+ if (!init_msg_from_value (env, value, &(*out)[index])) {
1077
+ close_built_msg_storage (out, built);
1078
+ return false;
1079
+ }
1080
+ ++built;
1081
+ }
1082
+ return true;
1083
+ }
1084
+
1085
+ bool build_msg_vector_or_single (
1086
+ napi_env env, napi_value value, small_msg_storage_t *out)
1087
+ {
1088
+ bool is_array = false;
1089
+ if (napi_is_array (env, value, &is_array) == napi_ok && is_array)
1090
+ return build_msg_vector (env, value, out);
1091
+
1092
+ if (!out->prepare (env, 1))
1093
+ return false;
1094
+ if (!init_msg_from_value (env, value, &(*out)[0])) {
1095
+ out->release ();
1096
+ return false;
1097
+ }
1098
+ return true;
1099
+ }
1100
+
1101
+ void close_msg_vector (std::vector<zlink_msg_t> &parts)
1102
+ {
1103
+ for (size_t i = 0; i < parts.size (); i++)
1104
+ zlink_msg_close (&parts[i]);
1105
+ }
1106
+
1107
+ bool get_uint64_like (napi_env env, napi_value value, uint64_t *out)
1108
+ {
1109
+ bool lossless = false;
1110
+ if (napi_get_value_bigint_uint64 (env, value, out, &lossless) == napi_ok)
1111
+ return true;
1112
+
1113
+ napi_valuetype type = napi_undefined;
1114
+ if (napi_typeof (env, value, &type) != napi_ok)
1115
+ return false;
1116
+ if (type == napi_number) {
1117
+ double number = 0;
1118
+ if (napi_get_value_double (env, value, &number) != napi_ok || number < 0)
1119
+ return false;
1120
+ *out = static_cast<uint64_t> (number);
1121
+ return true;
1122
+ }
1123
+
1124
+ napi_value coerced;
1125
+ if (napi_coerce_to_string (env, value, &coerced) != napi_ok)
1126
+ return false;
1127
+ std::string text = get_string (env, coerced);
1128
+ char *end = NULL;
1129
+ errno = 0;
1130
+ unsigned long long parsed = strtoull (text.c_str (), &end, 10);
1131
+ if (errno != 0 || end == text.c_str () || (end && *end != '\0'))
1132
+ return false;
1133
+ *out = static_cast<uint64_t> (parsed);
1134
+ return true;
1135
+ }
1136
+
1137
+ bool init_msg_from_value (napi_env env,
1138
+ napi_value value,
1139
+ zlink_msg_t *msg,
1140
+ bool *contains_native_frame)
1141
+ {
1142
+ if (contains_native_frame)
1143
+ *contains_native_frame = false;
1144
+ bool is_buf = false;
1145
+ if (napi_is_buffer (env, value, &is_buf) == napi_ok && is_buf) {
1146
+ void *data = NULL;
1147
+ size_t len = 0;
1148
+ if (napi_get_buffer_info (env, value, &data, &len) != napi_ok) {
1149
+ napi_throw_type_error (env, NULL, "send buffer invalid");
1150
+ return false;
1151
+ }
1152
+ if (!init_msg_from_bytes (msg, data, len))
1153
+ return false;
1154
+ return true;
1155
+ }
1156
+
1157
+ bool has_native_message = false;
1158
+ if (napi_has_named_property (env, value, "nativeMessage", &has_native_message) != napi_ok) {
1159
+ napi_throw_type_error (env, NULL, "message snapshot native frame lookup failed");
1160
+ return false;
1161
+ }
1162
+ if (has_native_message) {
1163
+ napi_value native_message;
1164
+ if (napi_get_named_property (env, value, "nativeMessage", &native_message) != napi_ok) {
1165
+ napi_throw_type_error (env, NULL, "message snapshot native frame read failed");
1166
+ return false;
1167
+ }
1168
+ native_message_frame_t *frame = get_native_message_frame (
1169
+ env, native_message, "message snapshot native frame is invalid");
1170
+ if (!frame) {
1171
+ return false;
1172
+ }
1173
+ if (zlink_msg_init (msg) != 0)
1174
+ return false;
1175
+ if (zlink_msg_copy (msg, &frame->message) != 0) {
1176
+ zlink_msg_close (msg);
1177
+ return false;
1178
+ }
1179
+ if (contains_native_frame)
1180
+ *contains_native_frame = true;
1181
+ return true;
1182
+ }
1183
+
1184
+ napi_value data_value;
1185
+ if (napi_get_named_property (env, value, "data", &data_value) != napi_ok) {
1186
+ napi_throw_type_error (env, NULL, "message must be a Buffer or message snapshot");
1187
+ return false;
1188
+ }
1189
+ void *data = NULL;
1190
+ size_t len = 0;
1191
+ if (napi_get_buffer_info (env, data_value, &data, &len) != napi_ok) {
1192
+ napi_throw_type_error (env, NULL, "message snapshot data must be a Buffer");
1193
+ return false;
1194
+ }
1195
+ if (!init_msg_from_bytes (msg, data, len))
1196
+ return false;
1197
+
1198
+ return true;
1199
+ }
1200
+
1201
+ void consume_native_message_value (napi_env env, napi_value value)
1202
+ {
1203
+ bool is_array = false;
1204
+ if (napi_is_array (env, value, &is_array) == napi_ok && is_array) {
1205
+ uint32_t length = 0;
1206
+ napi_get_array_length (env, value, &length);
1207
+ for (uint32_t index = 0; index < length; ++index) {
1208
+ napi_value part;
1209
+ if (napi_get_element (env, value, index, &part) == napi_ok)
1210
+ consume_native_message_value (env, part);
1211
+ }
1212
+ return;
1213
+ }
1214
+ napi_valuetype value_type = napi_undefined;
1215
+ if (napi_typeof (env, value, &value_type) != napi_ok || value_type != napi_object)
1216
+ return;
1217
+ bool has_native_message = false;
1218
+ if (napi_has_named_property (env, value, "nativeMessage", &has_native_message) != napi_ok
1219
+ || !has_native_message)
1220
+ return;
1221
+ napi_value native_message;
1222
+ native_message_frame_handle_t *handle = NULL;
1223
+ if (napi_get_named_property (env, value, "nativeMessage", &native_message) != napi_ok
1224
+ || napi_get_value_external (
1225
+ env, native_message, reinterpret_cast<void **> (&handle)) != napi_ok
1226
+ || !handle || !handle->frame)
1227
+ return;
1228
+ napi_value data;
1229
+ bool has_data = false;
1230
+ if (napi_has_named_property (env, value, "data", &has_data) == napi_ok && has_data
1231
+ && napi_get_named_property (env, value, "data", &data) == napi_ok) {
1232
+ napi_value array_buffer;
1233
+ if (napi_get_named_property (env, data, "buffer", &array_buffer) == napi_ok) {
1234
+ // A successful send consumes Message ownership. Detaching the
1235
+ // previously returned writable Buffer view enforces that boundary
1236
+ // before the shared native payload becomes visible to Core.
1237
+ napi_detach_arraybuffer (env, array_buffer);
1238
+ }
1239
+ }
1240
+ zlink_msg_close (&handle->frame->message);
1241
+ zlink_msg_init (&handle->frame->message);
1242
+ }
1243
+
1244
+ napi_value message_from_buffer (napi_env env, napi_callback_info info)
1245
+ {
1246
+ napi_value argv[1];
1247
+ size_t argc = 1;
1248
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1249
+ if (argc < 1) {
1250
+ napi_throw_type_error (env, NULL, "messageFromBuffer requires a Buffer");
1251
+ return NULL;
1252
+ }
1253
+ void *data = NULL;
1254
+ size_t size = 0;
1255
+ if (napi_get_buffer_info (env, argv[0], &data, &size) != napi_ok) {
1256
+ napi_throw_type_error (env, NULL, "messageFromBuffer requires a Buffer");
1257
+ return NULL;
1258
+ }
1259
+ native_message_frame_t *frame = new (std::nothrow) native_message_frame_t;
1260
+ if (!frame) {
1261
+ napi_throw_error (env, NULL, "native message frame allocation failed");
1262
+ return NULL;
1263
+ }
1264
+ if (!init_msg_from_bytes (&frame->message, data, size)) {
1265
+ delete frame;
1266
+ return throw_last_error (env, "message native allocation failed");
1267
+ }
1268
+ // Message.from() can be submitted without exposing data(). Defer the
1269
+ // external Buffer view until JavaScript actually asks for it.
1270
+ return create_native_message_value (env, frame, false);
1271
+ }
1272
+
1273
+ napi_value message_allocate (napi_env env, napi_callback_info info)
1274
+ {
1275
+ napi_value argv[1];
1276
+ size_t argc = 1;
1277
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1278
+ int64_t size = 0;
1279
+ if (argc < 1 || napi_get_value_int64 (env, argv[0], &size) != napi_ok || size < 0) {
1280
+ napi_throw_range_error (env, NULL, "messageAllocate requires a non-negative size");
1281
+ return NULL;
1282
+ }
1283
+ native_message_frame_t *frame = new (std::nothrow) native_message_frame_t;
1284
+ if (!frame) {
1285
+ napi_throw_error (env, NULL, "native message frame allocation failed");
1286
+ return NULL;
1287
+ }
1288
+ if (zlink_msg_init_size (&frame->message, static_cast<size_t> (size)) != 0) {
1289
+ delete frame;
1290
+ return throw_last_error (env, "message native allocation failed");
1291
+ }
1292
+ // Allocation owns native storage immediately; its JavaScript Buffer view
1293
+ // is created lazily by Message.data().
1294
+ return create_native_message_value (env, frame, false);
1295
+ }
1296
+
1297
+ napi_value message_frame_data (napi_env env, napi_callback_info info)
1298
+ {
1299
+ napi_value argv[1];
1300
+ size_t argc = 1;
1301
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1302
+ if (argc < 1) {
1303
+ napi_throw_type_error (env, NULL, "messageFrameData requires a native message frame");
1304
+ return NULL;
1305
+ }
1306
+ native_message_frame_t *frame = get_native_message_frame (
1307
+ env, argv[0], "messageFrameData requires a native message frame");
1308
+ if (!frame)
1309
+ return NULL;
1310
+ return create_native_message_data_buffer (env, frame);
1311
+ }
1312
+
1313
+ napi_value message_frame_copy_data (napi_env env, napi_callback_info info)
1314
+ {
1315
+ napi_value argv[1];
1316
+ size_t argc = 1;
1317
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1318
+ if (argc < 1) {
1319
+ napi_throw_type_error (env, NULL, "messageFrameCopyData requires a native message frame");
1320
+ return NULL;
1321
+ }
1322
+ native_message_frame_t *frame = get_native_message_frame (
1323
+ env, argv[0], "messageFrameCopyData requires a native message frame");
1324
+ if (!frame)
1325
+ return NULL;
1326
+ return create_received_message_buffer (env, &frame->message);
1327
+ }
1328
+
1329
+ napi_value message_frame_size (napi_env env, napi_callback_info info)
1330
+ {
1331
+ napi_value argv[1];
1332
+ size_t argc = 1;
1333
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1334
+ if (argc < 1) {
1335
+ napi_throw_type_error (env, NULL, "messageFrameSize requires a native message frame");
1336
+ return NULL;
1337
+ }
1338
+ native_message_frame_t *frame = get_native_message_frame (
1339
+ env, argv[0], "messageFrameSize requires a native message frame");
1340
+ if (!frame)
1341
+ return NULL;
1342
+ napi_value size;
1343
+ napi_create_double (env, static_cast<double> (zlink_msg_size (&frame->message)), &size);
1344
+ return size;
1345
+ }
1346
+
1347
+ napi_value message_frame_close (napi_env env, napi_callback_info info)
1348
+ {
1349
+ napi_value argv[1];
1350
+ size_t argc = 1;
1351
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1352
+ native_message_frame_handle_t *handle = NULL;
1353
+ if (argc < 1
1354
+ || napi_get_value_external (
1355
+ env, argv[0], reinterpret_cast<void **> (&handle)) != napi_ok
1356
+ || !handle) {
1357
+ napi_throw_type_error (env, NULL, "messageFrameClose requires a native message frame");
1358
+ return NULL;
1359
+ }
1360
+ release_native_message_frame (handle->frame);
1361
+ handle->frame = NULL;
1362
+ napi_value out;
1363
+ napi_get_undefined (env, &out);
1364
+ return out;
1365
+ }
1366
+
1367
+ napi_value version (napi_env env, napi_callback_info info)
1368
+ {
1369
+ int major = 0, minor = 0, patch = 0;
1370
+ zlink_version (&major, &minor, &patch);
1371
+ napi_value arr;
1372
+ napi_create_array_with_length (env, 3, &arr);
1373
+ napi_value v0, v1, v2;
1374
+ napi_create_int32 (env, major, &v0);
1375
+ napi_create_int32 (env, minor, &v1);
1376
+ napi_create_int32 (env, patch, &v2);
1377
+ napi_set_element (env, arr, 0, v0);
1378
+ napi_set_element (env, arr, 1, v1);
1379
+ napi_set_element (env, arr, 2, v2);
1380
+ return arr;
1381
+ }
1382
+
1383
+ napi_value errno_value (napi_env env, napi_callback_info info)
1384
+ {
1385
+ napi_value out;
1386
+ napi_create_int32 (env, zlink_errno (), &out);
1387
+ return out;
1388
+ }
1389
+
1390
+ napi_value strerror_value (napi_env env, napi_callback_info info)
1391
+ {
1392
+ napi_value argv[1];
1393
+ size_t argc = 1;
1394
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1395
+ int32_t errnum = 0;
1396
+ if (argc >= 1)
1397
+ napi_get_value_int32 (env, argv[0], &errnum);
1398
+
1399
+ const char *message = zlink_strerror (errnum);
1400
+ napi_value out;
1401
+ napi_create_string_utf8 (env, message ? message : "", NAPI_AUTO_LENGTH, &out);
1402
+ return out;
1403
+ }
1404
+
1405
+ napi_value has (napi_env env, napi_callback_info info)
1406
+ {
1407
+ napi_value argv[1];
1408
+ size_t argc = 1;
1409
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1410
+ if (argc < 1) {
1411
+ napi_throw_type_error (env, NULL, "has requires a capability string");
1412
+ return NULL;
1413
+ }
1414
+
1415
+ std::string capability = get_string (env, argv[0]);
1416
+ napi_value out;
1417
+ napi_get_boolean (env, zlink_has (capability.c_str ()), &out);
1418
+ return out;
1419
+ }
1420
+
1421
+ napi_value proxy (napi_env env, napi_callback_info info)
1422
+ {
1423
+ napi_value argv[3];
1424
+ size_t argc = 3;
1425
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1426
+ if (argc < 2) {
1427
+ napi_throw_type_error (env, NULL, "proxy requires frontend and backend");
1428
+ return NULL;
1429
+ }
1430
+
1431
+ void *frontend = NULL;
1432
+ void *backend = NULL;
1433
+ void *capture = NULL;
1434
+ napi_get_value_external (env, argv[0], &frontend);
1435
+ napi_get_value_external (env, argv[1], &backend);
1436
+ if (argc >= 3) {
1437
+ napi_valuetype capture_type = napi_undefined;
1438
+ napi_typeof (env, argv[2], &capture_type);
1439
+ if (capture_type != napi_undefined && capture_type != napi_null)
1440
+ napi_get_value_external (env, argv[2], &capture);
1441
+ }
1442
+
1443
+ if (zlink_proxy (frontend, backend, capture) != ZLINK_CONFIG_OK)
1444
+ return throw_last_error (env, "proxy failed");
1445
+ napi_value out;
1446
+ napi_get_undefined (env, &out);
1447
+ return out;
1448
+ }
1449
+
1450
+ napi_value sleep (napi_env env, napi_callback_info info)
1451
+ {
1452
+ napi_value argv[1];
1453
+ size_t argc = 1;
1454
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1455
+ int32_t seconds = 0;
1456
+ if (argc >= 1)
1457
+ napi_get_value_int32 (env, argv[0], &seconds);
1458
+
1459
+ zlink_sleep (seconds);
1460
+ napi_value out;
1461
+ napi_get_undefined (env, &out);
1462
+ return out;
1463
+ }
1464
+
1465
+ napi_value ctx_new (napi_env env, napi_callback_info info)
1466
+ {
1467
+ void *ctx = zlink_ctx_new ();
1468
+ if (!ctx)
1469
+ return throw_last_error (env, "ctx_new failed");
1470
+ napi_value ext;
1471
+ napi_create_external (env, ctx, NULL, NULL, &ext);
1472
+ return ext;
1473
+ }
1474
+
1475
+ napi_value ctx_shutdown (napi_env env, napi_callback_info info)
1476
+ {
1477
+ napi_value argv[1];
1478
+ size_t argc = 1;
1479
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1480
+ void *ctx = NULL;
1481
+ napi_get_value_external (env, argv[0], &ctx);
1482
+ int rc = zlink_ctx_shutdown (ctx);
1483
+ if (rc != 0)
1484
+ return throw_last_error (env, "ctx_shutdown failed");
1485
+ napi_value ok;
1486
+ napi_get_undefined (env, &ok);
1487
+ return ok;
1488
+ }
1489
+
1490
+ napi_value ctx_term (napi_env env, napi_callback_info info)
1491
+ {
1492
+ napi_value argv[1];
1493
+ size_t argc = 1;
1494
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1495
+ void *ctx = NULL;
1496
+ napi_get_value_external (env, argv[0], &ctx);
1497
+ int rc;
1498
+ do {
1499
+ rc = zlink_ctx_term (ctx);
1500
+ } while (rc != 0 && zlink_errno () == EINTR);
1501
+ if (rc != 0)
1502
+ return throw_last_error (env, "ctx_term failed");
1503
+ napi_value ok;
1504
+ napi_get_undefined (env, &ok);
1505
+ return ok;
1506
+ }
1507
+
1508
+ napi_value ctx_setopt (napi_env env, napi_callback_info info)
1509
+ {
1510
+ napi_value argv[3];
1511
+ size_t argc = 3;
1512
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1513
+ void *ctx = NULL;
1514
+ napi_get_value_external (env, argv[0], &ctx);
1515
+ int32_t opt = 0;
1516
+ int32_t value = 0;
1517
+ napi_get_value_int32 (env, argv[1], &opt);
1518
+ bool is_buffer = false;
1519
+ napi_is_buffer (env, argv[2], &is_buffer);
1520
+ if (is_buffer) {
1521
+ void *data = NULL;
1522
+ size_t length = 0;
1523
+ napi_get_buffer_info (env, argv[2], &data, &length);
1524
+ zlink_config_result_t rc =
1525
+ zlink_ctx_set_data (ctx, static_cast<zlink_ctx_option_t> (opt), data, length);
1526
+ if (rc != ZLINK_CONFIG_OK)
1527
+ return throw_last_error (env, "ctx_setopt failed");
1528
+ napi_value ok;
1529
+ napi_get_undefined (env, &ok);
1530
+ return ok;
1531
+ }
1532
+ napi_get_value_int32 (env, argv[2], &value);
1533
+ int rc = zlink_ctx_set (ctx, static_cast<zlink_ctx_option_t> (opt), value);
1534
+ if (rc != 0)
1535
+ return throw_last_error (env, "ctx_setopt failed");
1536
+ napi_value ok;
1537
+ napi_get_undefined (env, &ok);
1538
+ return ok;
1539
+ }
1540
+
1541
+ napi_value ctx_getopt (napi_env env, napi_callback_info info)
1542
+ {
1543
+ napi_value argv[2];
1544
+ size_t argc = 2;
1545
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1546
+ void *ctx = NULL;
1547
+ napi_get_value_external (env, argv[0], &ctx);
1548
+ int32_t opt = 0;
1549
+ napi_get_value_int32 (env, argv[1], &opt);
1550
+ zlink_config_result_t err = ZLINK_CONFIG_OK;
1551
+ const int rc = zlink_ctx_get (ctx, static_cast<zlink_ctx_option_t> (opt), &err);
1552
+ if (err != ZLINK_CONFIG_OK)
1553
+ return throw_last_error (env, "ctx_getopt failed");
1554
+ napi_value out;
1555
+ napi_create_int32 (env, rc, &out);
1556
+ return out;
1557
+ }
1558
+
1559
+ napi_value ctx_getopt_data (napi_env env, napi_callback_info info)
1560
+ {
1561
+ napi_value argv[2];
1562
+ size_t argc = 2;
1563
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1564
+ void *ctx = NULL;
1565
+ napi_get_value_external (env, argv[0], &ctx);
1566
+ int32_t opt = 0;
1567
+ napi_get_value_int32 (env, argv[1], &opt);
1568
+ uint64_t value = 0;
1569
+ size_t size = sizeof (value);
1570
+ const zlink_config_result_t rc = zlink_ctx_get_data (
1571
+ ctx, static_cast<zlink_ctx_option_t> (opt), &value, &size);
1572
+ if (rc != ZLINK_CONFIG_OK)
1573
+ return throw_last_error (env, "ctx_getopt_data failed");
1574
+ if (size != sizeof (value)) {
1575
+ napi_throw_error (env, NULL, "ctx_getopt_data returned an unexpected value size");
1576
+ return NULL;
1577
+ }
1578
+ napi_value out;
1579
+ napi_create_buffer_copy (env, sizeof (value), &value, NULL, &out);
1580
+ return out;
1581
+ }
1582
+
1583
+ napi_value ctx_recalculate_auto_hwm (napi_env env, napi_callback_info info)
1584
+ {
1585
+ napi_value argv[1];
1586
+ size_t argc = 1;
1587
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1588
+ void *ctx = NULL;
1589
+ napi_get_value_external (env, argv[0], &ctx);
1590
+ zlink_config_result_t rc = zlink_ctx_auto_hwm_recalculate (ctx);
1591
+ if (rc != ZLINK_CONFIG_OK)
1592
+ return throw_last_error (env, "ctx_auto_hwm_recalculate failed");
1593
+ napi_value ok;
1594
+ napi_get_undefined (env, &ok);
1595
+ return ok;
1596
+ }
1597
+
1598
+ napi_value ctx_get_auto_hwm_budget_snapshot (napi_env env, napi_callback_info info)
1599
+ {
1600
+ napi_value argv[1];
1601
+ size_t argc = 1;
1602
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1603
+ void *ctx = NULL;
1604
+ napi_get_value_external (env, argv[0], &ctx);
1605
+ zlink_auto_hwm_budget_snapshot_t snapshot;
1606
+ memset (&snapshot, 0, sizeof (snapshot));
1607
+ snapshot.abi_version = ZLINK_AUTO_HWM_BUDGET_SNAPSHOT_ABI_V1;
1608
+ snapshot.struct_size = sizeof (snapshot);
1609
+ const zlink_config_result_t rc =
1610
+ zlink_ctx_get_auto_hwm_budget_snapshot (ctx, &snapshot);
1611
+ if (rc != ZLINK_CONFIG_OK)
1612
+ return throw_last_error (env, "ctx_get_auto_hwm_budget_snapshot failed");
1613
+
1614
+ napi_value out;
1615
+ napi_create_object (env, &out);
1616
+ set_uint32_property (env, out, "abiVersion", snapshot.abi_version);
1617
+ set_uint32_property (env, out, "structSize", snapshot.struct_size);
1618
+ set_uint64_bigint_property (env, out, "budgetGeneration", snapshot.budget_generation);
1619
+ set_uint64_bigint_property (env, out, "measurementEpoch", snapshot.measurement_epoch);
1620
+ set_uint64_bigint_property (env, out, "configuredMemoryLimitBytes",
1621
+ snapshot.configured_memory_limit_bytes);
1622
+ set_uint64_bigint_property (env, out, "runtimeMemoryLimitBytes",
1623
+ snapshot.runtime_memory_limit_bytes);
1624
+ set_uint64_bigint_property (env, out, "resolvedMemoryLimitBytes",
1625
+ snapshot.resolved_memory_limit_bytes);
1626
+ set_uint64_bigint_property (env, out, "configuredCoreBudgetBytes",
1627
+ snapshot.configured_core_budget_bytes);
1628
+ set_uint64_bigint_property (env, out, "effectiveCoreBudgetBytes",
1629
+ snapshot.effective_core_budget_bytes);
1630
+ set_uint64_bigint_property (env, out, "totalPlannedHwmBytes",
1631
+ snapshot.total_planned_hwm_bytes);
1632
+ set_uint64_bigint_property (env, out, "totalAppliedHwmBytes",
1633
+ snapshot.total_applied_hwm_bytes);
1634
+ set_uint64_bigint_property (env, out, "manualReservedHwmBytes",
1635
+ snapshot.manual_reserved_hwm_bytes);
1636
+ set_uint64_bigint_property (env, out, "coreQueueAccountedBytes",
1637
+ snapshot.core_queue_accounted_bytes);
1638
+ set_uint64_bigint_property (env, out, "applicationAccountedBytes",
1639
+ snapshot.application_accounted_bytes);
1640
+ set_uint64_bigint_property (env, out, "currentAccountedBytes",
1641
+ snapshot.current_accounted_bytes);
1642
+ set_uint64_bigint_property (env, out, "provisionalAccountedBytes",
1643
+ snapshot.provisional_accounted_bytes);
1644
+ set_uint64_bigint_property (env, out, "peakAccountedBytes",
1645
+ snapshot.peak_accounted_bytes);
1646
+ set_uint64_bigint_property (env, out, "completionCurrentAccountedBytes",
1647
+ snapshot.completion_current_accounted_bytes);
1648
+ set_uint64_bigint_property (env, out, "completionPeakAccountedBytes",
1649
+ snapshot.completion_peak_accounted_bytes);
1650
+ set_uint64_bigint_property (env, out, "completionPendingMessageCount",
1651
+ snapshot.completion_pending_message_count);
1652
+ set_uint64_bigint_property (env, out, "totalMessagingAccountedBytes",
1653
+ snapshot.total_messaging_accounted_bytes);
1654
+ set_uint64_bigint_property (env, out, "monitorQueueAppliedHwmBytes",
1655
+ snapshot.monitor_queue_applied_hwm_bytes);
1656
+ set_uint64_bigint_property (env, out, "monitorQueueAccountedBytes",
1657
+ snapshot.monitor_queue_accounted_bytes);
1658
+ set_uint64_bigint_property (env, out, "totalInstanceAppliedHwmBytes",
1659
+ snapshot.total_instance_applied_hwm_bytes);
1660
+ set_uint64_bigint_property (env, out, "totalInstanceAccountedBytes",
1661
+ snapshot.total_instance_accounted_bytes);
1662
+ set_uint64_bigint_property (env, out, "oversizeAdmissionCount",
1663
+ snapshot.oversize_admission_count);
1664
+ set_uint64_bigint_property (env, out, "largestOversizeMessageBytes",
1665
+ snapshot.largest_oversize_message_bytes);
1666
+ set_uint64_bigint_property (env, out, "activeDirectionalQueueCount",
1667
+ snapshot.active_directional_queue_count);
1668
+ set_uint64_bigint_property (env, out, "activeCompletionDirectionalQueueCount",
1669
+ snapshot.active_completion_directional_queue_count);
1670
+ set_uint64_bigint_property (env, out, "activeSendQueueCount",
1671
+ snapshot.active_send_queue_count);
1672
+ set_uint64_bigint_property (env, out, "activeReceiveQueueCount",
1673
+ snapshot.active_receive_queue_count);
1674
+ set_uint64_bigint_property (env, out, "outstandingApplicationLeaseCount",
1675
+ snapshot.outstanding_application_lease_count);
1676
+ set_uint64_bigint_property (env, out, "retiredQueueCount",
1677
+ snapshot.retired_queue_count);
1678
+ set_uint64_bigint_property (env, out, "deferredOriginCreditBytes",
1679
+ snapshot.deferred_origin_credit_bytes);
1680
+ set_uint64_bigint_property (env, out, "unlimitedManualQueueCount",
1681
+ snapshot.unlimited_manual_queue_count);
1682
+ set_uint32_property (env, out, "blockedRatioPpm", snapshot.blocked_ratio_ppm);
1683
+ set_uint32_property (env, out, "flags", snapshot.flags);
1684
+ napi_value reserved;
1685
+ napi_create_array_with_length (env, 8, &reserved);
1686
+ for (uint32_t index = 0; index < 8; ++index) {
1687
+ napi_value value;
1688
+ napi_create_bigint_uint64 (env, snapshot.reserved_u64[index], &value);
1689
+ napi_set_element (env, reserved, index, value);
1690
+ }
1691
+ napi_set_named_property (env, out, "reservedUInt64", reserved);
1692
+ return out;
1693
+ }
1694
+
1695
+ napi_value ctx_reset_auto_hwm_budget_metrics (napi_env env, napi_callback_info info)
1696
+ {
1697
+ napi_value argv[1];
1698
+ size_t argc = 1;
1699
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1700
+ void *ctx = NULL;
1701
+ napi_get_value_external (env, argv[0], &ctx);
1702
+ const zlink_config_result_t rc = zlink_ctx_reset_auto_hwm_budget_metrics (ctx);
1703
+ if (rc != ZLINK_CONFIG_OK)
1704
+ return throw_last_error (env, "ctx_reset_auto_hwm_budget_metrics failed");
1705
+ napi_value ok;
1706
+ napi_get_undefined (env, &ok);
1707
+ return ok;
1708
+ }
1709
+
1710
+ napi_value socket_new (napi_env env, napi_callback_info info)
1711
+ {
1712
+ napi_value argv[2];
1713
+ size_t argc = 2;
1714
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1715
+ void *ctx = NULL;
1716
+ int32_t type = 0;
1717
+ napi_get_value_external (env, argv[0], &ctx);
1718
+ napi_get_value_int32 (env, argv[1], &type);
1719
+ void *sock = zlink_socket (ctx, translate_socket_type (type));
1720
+ if (!sock)
1721
+ return throw_last_error (env, "socket failed");
1722
+ napi_value ext;
1723
+ napi_create_external (env, sock, NULL, NULL, &ext);
1724
+ return ext;
1725
+ }
1726
+
1727
+ napi_value socket_close (napi_env env, napi_callback_info info)
1728
+ {
1729
+ napi_value argv[1];
1730
+ size_t argc = 1;
1731
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1732
+ void *sock = NULL;
1733
+ napi_get_value_external (env, argv[0], &sock);
1734
+ int rc = zlink_close (sock);
1735
+ if (rc != 0)
1736
+ return throw_last_error (env, "close failed");
1737
+ napi_value ok;
1738
+ napi_get_undefined (env, &ok);
1739
+ return ok;
1740
+ }
1741
+
1742
+ napi_value test_begin_held_routed_multipart (napi_env env, napi_callback_info info)
1743
+ {
1744
+ napi_value argv[2];
1745
+ size_t argc = 2;
1746
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1747
+ if (argc < 2) {
1748
+ napi_throw_type_error (env, NULL, "test hook requires socket and routing id");
1749
+ return NULL;
1750
+ }
1751
+
1752
+ held_routed_multipart_test_t *state =
1753
+ new (std::nothrow) held_routed_multipart_test_t ();
1754
+ if (!state) {
1755
+ napi_throw_error (env, NULL, "test hook allocation failed");
1756
+ return NULL;
1757
+ }
1758
+ napi_get_value_external (env, argv[0], &state->socket);
1759
+ if (!state->socket || !parse_routing_id_value (env, argv[1], &state->routing_id)) {
1760
+ delete state;
1761
+ return NULL;
1762
+ }
1763
+
1764
+ state->worker = std::thread (run_held_routed_multipart_test, state);
1765
+ {
1766
+ std::unique_lock<std::mutex> lock (state->mutex);
1767
+ state->condition.wait (lock, [state] { return state->opened; });
1768
+ }
1769
+
1770
+ napi_value out;
1771
+ napi_value external;
1772
+ napi_create_object (env, &out);
1773
+ napi_create_external (env, state, NULL, NULL, &external);
1774
+ napi_set_named_property (env, out, "state", external);
1775
+ set_int64_property (env, out, "openResult", state->open_result);
1776
+ set_int64_property (env, out, "openErrno", state->open_errno);
1777
+ return out;
1778
+ }
1779
+
1780
+ napi_value test_end_held_routed_multipart (napi_env env, napi_callback_info info)
1781
+ {
1782
+ napi_value argv[1];
1783
+ size_t argc = 1;
1784
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1785
+ held_routed_multipart_test_t *state = NULL;
1786
+ if (argc < 1
1787
+ || napi_get_value_external (
1788
+ env, argv[0], reinterpret_cast<void **> (&state)) != napi_ok
1789
+ || !state) {
1790
+ napi_throw_type_error (env, NULL, "test hook state is invalid");
1791
+ return NULL;
1792
+ }
1793
+
1794
+ {
1795
+ std::lock_guard<std::mutex> lock (state->mutex);
1796
+ state->finish = true;
1797
+ }
1798
+ state->condition.notify_one ();
1799
+ if (state->worker.joinable ())
1800
+ state->worker.join ();
1801
+
1802
+ napi_value out;
1803
+ napi_create_object (env, &out);
1804
+ set_int64_property (env, out, "finalResult", state->final_result);
1805
+ set_int64_property (env, out, "finalErrno", state->final_errno);
1806
+ delete state;
1807
+ return out;
1808
+ }
1809
+
1810
+ napi_value test_run_send_close_stress (napi_env env, napi_callback_info info)
1811
+ {
1812
+ napi_value argv[2];
1813
+ size_t argc = 2;
1814
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1815
+ uint32_t thread_count = 4;
1816
+ uint32_t iterations = 10000;
1817
+ if (argc >= 1)
1818
+ napi_get_value_uint32 (env, argv[0], &thread_count);
1819
+ if (argc >= 2)
1820
+ napi_get_value_uint32 (env, argv[1], &iterations);
1821
+ if (thread_count == 0 || thread_count > 32 || iterations == 0) {
1822
+ napi_throw_range_error (env, NULL, "stress dimensions are invalid");
1823
+ return NULL;
1824
+ }
1825
+
1826
+ void *context = zlink_ctx_new ();
1827
+ void *sender = context ? zlink_socket (context, ZLINK_SOCKET_PAIR) : NULL;
1828
+ void *receiver = context ? zlink_socket (context, ZLINK_SOCKET_PAIR) : NULL;
1829
+ if (!context || !sender || !receiver) {
1830
+ if (sender)
1831
+ zlink_close (sender);
1832
+ if (receiver)
1833
+ zlink_close (receiver);
1834
+ if (context)
1835
+ zlink_ctx_term (context);
1836
+ return throw_last_error (env, "stress setup failed");
1837
+ }
1838
+
1839
+ static std::atomic<uint64_t> next_endpoint (1);
1840
+ char endpoint[96];
1841
+ snprintf (endpoint, sizeof (endpoint), "inproc://node-send-close-stress-%llu",
1842
+ static_cast<unsigned long long> (
1843
+ next_endpoint.fetch_add (1, std::memory_order_relaxed)));
1844
+ if (zlink_bind (receiver, endpoint) != ZLINK_BIND_OK
1845
+ || zlink_connect (sender, endpoint) != ZLINK_CONNECT_OK) {
1846
+ zlink_close (sender);
1847
+ zlink_close (receiver);
1848
+ zlink_ctx_term (context);
1849
+ return throw_last_error (env, "stress connect failed");
1850
+ }
1851
+
1852
+ send_close_stress_counts_t counts;
1853
+ std::atomic<uint32_t> ready (0);
1854
+ std::atomic<bool> start (false);
1855
+ std::atomic<bool> close_finished (false);
1856
+ std::atomic<bool> senders_done (false);
1857
+ const uint64_t total_attempts =
1858
+ static_cast<uint64_t> (thread_count) * iterations;
1859
+ std::vector<std::thread> senders;
1860
+ senders.reserve (thread_count);
1861
+ for (uint32_t index = 0; index < thread_count; ++index) {
1862
+ senders.emplace_back (
1863
+ run_send_close_stress_sender, sender, index, iterations,
1864
+ total_attempts, &ready, &start, &close_finished, &counts);
1865
+ }
1866
+ std::thread receiver_thread (
1867
+ run_send_close_stress_receiver, receiver, &senders_done, &counts);
1868
+ std::thread closer_thread (
1869
+ run_send_close_stress_closer, sender, total_attempts,
1870
+ &close_finished, &counts);
1871
+
1872
+ while (ready.load (std::memory_order_acquire) != thread_count)
1873
+ std::this_thread::yield ();
1874
+ start.store (true, std::memory_order_release);
1875
+ for (size_t index = 0; index < senders.size (); ++index)
1876
+ senders[index].join ();
1877
+ closer_thread.join ();
1878
+ senders_done.store (true, std::memory_order_release);
1879
+ receiver_thread.join ();
1880
+
1881
+ zlink_close (receiver);
1882
+ zlink_ctx_term (context);
1883
+
1884
+ napi_value out;
1885
+ napi_create_object (env, &out);
1886
+ #define SET_STRESS_COUNT(name) \
1887
+ set_uint64_bigint_property ( \
1888
+ env, out, #name, counts.name.load (std::memory_order_relaxed))
1889
+ SET_STRESS_COUNT (attempts);
1890
+ SET_STRESS_COUNT (single_attempts);
1891
+ SET_STRESS_COUNT (multipart_attempts);
1892
+ SET_STRESS_COUNT (submitted);
1893
+ SET_STRESS_COUNT (rejected_einval);
1894
+ SET_STRESS_COUNT (shutdown);
1895
+ SET_STRESS_COUNT (backpressured);
1896
+ SET_STRESS_COUNT (other_submit);
1897
+ SET_STRESS_COUNT (received_records);
1898
+ SET_STRESS_COUNT (bad_records);
1899
+ SET_STRESS_COUNT (bad_first_parts);
1900
+ SET_STRESS_COUNT (bad_mixed_parts);
1901
+ SET_STRESS_COUNT (bad_part_counts);
1902
+ SET_STRESS_COUNT (bad_next_part_results);
1903
+ SET_STRESS_COUNT (close_ok);
1904
+ SET_STRESS_COUNT (close_busy);
1905
+ SET_STRESS_COUNT (close_shutdown);
1906
+ SET_STRESS_COUNT (close_other);
1907
+ #undef SET_STRESS_COUNT
1908
+ return out;
1909
+ }
1910
+
1911
+ napi_value socket_bind (napi_env env, napi_callback_info info)
1912
+ {
1913
+ napi_value argv[2];
1914
+ size_t argc = 2;
1915
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1916
+ void *sock = NULL;
1917
+ napi_get_value_external (env, argv[0], &sock);
1918
+ std::string addr = get_string (env, argv[1]);
1919
+ int rc = zlink_bind (sock, addr.c_str ());
1920
+ if (rc != 0) {
1921
+ char buf[128];
1922
+ snprintf (buf, sizeof (buf), "bind failed (result=%d)", rc);
1923
+ napi_throw_error (env, NULL, buf);
1924
+ return NULL;
1925
+ }
1926
+ napi_value ok;
1927
+ napi_get_undefined (env, &ok);
1928
+ return ok;
1929
+ }
1930
+
1931
+ napi_value socket_unbind (napi_env env, napi_callback_info info)
1932
+ {
1933
+ napi_value argv[2];
1934
+ size_t argc = 2;
1935
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1936
+ void *sock = NULL;
1937
+ napi_get_value_external (env, argv[0], &sock);
1938
+ std::string addr = get_string (env, argv[1]);
1939
+ int rc = zlink_unbind (sock, addr.c_str ());
1940
+ if (rc != 0)
1941
+ return throw_last_error (env, "unbind failed");
1942
+ napi_value ok;
1943
+ napi_get_undefined (env, &ok);
1944
+ return ok;
1945
+ }
1946
+
1947
+ napi_value socket_connect (napi_env env, napi_callback_info info)
1948
+ {
1949
+ napi_value argv[2];
1950
+ size_t argc = 2;
1951
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1952
+ void *sock = NULL;
1953
+ napi_get_value_external (env, argv[0], &sock);
1954
+ std::string addr = get_string (env, argv[1]);
1955
+ int rc = zlink_connect (sock, addr.c_str ());
1956
+ if (rc != 0)
1957
+ return throw_last_error (env, "connect failed");
1958
+ napi_value ok;
1959
+ napi_get_undefined (env, &ok);
1960
+ return ok;
1961
+ }
1962
+
1963
+ napi_value socket_disconnect (napi_env env, napi_callback_info info)
1964
+ {
1965
+ napi_value argv[2];
1966
+ size_t argc = 2;
1967
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1968
+ void *sock = NULL;
1969
+ napi_get_value_external (env, argv[0], &sock);
1970
+ std::string addr = get_string (env, argv[1]);
1971
+ int rc = zlink_disconnect (sock, addr.c_str ());
1972
+ if (rc != 0)
1973
+ return throw_last_error (env, "disconnect failed");
1974
+ napi_value ok;
1975
+ napi_get_undefined (env, &ok);
1976
+ return ok;
1977
+ }
1978
+
1979
+ napi_value socket_disconnect_rid (napi_env env, napi_callback_info info)
1980
+ {
1981
+ napi_value argv[2];
1982
+ size_t argc = 2;
1983
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
1984
+ void *sock = NULL;
1985
+ napi_get_value_external (env, argv[0], &sock);
1986
+ zlink_routing_id_t peer_rid;
1987
+ if (!parse_routing_id_value (env, argv[1], &peer_rid))
1988
+ return NULL;
1989
+ int rc = zlink_disconnect_rid (sock, &peer_rid);
1990
+ if (rc != 0)
1991
+ return throw_last_error (env, "disconnect_rid failed");
1992
+ napi_value ok;
1993
+ napi_get_undefined (env, &ok);
1994
+ return ok;
1995
+ }
1996
+
1997
+ napi_value socket_set_tls_server (napi_env env, napi_callback_info info)
1998
+ {
1999
+ napi_value argv[4];
2000
+ size_t argc = 4;
2001
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2002
+ void *sock = NULL;
2003
+ napi_get_value_external (env, argv[0], &sock);
2004
+ std::string cert = get_string (env, argv[1]);
2005
+ std::string key = get_string (env, argv[2]);
2006
+ int32_t require_client = 0;
2007
+ if (argc >= 4)
2008
+ napi_get_value_int32 (env, argv[3], &require_client);
2009
+ int rc = zlink_set_tls_server (sock, cert.c_str (), key.c_str (), require_client);
2010
+ if (rc != 0)
2011
+ return throw_last_error (env, "socket_set_tls_server failed");
2012
+ napi_value ok;
2013
+ napi_get_undefined (env, &ok);
2014
+ return ok;
2015
+ }
2016
+
2017
+ napi_value socket_set_tls_client (napi_env env, napi_callback_info info)
2018
+ {
2019
+ napi_value argv[4];
2020
+ size_t argc = 4;
2021
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2022
+ void *sock = NULL;
2023
+ napi_get_value_external (env, argv[0], &sock);
2024
+ std::string ca = get_string (env, argv[1]);
2025
+ std::string host = get_string (env, argv[2]);
2026
+ int32_t trust = 0;
2027
+ if (argc >= 4)
2028
+ napi_get_value_int32 (env, argv[3], &trust);
2029
+ int rc = zlink_set_tls_client (sock, ca.c_str (), host.c_str (), trust);
2030
+ if (rc != 0)
2031
+ return throw_last_error (env, "socket_set_tls_client failed");
2032
+ napi_value ok;
2033
+ napi_get_undefined (env, &ok);
2034
+ return ok;
2035
+ }
2036
+
2037
+
2038
+ static napi_value create_send_submit_result (napi_env env,
2039
+ int result,
2040
+ int native_errno)
2041
+ {
2042
+ napi_value out;
2043
+ napi_create_object (env, &out);
2044
+ set_int64_property (env, out, "result", result);
2045
+ set_int64_property (env, out, "nativeErrno", native_errno);
2046
+ return out;
2047
+ }
2048
+
2049
+ static napi_value create_completion_submit_result (
2050
+ napi_env env, int result, int native_errno, uint64_t completion_id)
2051
+ {
2052
+ napi_value out = create_send_submit_result (env, result, native_errno);
2053
+ set_uint64_bigint_property (env, out, "completionId", completion_id);
2054
+ return out;
2055
+ }
2056
+
2057
+ static bool parse_optional_routing_id (
2058
+ napi_env env, napi_value value, zlink_routing_id_t *storage,
2059
+ const zlink_routing_id_t **out)
2060
+ {
2061
+ napi_value null_value;
2062
+ bool is_null = false;
2063
+ napi_get_null (env, &null_value);
2064
+ napi_strict_equals (env, value, null_value, &is_null);
2065
+ if (is_null) {
2066
+ *out = NULL;
2067
+ return true;
2068
+ }
2069
+ if (!parse_routing_id_value (env, value, storage))
2070
+ return false;
2071
+ *out = storage;
2072
+ return true;
2073
+ }
2074
+
2075
+ static int submit_send_parts_016 (
2076
+ void *socket, const zlink_routing_id_t *target, zlink_msg_t *parts,
2077
+ size_t part_count, zlink_send_flags_t flags, void *user_context,
2078
+ zlink_completion_id_t *completion_id)
2079
+ {
2080
+ return submit_msg_parts (
2081
+ parts, part_count,
2082
+ [socket, target, flags, user_context, completion_id] (
2083
+ zlink_msg_t *part, zlink_part_flag_t part_flag, bool final) {
2084
+ return target
2085
+ ? zlink_send_part_rid (
2086
+ socket, target, part, flags, part_flag,
2087
+ final ? user_context : NULL, final ? completion_id : NULL)
2088
+ : zlink_send_part (
2089
+ socket, part, flags, part_flag,
2090
+ final ? user_context : NULL, final ? completion_id : NULL);
2091
+ });
2092
+ }
2093
+
2094
+ static int submit_request_parts_016 (
2095
+ void *socket, const zlink_routing_id_t *target, zlink_msg_t *parts,
2096
+ size_t part_count, zlink_send_flags_t flags, uint32_t timeout_ms,
2097
+ void *user_context, zlink_completion_id_t *completion_id)
2098
+ {
2099
+ return submit_msg_parts (
2100
+ parts, part_count,
2101
+ [socket, target, flags, timeout_ms, user_context, completion_id] (
2102
+ zlink_msg_t *part, zlink_part_flag_t part_flag, bool final) {
2103
+ return zlink_request_part (
2104
+ socket, target, part, flags, part_flag,
2105
+ final ? timeout_ms : 0u,
2106
+ final ? user_context : NULL, final ? completion_id : NULL);
2107
+ });
2108
+ }
2109
+
2110
+ napi_value socket_submit_send (napi_env env, napi_callback_info info)
2111
+ {
2112
+ napi_value argv[5];
2113
+ size_t argc = 5;
2114
+ void *success_ref = NULL;
2115
+ napi_get_cb_info (env, info, &argc, argv, NULL, &success_ref);
2116
+ if (argc < 5) {
2117
+ napi_throw_type_error (
2118
+ env, NULL,
2119
+ "socketSubmitSend requires (socket, parts, routingIdOrNull, flags, token)");
2120
+ return NULL;
2121
+ }
2122
+ void *socket = NULL;
2123
+ napi_get_value_external (env, argv[0], &socket);
2124
+ zlink_routing_id_t target_storage;
2125
+ const zlink_routing_id_t *target = NULL;
2126
+ if (!parse_optional_routing_id (env, argv[2], &target_storage, &target))
2127
+ return NULL;
2128
+ int32_t flags = 0;
2129
+ if (napi_get_value_int32 (env, argv[3], &flags) != napi_ok) {
2130
+ napi_throw_type_error (env, NULL, "send flags must be int32");
2131
+ return NULL;
2132
+ }
2133
+ uint64_t token = 0;
2134
+ if (!get_uint64_like (env, argv[4], &token)) {
2135
+ napi_throw_type_error (env, NULL, "completion token must be uint64");
2136
+ return NULL;
2137
+ }
2138
+ small_msg_storage_t parts;
2139
+ if (!build_msg_vector_or_single (env, argv[1], &parts))
2140
+ return NULL;
2141
+ zlink_completion_id_t completion_id = 0;
2142
+ void *user_context = token == 0
2143
+ ? NULL : reinterpret_cast<void *> (static_cast<uintptr_t> (token));
2144
+ const int result = submit_send_parts_016 (
2145
+ socket, target, parts.data (), parts.size (),
2146
+ static_cast<zlink_send_flags_t> (flags), user_context, &completion_id);
2147
+ const int native_errno = result == ZLINK_SUBMIT_OK ? 0 : zlink_errno ();
2148
+ parts.release ();
2149
+ if (result == ZLINK_SUBMIT_OK) {
2150
+ consume_native_message_value (env, argv[1]);
2151
+ if (completion_id == 0) {
2152
+ napi_value success;
2153
+ napi_get_reference_value (env, static_cast<napi_ref> (success_ref), &success);
2154
+ return success;
2155
+ }
2156
+ }
2157
+ return create_completion_submit_result (
2158
+ env, result, native_errno, completion_id);
2159
+ }
2160
+
2161
+ napi_value socket_submit_request (napi_env env, napi_callback_info info)
2162
+ {
2163
+ napi_value argv[6];
2164
+ size_t argc = 6;
2165
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2166
+ if (argc < 6) {
2167
+ napi_throw_type_error (
2168
+ env, NULL,
2169
+ "socketSubmitRequest requires (socket, targetOrNull, parts, timeoutMs, flags, token)");
2170
+ return NULL;
2171
+ }
2172
+ void *socket = NULL;
2173
+ napi_get_value_external (env, argv[0], &socket);
2174
+ zlink_routing_id_t target_storage;
2175
+ const zlink_routing_id_t *target = NULL;
2176
+ if (!parse_optional_routing_id (env, argv[1], &target_storage, &target))
2177
+ return NULL;
2178
+ int32_t timeout_ms = 0;
2179
+ int32_t flags = 0;
2180
+ if (napi_get_value_int32 (env, argv[3], &timeout_ms) != napi_ok
2181
+ || timeout_ms <= 0) {
2182
+ napi_throw_range_error (env, NULL, "request timeout must be positive");
2183
+ return NULL;
2184
+ }
2185
+ if (napi_get_value_int32 (env, argv[4], &flags) != napi_ok) {
2186
+ napi_throw_type_error (env, NULL, "request flags must be int32");
2187
+ return NULL;
2188
+ }
2189
+ uint64_t token = 0;
2190
+ if (!get_uint64_like (env, argv[5], &token)) {
2191
+ napi_throw_type_error (env, NULL, "completion token must be uint64");
2192
+ return NULL;
2193
+ }
2194
+ small_msg_storage_t parts;
2195
+ if (!build_msg_vector_or_single (env, argv[2], &parts))
2196
+ return NULL;
2197
+ zlink_completion_id_t completion_id = 0;
2198
+ void *user_context = token == 0
2199
+ ? NULL : reinterpret_cast<void *> (static_cast<uintptr_t> (token));
2200
+ const int result = submit_request_parts_016 (
2201
+ socket, target, parts.data (), parts.size (),
2202
+ static_cast<zlink_send_flags_t> (flags),
2203
+ static_cast<uint32_t> (timeout_ms), user_context, &completion_id);
2204
+ const int native_errno = result == ZLINK_SUBMIT_OK ? 0 : zlink_errno ();
2205
+ parts.release ();
2206
+ if (result == ZLINK_SUBMIT_OK)
2207
+ consume_native_message_value (env, argv[2]);
2208
+ return create_completion_submit_result (
2209
+ env, result, native_errno, completion_id);
2210
+ }
2211
+
2212
+ class completion_close_guard_t
2213
+ {
2214
+ public:
2215
+ explicit completion_close_guard_t (zlink_completion_t *value) : value_ (value) {}
2216
+ ~completion_close_guard_t ()
2217
+ {
2218
+ zlink_completion_close (value_);
2219
+ g_completion_close_count.fetch_add (1, std::memory_order_relaxed);
2220
+ }
2221
+ private:
2222
+ zlink_completion_t *value_;
2223
+ };
2224
+
2225
+ static napi_value create_completion_value (
2226
+ napi_env env, zlink_completion_t *completion)
2227
+ {
2228
+ completion_close_guard_t guard (completion);
2229
+ napi_value out;
2230
+ napi_create_object (env, &out);
2231
+ set_int64_property (env, out, "kind", completion->kind);
2232
+ set_uint64_bigint_property (
2233
+ env, out, "completionId", completion->completion_id);
2234
+ set_uint64_bigint_property (
2235
+ env, out, "userContext",
2236
+ static_cast<uint64_t> (
2237
+ reinterpret_cast<uintptr_t> (completion->user_context)));
2238
+ napi_value peer_rid = create_routing_id_value (env, completion->peer_rid);
2239
+ if (!peer_rid)
2240
+ return NULL;
2241
+ napi_set_named_property (env, out, "peerRoutingId", peer_rid);
2242
+ set_int64_property (env, out, "sendResult", completion->send_result);
2243
+ set_int64_property (
2244
+ env, out, "terminalErrno", completion->send_terminal_errno);
2245
+ set_int64_property (
2246
+ env, out, "requestResult", completion->request_result);
2247
+ if (completion->kind == ZLINK_COMPLETION_REQUEST
2248
+ && completion->request_result == ZLINK_REQUEST_OK) {
2249
+ napi_value parts;
2250
+ napi_create_array_with_length (
2251
+ env, completion->reply_part_count, &parts);
2252
+ for (size_t index = 0; index < completion->reply_part_count; ++index) {
2253
+ napi_value part = create_received_message_buffer (
2254
+ env, &completion->reply_parts[index]);
2255
+ if (!part)
2256
+ return NULL;
2257
+ napi_set_element (
2258
+ env, parts, static_cast<uint32_t> (index), part);
2259
+ }
2260
+ napi_set_named_property (env, out, "parts", parts);
2261
+ }
2262
+ return out;
2263
+ }
2264
+
2265
+ napi_value socket_completion_recv (napi_env env, napi_callback_info info)
2266
+ {
2267
+ napi_value argv[2];
2268
+ size_t argc = 2;
2269
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2270
+ if (argc < 2) {
2271
+ napi_throw_type_error (
2272
+ env, NULL, "socketCompletionRecv requires (socket, flags)");
2273
+ return NULL;
2274
+ }
2275
+ void *socket = NULL;
2276
+ napi_get_value_external (env, argv[0], &socket);
2277
+ int32_t flags = 0;
2278
+ napi_get_value_int32 (env, argv[1], &flags);
2279
+ zlink_completion_t completion;
2280
+ memset (&completion, 0, sizeof (completion));
2281
+ completion.struct_size = sizeof (completion);
2282
+ const int result = zlink_completion_recv (
2283
+ socket, &completion, static_cast<zlink_recv_flags_t> (flags));
2284
+ if (result == ZLINK_RECV_NO_DATA) {
2285
+ napi_value null_value;
2286
+ napi_get_null (env, &null_value);
2287
+ return null_value;
2288
+ }
2289
+ if (result != ZLINK_RECV_OK)
2290
+ return throw_last_error (env, "completion recv failed");
2291
+ return create_completion_value (env, &completion);
2292
+ }
2293
+
2294
+ namespace
2295
+ {
2296
+
2297
+ struct socket_readable_watch_t
2298
+ {
2299
+ uv_poll_t poll;
2300
+ napi_env env;
2301
+ napi_ref callback;
2302
+ napi_async_context async_context;
2303
+ bool closing;
2304
+ bool closed;
2305
+ bool finalized;
2306
+ };
2307
+
2308
+ static void delete_socket_readable_watch_if_finalized (
2309
+ socket_readable_watch_t *watch)
2310
+ {
2311
+ if (watch->closed && watch->finalized)
2312
+ delete watch;
2313
+ }
2314
+
2315
+ static void socket_readable_watch_closed (uv_handle_t *handle)
2316
+ {
2317
+ socket_readable_watch_t *watch =
2318
+ static_cast<socket_readable_watch_t *> (handle->data);
2319
+ watch->closed = true;
2320
+ if (watch->async_context) {
2321
+ napi_async_destroy (watch->env, watch->async_context);
2322
+ watch->async_context = NULL;
2323
+ }
2324
+ if (watch->callback) {
2325
+ napi_delete_reference (watch->env, watch->callback);
2326
+ watch->callback = NULL;
2327
+ }
2328
+ delete_socket_readable_watch_if_finalized (watch);
2329
+ }
2330
+
2331
+ static void close_socket_readable_watch (socket_readable_watch_t *watch)
2332
+ {
2333
+ if (watch->closing)
2334
+ return;
2335
+ watch->closing = true;
2336
+ uv_poll_stop (&watch->poll);
2337
+ uv_close (
2338
+ reinterpret_cast<uv_handle_t *> (&watch->poll),
2339
+ socket_readable_watch_closed);
2340
+ }
2341
+
2342
+ static void socket_readable_watch_finalize (
2343
+ napi_env, void *data, void *)
2344
+ {
2345
+ socket_readable_watch_t *watch =
2346
+ static_cast<socket_readable_watch_t *> (data);
2347
+ watch->finalized = true;
2348
+ if (!watch->closing)
2349
+ close_socket_readable_watch (watch);
2350
+ else
2351
+ delete_socket_readable_watch_if_finalized (watch);
2352
+ }
2353
+
2354
+ static void socket_readable_watch_ready (
2355
+ uv_poll_t *poll, int status, int)
2356
+ {
2357
+ socket_readable_watch_t *watch =
2358
+ static_cast<socket_readable_watch_t *> (poll->data);
2359
+ if (watch->closing || !watch->callback)
2360
+ return;
2361
+ napi_handle_scope scope;
2362
+ if (napi_open_handle_scope (watch->env, &scope) != napi_ok)
2363
+ return;
2364
+ napi_value callback;
2365
+ napi_value receiver;
2366
+ napi_value status_value;
2367
+ napi_get_reference_value (watch->env, watch->callback, &callback);
2368
+ napi_get_global (watch->env, &receiver);
2369
+ napi_create_int32 (watch->env, status, &status_value);
2370
+ napi_value argv[] = {status_value};
2371
+ napi_value ignored;
2372
+ // This callback enters JavaScript from libuv, not from a JavaScript call.
2373
+ // MakeCallback completes the Node callback scope, including its Promise
2374
+ // microtasks, before another event-loop callback starts.
2375
+ napi_make_callback (
2376
+ watch->env, watch->async_context, receiver, callback,
2377
+ sizeof (argv) / sizeof (*argv), argv,
2378
+ &ignored);
2379
+ napi_close_handle_scope (watch->env, scope);
2380
+ }
2381
+
2382
+ } // namespace
2383
+
2384
+ napi_value socket_readable_watch_start (napi_env env, napi_callback_info info)
2385
+ {
2386
+ napi_value argv[2];
2387
+ size_t argc = 2;
2388
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2389
+ if (argc < 2) {
2390
+ napi_throw_type_error (
2391
+ env, NULL, "socketReadableWatchStart requires (socket, callback)");
2392
+ return NULL;
2393
+ }
2394
+ void *socket = NULL;
2395
+ napi_get_value_external (env, argv[0], &socket);
2396
+ napi_valuetype callback_type = napi_undefined;
2397
+ napi_typeof (env, argv[1], &callback_type);
2398
+ if (!socket || callback_type != napi_function) {
2399
+ napi_throw_type_error (env, NULL, "invalid readable watch arguments");
2400
+ return NULL;
2401
+ }
2402
+
2403
+ zlink_fd_t fd;
2404
+ size_t fd_size = sizeof (fd);
2405
+ const zlink_config_result_t get_result =
2406
+ zlink_get_option (socket, ZLINK_OPT_FD, &fd, &fd_size);
2407
+ if (get_result != ZLINK_CONFIG_OK)
2408
+ return throw_last_error (env, "socket readable fd lookup failed");
2409
+
2410
+ uv_loop_t *loop = NULL;
2411
+ if (napi_get_uv_event_loop (env, &loop) != napi_ok || !loop) {
2412
+ napi_throw_error (env, NULL, "Node event loop is unavailable");
2413
+ return NULL;
2414
+ }
2415
+ socket_readable_watch_t *watch = new (std::nothrow) socket_readable_watch_t;
2416
+ if (!watch) {
2417
+ napi_throw_error (env, NULL, "socket readable watch allocation failed");
2418
+ return NULL;
2419
+ }
2420
+ memset (&watch->poll, 0, sizeof (watch->poll));
2421
+ watch->env = env;
2422
+ watch->callback = NULL;
2423
+ watch->async_context = NULL;
2424
+ watch->closing = false;
2425
+ watch->closed = false;
2426
+ watch->finalized = false;
2427
+ const int init_result = uv_poll_init_socket (loop, &watch->poll, fd);
2428
+ if (init_result != 0) {
2429
+ napi_throw_error (env, NULL, "socket readable watch start failed");
2430
+ delete watch;
2431
+ return NULL;
2432
+ }
2433
+ watch->poll.data = watch;
2434
+ if (napi_create_reference (env, argv[1], 1, &watch->callback) != napi_ok) {
2435
+ watch->finalized = true;
2436
+ close_socket_readable_watch (watch);
2437
+ napi_throw_error (env, NULL, "socket readable watch callback retention failed");
2438
+ return NULL;
2439
+ }
2440
+ // The retained callback is also the async resource, so its existing
2441
+ // reference keeps that resource alive until the uv handle closes.
2442
+ napi_value resource_name;
2443
+ if (napi_create_string_utf8 (
2444
+ env, "zlink:completion", NAPI_AUTO_LENGTH, &resource_name) != napi_ok
2445
+ || napi_async_init (
2446
+ env, argv[1], resource_name, &watch->async_context) != napi_ok) {
2447
+ watch->finalized = true;
2448
+ close_socket_readable_watch (watch);
2449
+ napi_throw_error (env, NULL, "completion async resource creation failed");
2450
+ return NULL;
2451
+ }
2452
+ const int start_result = uv_poll_start (
2453
+ &watch->poll, UV_READABLE, socket_readable_watch_ready);
2454
+ if (start_result != 0) {
2455
+ watch->finalized = true;
2456
+ close_socket_readable_watch (watch);
2457
+ napi_throw_error (env, NULL, "socket readable watch start failed");
2458
+ return NULL;
2459
+ }
2460
+
2461
+ napi_value out;
2462
+ napi_create_external (
2463
+ env, watch, socket_readable_watch_finalize, NULL, &out);
2464
+ return out;
2465
+ }
2466
+
2467
+ napi_value socket_readable_watch_stop (napi_env env, napi_callback_info info)
2468
+ {
2469
+ napi_value argv[1];
2470
+ size_t argc = 1;
2471
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2472
+ if (argc < 1) {
2473
+ napi_throw_type_error (
2474
+ env, NULL, "socketReadableWatchStop requires (watch)");
2475
+ return NULL;
2476
+ }
2477
+ void *data = NULL;
2478
+ if (napi_get_value_external (env, argv[0], &data) != napi_ok || !data) {
2479
+ napi_throw_type_error (env, NULL, "invalid socket readable watch");
2480
+ return NULL;
2481
+ }
2482
+ close_socket_readable_watch (
2483
+ static_cast<socket_readable_watch_t *> (data));
2484
+ napi_value out;
2485
+ napi_get_undefined (env, &out);
2486
+ return out;
2487
+ }
2488
+
2489
+ napi_value test_completion_close_count (napi_env env, napi_callback_info info)
2490
+ {
2491
+ napi_value argv[1];
2492
+ size_t argc = 1;
2493
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2494
+ bool reset = false;
2495
+ if (argc > 0)
2496
+ napi_get_value_bool (env, argv[0], &reset);
2497
+ const uint64_t value = reset
2498
+ ? g_completion_close_count.exchange (0, std::memory_order_relaxed)
2499
+ : g_completion_close_count.load (std::memory_order_relaxed);
2500
+ napi_value out;
2501
+ napi_create_bigint_uint64 (env, value, &out);
2502
+ return out;
2503
+ }
2504
+
2505
+ napi_value socket_request_sync (napi_env env, napi_callback_info info)
2506
+ {
2507
+ napi_value argv[4];
2508
+ size_t argc = 4;
2509
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2510
+ if (argc < 4) {
2511
+ napi_throw_type_error (
2512
+ env, NULL,
2513
+ "socketRequestSync requires (socket, targetOrNull, parts, timeoutMs)");
2514
+ return NULL;
2515
+ }
2516
+ void *socket = NULL;
2517
+ napi_get_value_external (env, argv[0], &socket);
2518
+ zlink_routing_id_t target_storage;
2519
+ const zlink_routing_id_t *target = NULL;
2520
+ if (!parse_optional_routing_id (env, argv[1], &target_storage, &target))
2521
+ return NULL;
2522
+ int32_t timeout_ms = 0;
2523
+ if (napi_get_value_int32 (env, argv[3], &timeout_ms) != napi_ok
2524
+ || timeout_ms <= 0) {
2525
+ napi_throw_range_error (env, NULL, "request timeout must be positive");
2526
+ return NULL;
2527
+ }
2528
+ small_msg_storage_t parts;
2529
+ if (!build_msg_vector_or_single (env, argv[2], &parts))
2530
+ return NULL;
2531
+ zlink_completion_id_t completion_id = 0;
2532
+ const int result = submit_request_parts_016 (
2533
+ socket, target, parts.data (), parts.size (),
2534
+ ZLINK_SEND_FLAGS_NONE, static_cast<uint32_t> (timeout_ms),
2535
+ NULL, &completion_id);
2536
+ const int native_errno = result == ZLINK_SUBMIT_OK ? 0 : zlink_errno ();
2537
+ parts.release ();
2538
+ if (result == ZLINK_SUBMIT_OK)
2539
+ consume_native_message_value (env, argv[2]);
2540
+
2541
+ napi_value out = create_completion_submit_result (
2542
+ env, result, native_errno, completion_id);
2543
+ napi_value completions;
2544
+ napi_create_array (env, &completions);
2545
+ napi_set_named_property (env, out, "completions", completions);
2546
+ if (result != ZLINK_SUBMIT_OK)
2547
+ return out;
2548
+
2549
+ uint32_t index = 0;
2550
+ bool found = false;
2551
+ while (!found) {
2552
+ zlink_completion_t completion;
2553
+ memset (&completion, 0, sizeof (completion));
2554
+ completion.struct_size = sizeof (completion);
2555
+ const int recv_result = zlink_completion_recv (
2556
+ socket, &completion, ZLINK_RECV_FLAGS_NONE);
2557
+ if (recv_result != ZLINK_RECV_OK)
2558
+ return throw_last_error (env, "request completion recv failed");
2559
+ found = completion.completion_id == completion_id;
2560
+ napi_value value = create_completion_value (env, &completion);
2561
+ if (!value)
2562
+ return NULL;
2563
+ napi_set_element (env, completions, index++, value);
2564
+ }
2565
+ return out;
2566
+ }
2567
+
2568
+ napi_value socket_publish (napi_env env, napi_callback_info info)
2569
+ {
2570
+ napi_value argv[4];
2571
+ size_t argc = 4;
2572
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2573
+ void *sock = NULL;
2574
+ napi_get_value_external (env, argv[0], &sock);
2575
+ char topic_stack[256];
2576
+ std::string topic_heap;
2577
+ const char *topic =
2578
+ get_c_string_arg (env, argv[1], topic_stack, sizeof (topic_stack), &topic_heap);
2579
+
2580
+ std::vector<zlink_msg_t> parts;
2581
+ zlink_msg_t single_part;
2582
+ bool use_single_part = false;
2583
+ bool is_buf = false;
2584
+ bool is_array = false;
2585
+ bool contains_native_frame = false;
2586
+ if (napi_is_buffer (env, argv[2], &is_buf) == napi_ok && is_buf) {
2587
+ if (!init_msg_from_value (env, argv[2], &single_part,
2588
+ &contains_native_frame))
2589
+ return throw_last_error (env, "publish failed");
2590
+ use_single_part = true;
2591
+ } else {
2592
+ napi_is_array (env, argv[2], &is_array);
2593
+ napi_valuetype payload_type = napi_undefined;
2594
+ napi_typeof (env, argv[2], &payload_type);
2595
+ if (is_array) {
2596
+ if (!build_msg_vector (env, argv[2], &parts))
2597
+ return NULL;
2598
+ } else if (payload_type == napi_object) {
2599
+ if (!init_msg_from_value (env, argv[2], &single_part,
2600
+ &contains_native_frame))
2601
+ return throw_last_error (env, "publish failed");
2602
+ use_single_part = true;
2603
+ } else if (!build_msg_vector (env, argv[2], &parts)) {
2604
+ return NULL;
2605
+ }
2606
+ }
2607
+
2608
+ int32_t flags = 0;
2609
+ napi_get_value_int32 (env, argv[3], &flags);
2610
+ size_t total = 0;
2611
+ if (use_single_part) {
2612
+ total = zlink_msg_size (&single_part);
2613
+ } else {
2614
+ for (size_t i = 0; i < parts.size (); ++i)
2615
+ total += zlink_msg_size (&parts[i]);
2616
+ }
2617
+ int rc =
2618
+ publish_parts (sock, topic, use_single_part ? &single_part : parts.data (),
2619
+ use_single_part ? 1 : parts.size (), static_cast<zlink_send_flags_t> (flags));
2620
+ if (rc != ZLINK_SUBMIT_OK) {
2621
+ return throw_submit_error (env, "publish failed", rc);
2622
+ }
2623
+ if (is_array || contains_native_frame)
2624
+ consume_native_message_value (env, argv[2]);
2625
+
2626
+ napi_value out;
2627
+ napi_create_int32 (env, static_cast<int32_t> (total), &out);
2628
+ return out;
2629
+ }
2630
+
2631
+ napi_value socket_try_publish (napi_env env, napi_callback_info info)
2632
+ {
2633
+ napi_value argv[3];
2634
+ size_t argc = 3;
2635
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2636
+ void *sock = NULL;
2637
+ napi_get_value_external (env, argv[0], &sock);
2638
+ char topic_stack[256];
2639
+ std::string topic_heap;
2640
+ const char *topic =
2641
+ get_c_string_arg (env, argv[1], topic_stack, sizeof (topic_stack), &topic_heap);
2642
+
2643
+ std::vector<zlink_msg_t> parts;
2644
+ zlink_msg_t single_part;
2645
+ bool use_single_part = false;
2646
+ bool is_buf = false;
2647
+ bool is_array = false;
2648
+ bool contains_native_frame = false;
2649
+ if (napi_is_buffer (env, argv[2], &is_buf) == napi_ok && is_buf) {
2650
+ void *data = NULL;
2651
+ size_t len = 0;
2652
+ if (napi_get_buffer_info (env, argv[2], &data, &len) != napi_ok) {
2653
+ napi_throw_type_error (env, NULL, "publish buffer invalid");
2654
+ return NULL;
2655
+ }
2656
+ if (!init_msg_from_bytes (&single_part, data, len))
2657
+ return throw_last_error (env, "publishNoWaitResult failed");
2658
+ use_single_part = true;
2659
+ } else {
2660
+ napi_is_array (env, argv[2], &is_array);
2661
+ napi_valuetype payload_type = napi_undefined;
2662
+ napi_typeof (env, argv[2], &payload_type);
2663
+ if (is_array) {
2664
+ if (!build_msg_vector (env, argv[2], &parts))
2665
+ return NULL;
2666
+ } else if (payload_type == napi_object) {
2667
+ if (!init_msg_from_value (env, argv[2], &single_part,
2668
+ &contains_native_frame))
2669
+ return throw_last_error (env, "publishNoWaitResult failed");
2670
+ use_single_part = true;
2671
+ } else if (!build_msg_vector (env, argv[2], &parts)) {
2672
+ return NULL;
2673
+ }
2674
+ }
2675
+
2676
+ int rc = publish_parts (sock, topic, use_single_part ? &single_part : parts.data (),
2677
+ use_single_part ? 1 : parts.size (), ZLINK_SEND_FLAGS_DONTWAIT);
2678
+ if (rc != ZLINK_SUBMIT_OK)
2679
+ rc = preserve_try_send_result (rc);
2680
+ if (rc < 0) {
2681
+ return throw_last_error (env, "publishNoWaitResult failed");
2682
+ }
2683
+ if (rc == ZLINK_SUBMIT_OK && (is_array || contains_native_frame))
2684
+ consume_native_message_value (env, argv[2]);
2685
+ napi_value out;
2686
+ napi_create_int32 (env, rc, &out);
2687
+ return out;
2688
+ }
2689
+
2690
+ int recv_message_value (napi_env env,
2691
+ void *sock,
2692
+ int32_t flags,
2693
+ napi_value *out,
2694
+ size_t *received_bytes = NULL)
2695
+ {
2696
+ if (!out)
2697
+ return ZLINK_RECV_INTERNAL_ERROR;
2698
+
2699
+ *out = NULL;
2700
+ zlink_routing_id_t routing_id;
2701
+ zlink_msg_t first_part;
2702
+ if (zlink_msg_init (&first_part) != 0)
2703
+ return ZLINK_RECV_INTERNAL_ERROR;
2704
+
2705
+ const zlink_routing_id_t *source_rid = NULL;
2706
+ zlink_part_flag_t has_more = ZLINK_PART_FINAL;
2707
+ const int rc = zlink_recv_part (sock, &source_rid, &first_part, &has_more,
2708
+ static_cast<zlink_recv_flags_t> (flags));
2709
+ if (rc != ZLINK_RECV_OK) {
2710
+ zlink_msg_close (&first_part);
2711
+ return rc;
2712
+ }
2713
+
2714
+ copy_routing_id (&routing_id, source_rid);
2715
+ if (has_more == ZLINK_PART_FINAL) {
2716
+ if (received_bytes)
2717
+ *received_bytes = zlink_msg_size (&first_part);
2718
+ if (routing_id.size == 0) {
2719
+ *out = create_received_message_buffer (env, &first_part);
2720
+ zlink_msg_close (&first_part);
2721
+ return *out ? ZLINK_RECV_OK : ZLINK_RECV_INTERNAL_ERROR;
2722
+ }
2723
+ *out = create_recv_message_value (env, routing_id, &first_part, 1);
2724
+ zlink_msg_close (&first_part);
2725
+ return *out ? ZLINK_RECV_OK : ZLINK_RECV_INTERNAL_ERROR;
2726
+ }
2727
+
2728
+ small_msg_storage_t parts;
2729
+ const int collect_rc = collect_recv_parts (sock, &first_part, has_more, &parts);
2730
+ if (collect_rc != ZLINK_RECV_OK)
2731
+ return collect_rc;
2732
+ if (received_bytes) {
2733
+ *received_bytes = 0;
2734
+ for (size_t index = 0; index < parts.size (); ++index)
2735
+ *received_bytes += zlink_msg_size (&parts[index]);
2736
+ }
2737
+ *out = create_recv_message_value (env, routing_id, parts.data (), parts.size ());
2738
+ parts.close ();
2739
+ return *out ? ZLINK_RECV_OK : ZLINK_RECV_INTERNAL_ERROR;
2740
+ }
2741
+
2742
+ napi_value socket_recv_message (napi_env env, napi_callback_info info)
2743
+ {
2744
+ napi_value argv[2];
2745
+ size_t argc = 2;
2746
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2747
+ void *sock = NULL;
2748
+ napi_get_value_external (env, argv[0], &sock);
2749
+ int32_t flags = 0;
2750
+ if (argc >= 2)
2751
+ napi_get_value_int32 (env, argv[1], &flags);
2752
+
2753
+ napi_value out;
2754
+ int rc = recv_message_value (env, sock, flags, &out);
2755
+ if (rc != ZLINK_RECV_OK)
2756
+ return throw_last_error (env, "recv failed");
2757
+ return out;
2758
+ }
2759
+
2760
+ napi_value socket_try_recv_message (napi_env env, napi_callback_info info)
2761
+ {
2762
+ napi_value argv[1];
2763
+ size_t argc = 1;
2764
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2765
+ void *sock = NULL;
2766
+ napi_get_value_external (env, argv[0], &sock);
2767
+
2768
+ napi_value out;
2769
+ int rc = recv_message_value (env, sock, static_cast<int32_t> (ZLINK_RECV_FLAGS_DONTWAIT),
2770
+ &out);
2771
+ if (rc != ZLINK_RECV_OK) {
2772
+ if (zlink_errno () == EAGAIN) {
2773
+ napi_value none;
2774
+ napi_get_null (env, &none);
2775
+ return none;
2776
+ }
2777
+ return throw_last_error (env, "tryReceive failed");
2778
+ }
2779
+ return out;
2780
+ }
2781
+
2782
+ class subscribe_topic_buffer_t
2783
+ {
2784
+ public:
2785
+ subscribe_topic_buffer_t () : data_ (stack_), size_ (sizeof (stack_)) {}
2786
+
2787
+ char *data () { return data_; }
2788
+ size_t size () const { return size_; }
2789
+
2790
+ void resize (size_t required_size)
2791
+ {
2792
+ heap_.assign (required_size > 0 ? required_size : 1, '\0');
2793
+ data_ = heap_.data ();
2794
+ size_ = heap_.size ();
2795
+ }
2796
+
2797
+ private:
2798
+ char stack_[256] = {};
2799
+ std::vector<char> heap_;
2800
+ char *data_;
2801
+ size_t size_;
2802
+ };
2803
+
2804
+ napi_value socket_subscribe_message (napi_env env, napi_callback_info info)
2805
+ {
2806
+ napi_value argv[1];
2807
+ size_t argc = 1;
2808
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2809
+ void *sock = NULL;
2810
+ napi_get_value_external (env, argv[0], &sock);
2811
+
2812
+ subscribe_topic_buffer_t topic;
2813
+ zlink_routing_id_t routing_id;
2814
+ small_msg_storage_t parts;
2815
+ size_t topic_len = topic.size ();
2816
+
2817
+ for (;;) {
2818
+ memset (&routing_id, 0, sizeof (routing_id));
2819
+ int rc = subscribe_parts (sock, &routing_id, topic.data (), topic.size (), &topic_len,
2820
+ &parts, ZLINK_RECV_FLAGS_NONE);
2821
+ if (rc == ZLINK_RECV_OK) {
2822
+ napi_value out = create_subscribed_value (env, routing_id, topic.data (), topic_len,
2823
+ parts.data (), parts.size ());
2824
+ parts.close ();
2825
+ return out;
2826
+ }
2827
+ if (zlink_errno () != EMSGSIZE)
2828
+ return throw_last_error (env, "subscribe failed");
2829
+ topic.resize (topic_len);
2830
+ }
2831
+ }
2832
+
2833
+ int try_subscribe_message_value (napi_env env,
2834
+ void *sock,
2835
+ napi_value *out,
2836
+ size_t *received_bytes = NULL)
2837
+ {
2838
+ subscribe_topic_buffer_t topic;
2839
+ zlink_routing_id_t routing_id;
2840
+ size_t topic_len = topic.size ();
2841
+
2842
+ for (;;) {
2843
+ memset (&routing_id, 0, sizeof (routing_id));
2844
+ small_msg_storage_t parts;
2845
+ const int rc = subscribe_parts (sock, &routing_id, topic.data (), topic.size (), &topic_len,
2846
+ &parts, ZLINK_RECV_FLAGS_DONTWAIT);
2847
+ if (rc == ZLINK_RECV_OK) {
2848
+ if (received_bytes) {
2849
+ *received_bytes = 0;
2850
+ for (size_t index = 0; index < parts.size (); ++index)
2851
+ *received_bytes += zlink_msg_size (&parts[index]);
2852
+ }
2853
+ *out = create_subscribed_value (env, routing_id, topic.data (), topic_len,
2854
+ parts.data (), parts.size ());
2855
+ parts.close ();
2856
+ return *out ? ZLINK_RECV_OK : ZLINK_RECV_INTERNAL_ERROR;
2857
+ }
2858
+ const int err = zlink_errno ();
2859
+ if (err == EAGAIN)
2860
+ return rc;
2861
+ if (err != EMSGSIZE)
2862
+ return rc;
2863
+ topic.resize (topic_len);
2864
+ }
2865
+ }
2866
+
2867
+ napi_value socket_try_subscribe_message (napi_env env, napi_callback_info info)
2868
+ {
2869
+ napi_value argv[1];
2870
+ size_t argc = 1;
2871
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2872
+ void *sock = NULL;
2873
+ napi_get_value_external (env, argv[0], &sock);
2874
+ napi_value out = NULL;
2875
+ const int rc = try_subscribe_message_value (env, sock, &out);
2876
+ if (rc == ZLINK_RECV_OK)
2877
+ return out;
2878
+ if (zlink_errno () == EAGAIN) {
2879
+ napi_value none;
2880
+ napi_get_null (env, &none);
2881
+ return none;
2882
+ }
2883
+ return throw_last_error (env, "subscribeNoWait failed");
2884
+ }
2885
+
2886
+ napi_value socket_subscription_event (napi_env env, napi_callback_info info)
2887
+ {
2888
+ napi_value argv[1];
2889
+ size_t argc = 1;
2890
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2891
+ void *sock = NULL;
2892
+ napi_get_value_external (env, argv[0], &sock);
2893
+
2894
+ std::vector<char> topic (256, '\0');
2895
+ zlink_routing_id_t routing_id;
2896
+ int subscribed = 0;
2897
+ size_t topic_len = topic.size ();
2898
+
2899
+ for (;;) {
2900
+ const zlink_routing_id_t *source_rid = NULL;
2901
+ memset (&routing_id, 0, sizeof (routing_id));
2902
+ int rc = zlink_xpub_recv_part (sock, &source_rid, &subscribed, topic.data (), topic.size (),
2903
+ &topic_len, ZLINK_RECV_FLAGS_NONE);
2904
+ if (rc == ZLINK_RECV_OK) {
2905
+ copy_routing_id (&routing_id, source_rid);
2906
+ return create_subscription_event_value (env, routing_id, subscribed, topic.data (),
2907
+ topic_len);
2908
+ }
2909
+ if (zlink_errno () != EMSGSIZE)
2910
+ return throw_last_error (env, "receiveSubscriptionEvent failed");
2911
+ topic.assign (topic_len > 0 ? topic_len : 1, '\0');
2912
+ }
2913
+ }
2914
+
2915
+ napi_value socket_try_subscription_event (napi_env env, napi_callback_info info)
2916
+ {
2917
+ napi_value argv[1];
2918
+ size_t argc = 1;
2919
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2920
+ void *sock = NULL;
2921
+ napi_get_value_external (env, argv[0], &sock);
2922
+
2923
+ std::vector<char> topic (256, '\0');
2924
+ zlink_routing_id_t routing_id;
2925
+ int subscribed = 0;
2926
+ size_t topic_len = topic.size ();
2927
+
2928
+ for (;;) {
2929
+ const zlink_routing_id_t *source_rid = NULL;
2930
+ memset (&routing_id, 0, sizeof (routing_id));
2931
+ int rc = zlink_xpub_recv_part (sock, &source_rid, &subscribed, topic.data (), topic.size (),
2932
+ &topic_len, ZLINK_RECV_FLAGS_DONTWAIT);
2933
+ if (rc == ZLINK_RECV_OK) {
2934
+ copy_routing_id (&routing_id, source_rid);
2935
+ return create_subscription_event_value (env, routing_id, subscribed, topic.data (),
2936
+ topic_len);
2937
+ }
2938
+ const int err = zlink_errno ();
2939
+ if (err == EAGAIN) {
2940
+ napi_value none;
2941
+ napi_get_null (env, &none);
2942
+ return none;
2943
+ }
2944
+ if (err != EMSGSIZE)
2945
+ return throw_last_error (env, "tryReceiveSubscriptionEvent failed");
2946
+ topic.assign (topic_len > 0 ? topic_len : 1, '\0');
2947
+ }
2948
+ }
2949
+
2950
+ napi_value subscription_at (napi_env env, napi_callback_info info)
2951
+ {
2952
+ napi_value argv[2];
2953
+ size_t argc = 2;
2954
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2955
+ if (argc < 2) {
2956
+ napi_throw_type_error (env, NULL, "subscriptionAt expects handle, index");
2957
+ return NULL;
2958
+ }
2959
+
2960
+ void *handle = NULL;
2961
+ napi_get_value_external (env, argv[0], &handle);
2962
+ uint32_t index = 0;
2963
+ napi_get_value_uint32 (env, argv[1], &index);
2964
+
2965
+ std::vector<char> filter (256, '\0');
2966
+ size_t filter_len = filter.size ();
2967
+ int is_pattern = 0;
2968
+ for (;;) {
2969
+ zlink_config_result_t rc = zlink_subscription_at (handle, static_cast<size_t> (index),
2970
+ filter.data (), &filter_len, &is_pattern);
2971
+ if (rc == ZLINK_CONFIG_OK) {
2972
+ napi_value obj;
2973
+ napi_create_object (env, &obj);
2974
+ napi_value filter_value;
2975
+ napi_create_string_utf8 (env, filter.data (), filter_len, &filter_value);
2976
+ napi_set_named_property (env, obj, "filter", filter_value);
2977
+ napi_value pattern_value;
2978
+ napi_get_boolean (env, is_pattern != 0, &pattern_value);
2979
+ napi_set_named_property (env, obj, "isPattern", pattern_value);
2980
+ return obj;
2981
+ }
2982
+ if (rc == ZLINK_CONFIG_NOT_FOUND) {
2983
+ napi_value none;
2984
+ napi_get_null (env, &none);
2985
+ return none;
2986
+ }
2987
+ if (zlink_errno () != EMSGSIZE)
2988
+ return throw_last_error (env, "subscription_at failed");
2989
+ filter.assign (filter_len > 0 ? filter_len : 1, '\0');
2990
+ }
2991
+ }
2992
+
2993
+ napi_value socket_stream_recv_packet (napi_env env, napi_callback_info info)
2994
+ {
2995
+ napi_value argv[2];
2996
+ size_t argc = 2;
2997
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
2998
+ if (argc < 2) {
2999
+ napi_throw_type_error (
3000
+ env, NULL, "socketStreamRecvPacket requires (socket, flags)");
3001
+ return NULL;
3002
+ }
3003
+ void *socket = NULL;
3004
+ napi_get_value_external (env, argv[0], &socket);
3005
+ int32_t flags = 0;
3006
+ napi_get_value_int32 (env, argv[1], &flags);
3007
+ const zlink_routing_id_t *source_rid = NULL;
3008
+ zlink_msg_t header;
3009
+ zlink_msg_t body;
3010
+ if (zlink_msg_init (&header) != 0 || zlink_msg_init (&body) != 0) {
3011
+ zlink_msg_close (&header);
3012
+ return throw_last_error (env, "stream packet init failed");
3013
+ }
3014
+ const int result = zlink_stream_recv_packet (
3015
+ socket, &source_rid, &header, &body,
3016
+ static_cast<zlink_recv_flags_t> (flags));
3017
+ if (result == ZLINK_RECV_NO_DATA) {
3018
+ zlink_msg_close (&header);
3019
+ zlink_msg_close (&body);
3020
+ napi_value null_value;
3021
+ napi_get_null (env, &null_value);
3022
+ return null_value;
3023
+ }
3024
+ if (result != ZLINK_RECV_OK) {
3025
+ zlink_msg_close (&header);
3026
+ zlink_msg_close (&body);
3027
+ return throw_last_error (env, "stream packet recv failed");
3028
+ }
3029
+ napi_value out;
3030
+ napi_create_object (env, &out);
3031
+ napi_value rid = create_routing_id_value (env, *source_rid);
3032
+ napi_value header_value = create_received_message_buffer (env, &header);
3033
+ napi_value body_value = create_received_message_buffer (env, &body);
3034
+ zlink_msg_close (&header);
3035
+ zlink_msg_close (&body);
3036
+ if (!rid || !header_value || !body_value)
3037
+ return NULL;
3038
+ napi_set_named_property (env, out, "routingId", rid);
3039
+ napi_set_named_property (env, out, "header", header_value);
3040
+ napi_set_named_property (env, out, "body", body_value);
3041
+ return out;
3042
+ }
3043
+
3044
+ napi_value socket_setopt (napi_env env, napi_callback_info info)
3045
+ {
3046
+ napi_value argv[3];
3047
+ size_t argc = 3;
3048
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3049
+ void *sock = NULL;
3050
+ napi_get_value_external (env, argv[0], &sock);
3051
+ int32_t opt = 0;
3052
+ napi_get_value_int32 (env, argv[1], &opt);
3053
+ void *data = NULL;
3054
+ size_t len = 0;
3055
+ if (napi_get_buffer_info (env, argv[2], &data, &len) != napi_ok) {
3056
+ napi_throw_type_error (env, NULL, "option value must be Buffer");
3057
+ return NULL;
3058
+ }
3059
+ int rc = set_socket_option (sock, opt, data, len);
3060
+ if (rc != 0)
3061
+ return throw_last_error (env, "setsockopt failed");
3062
+ napi_value ok;
3063
+ napi_get_undefined (env, &ok);
3064
+ return ok;
3065
+ }
3066
+
3067
+ napi_value socket_set_receive_flow_state (napi_env env, napi_callback_info info)
3068
+ {
3069
+ napi_value argv[2];
3070
+ size_t argc = 2;
3071
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3072
+ void *sock = NULL;
3073
+ napi_get_value_external (env, argv[0], &sock);
3074
+ int32_t state = 0;
3075
+ napi_get_value_int32 (env, argv[1], &state);
3076
+ const zlink_config_result_t rc = zlink_socket_set_receive_flow_state (
3077
+ sock, static_cast<zlink_receive_flow_state_t> (state));
3078
+ if (rc != ZLINK_CONFIG_OK)
3079
+ return throw_last_error (env, "socket_set_receive_flow_state failed");
3080
+ napi_value ok;
3081
+ napi_get_undefined (env, &ok);
3082
+ return ok;
3083
+ }
3084
+
3085
+ napi_value socket_getopt (napi_env env, napi_callback_info info)
3086
+ {
3087
+ napi_value argv[2];
3088
+ size_t argc = 2;
3089
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3090
+ void *sock = NULL;
3091
+ napi_get_value_external (env, argv[0], &sock);
3092
+ int32_t opt = 0;
3093
+ napi_get_value_int32 (env, argv[1], &opt);
3094
+ size_t len = initial_getopt_buffer_len (opt);
3095
+ void *data = NULL;
3096
+ napi_value buf;
3097
+ napi_create_buffer (env, len, &data, &buf);
3098
+ int rc = get_socket_option (sock, opt, data, &len);
3099
+ if (rc != 0)
3100
+ return throw_last_error (env, "getsockopt failed");
3101
+ if (len == initial_getopt_buffer_len (opt))
3102
+ return buf;
3103
+ napi_value out;
3104
+ napi_create_buffer_copy (env, len, data, NULL, &out);
3105
+ return out;
3106
+ }
3107
+
3108
+ napi_value socket_set_subscription (napi_env env, napi_callback_info info)
3109
+ {
3110
+ napi_value argv[2];
3111
+ size_t argc = 2;
3112
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3113
+ void *sock = NULL;
3114
+ napi_get_value_external (env, argv[0], &sock);
3115
+ char topic_stack[256];
3116
+ std::string topic_heap;
3117
+ const char *topic =
3118
+ get_c_string_arg (env, argv[1], topic_stack, sizeof (topic_stack), &topic_heap);
3119
+ int rc = zlink_set_subscription (sock, topic);
3120
+ if (rc != 0)
3121
+ return throw_last_error (env, "set subscription failed");
3122
+ napi_value ok;
3123
+ napi_get_undefined (env, &ok);
3124
+ return ok;
3125
+ }
3126
+
3127
+ napi_value socket_unset_subscription (napi_env env, napi_callback_info info)
3128
+ {
3129
+ napi_value argv[2];
3130
+ size_t argc = 2;
3131
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3132
+ void *sock = NULL;
3133
+ napi_get_value_external (env, argv[0], &sock);
3134
+ char topic_stack[256];
3135
+ std::string topic_heap;
3136
+ const char *topic =
3137
+ get_c_string_arg (env, argv[1], topic_stack, sizeof (topic_stack), &topic_heap);
3138
+ int rc = zlink_unset_subscription (sock, topic);
3139
+ if (rc != 0)
3140
+ return throw_last_error (env, "unset subscription failed");
3141
+ napi_value ok;
3142
+ napi_get_undefined (env, &ok);
3143
+ return ok;
3144
+ }
3145
+
3146
+ napi_value handle_set_routing_id (napi_env env, napi_callback_info info)
3147
+ {
3148
+ napi_value argv[2];
3149
+ size_t argc = 2;
3150
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3151
+ void *handle = NULL;
3152
+ napi_get_value_external (env, argv[0], &handle);
3153
+ zlink_routing_id_t routing_id;
3154
+ if (!parse_routing_id_value (env, argv[1], &routing_id))
3155
+ return NULL;
3156
+ int rc = zlink_set_routing_id (handle, routing_id.data, routing_id.size);
3157
+ if (rc != 0)
3158
+ return throw_last_error (env, "set_routing_id failed");
3159
+ napi_value ok;
3160
+ napi_get_undefined (env, &ok);
3161
+ return ok;
3162
+ }
3163
+
3164
+ napi_value handle_get_routing_id (napi_env env, napi_callback_info info)
3165
+ {
3166
+ napi_value argv[1];
3167
+ size_t argc = 1;
3168
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3169
+ void *handle = NULL;
3170
+ napi_get_value_external (env, argv[0], &handle);
3171
+ zlink_routing_id_t routing_id;
3172
+ memset (&routing_id, 0, sizeof (routing_id));
3173
+ int rc = zlink_get_routing_id (handle, &routing_id);
3174
+ if (rc != 0)
3175
+ return throw_last_error (env, "get_routing_id failed");
3176
+ return create_routing_id_value (env, routing_id);
3177
+ }
3178
+
3179
+ napi_value socket_reply (napi_env env, napi_callback_info info)
3180
+ {
3181
+ napi_value argv[4];
3182
+ size_t argc = 4;
3183
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3184
+ if (argc < 4) {
3185
+ napi_throw_type_error (
3186
+ env, NULL, "socketReply requires (socket, sourceRid, token, parts)");
3187
+ return NULL;
3188
+ }
3189
+ void *router = NULL;
3190
+ napi_get_value_external (env, argv[0], &router);
3191
+ zlink_routing_id_t source_rid;
3192
+ if (!parse_routing_id_value (env, argv[1], &source_rid))
3193
+ return NULL;
3194
+ uint64_t reply_token = 0;
3195
+ if (!get_uint64_like (env, argv[2], &reply_token)
3196
+ || reply_token == 0) {
3197
+ napi_throw_type_error (env, NULL, "reply token must be nonzero uint64");
3198
+ return NULL;
3199
+ }
3200
+ small_msg_storage_t parts;
3201
+ if (!build_msg_vector_or_single (env, argv[3], &parts))
3202
+ return NULL;
3203
+ const int result = reply_parts_016 (
3204
+ router, &source_rid, reply_token, parts.data (), parts.size ());
3205
+ parts.release ();
3206
+ if (result != ZLINK_SUBMIT_OK)
3207
+ return throw_submit_error (env, "reply failed", result);
3208
+ consume_native_message_value (env, argv[3]);
3209
+ napi_value out;
3210
+ napi_get_undefined (env, &out);
3211
+ return out;
3212
+ }
3213
+
3214
+ napi_value router_recv_message (napi_env env, napi_callback_info info)
3215
+ {
3216
+ napi_value argv[4];
3217
+ size_t argc = 4;
3218
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3219
+ void *router = NULL;
3220
+ napi_get_value_external (env, argv[0], &router);
3221
+ int32_t flags = 0;
3222
+ if (argc >= 2)
3223
+ napi_get_value_int32 (env, argv[1], &flags);
3224
+ bool prefer_managed_single_part = false;
3225
+ if (argc >= 3)
3226
+ napi_get_value_bool (env, argv[2], &prefer_managed_single_part);
3227
+ napi_value routing_id_storage = argc >= 4 ? argv[3] : NULL;
3228
+
3229
+ napi_value out = NULL;
3230
+ const int rc = router_recv_message_value (
3231
+ env, router, flags, prefer_managed_single_part, routing_id_storage, &out);
3232
+ if (rc != ZLINK_RECV_OK)
3233
+ return throw_last_error (env, "routerRecvMessage failed");
3234
+ return out;
3235
+ }
3236
+
3237
+ napi_value router_try_recv_message (napi_env env, napi_callback_info info)
3238
+ {
3239
+ napi_value argv[3];
3240
+ size_t argc = 3;
3241
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3242
+ void *router = NULL;
3243
+ napi_get_value_external (env, argv[0], &router);
3244
+ bool prefer_managed_single_part = false;
3245
+ if (argc >= 2)
3246
+ napi_get_value_bool (env, argv[1], &prefer_managed_single_part);
3247
+ napi_value routing_id_storage = argc >= 3 ? argv[2] : NULL;
3248
+
3249
+ napi_value out = NULL;
3250
+ const int rc = router_recv_message_value (
3251
+ env, router, ZLINK_RECV_FLAGS_DONTWAIT, prefer_managed_single_part,
3252
+ routing_id_storage, &out);
3253
+ if (rc != ZLINK_RECV_OK) {
3254
+ if (zlink_errno () == EAGAIN) {
3255
+ napi_value none;
3256
+ napi_get_null (env, &none);
3257
+ return none;
3258
+ }
3259
+ return throw_last_error (env, "routerRecvMessageNoWait failed");
3260
+ }
3261
+
3262
+ return out;
3263
+ }
3264
+
3265
+ napi_value monitor_open (napi_env env, napi_callback_info info)
3266
+ {
3267
+ napi_value argv[3];
3268
+ size_t argc = 3;
3269
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3270
+ void *sock = NULL;
3271
+ napi_get_value_external (env, argv[0], &sock);
3272
+ int32_t events = 0;
3273
+ napi_get_value_int32 (env, argv[1], &events);
3274
+ uint64_t monitor_hwm_bytes = 0;
3275
+ bool monitor_hwm_lossless = false;
3276
+ if (argc < 3
3277
+ || napi_get_value_bigint_uint64 (
3278
+ env, argv[2], &monitor_hwm_bytes, &monitor_hwm_lossless)
3279
+ != napi_ok
3280
+ || !monitor_hwm_lossless) {
3281
+ napi_throw_type_error (
3282
+ env, NULL, "monitorHwmBytes must be a lossless uint64 bigint");
3283
+ return NULL;
3284
+ }
3285
+ zlink_socket_monitor_open_options_t options{};
3286
+ options.events = static_cast<zlink_socket_monitor_event_mask_t> (events);
3287
+ options.monitor_hwm_bytes = monitor_hwm_bytes;
3288
+ void *mon = zlink_socket_monitor_open (sock, &options);
3289
+ if (!mon)
3290
+ return throw_last_error (env, "monitor_open failed");
3291
+ napi_value ext;
3292
+ napi_create_external (env, mon, NULL, NULL, &ext);
3293
+ return ext;
3294
+ }
3295
+
3296
+ napi_value monitor_recv (napi_env env, napi_callback_info info)
3297
+ {
3298
+ napi_value argv[2];
3299
+ size_t argc = 2;
3300
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3301
+ void *mon = NULL;
3302
+ napi_get_value_external (env, argv[0], &mon);
3303
+ (void) argv;
3304
+ zlink_monitor_event_t evt;
3305
+ int rc = zlink_socket_monitor_recv (mon, &evt, ZLINK_RECV_FLAGS_NONE);
3306
+ if (rc != 0)
3307
+ return throw_last_error (env, "monitor_recv failed");
3308
+ return create_socket_monitor_event_value (env, evt);
3309
+ }
3310
+
3311
+ napi_value monitor_try_recv (napi_env env, napi_callback_info info)
3312
+ {
3313
+ napi_value argv[1];
3314
+ size_t argc = 1;
3315
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3316
+ void *mon = NULL;
3317
+ napi_get_value_external (env, argv[0], &mon);
3318
+
3319
+ zlink_monitor_event_t evt;
3320
+ int rc = zlink_socket_monitor_recv (mon, &evt, ZLINK_RECV_FLAGS_DONTWAIT);
3321
+ if (rc != 0) {
3322
+ if (zlink_errno () == EAGAIN) {
3323
+ napi_value none;
3324
+ napi_get_null (env, &none);
3325
+ return none;
3326
+ }
3327
+ return throw_last_error (env, "monitor_try_recv failed");
3328
+ }
3329
+
3330
+ return create_socket_monitor_event_value (env, evt);
3331
+ }
3332
+
3333
+ napi_value monitor_status (napi_env env, napi_callback_info info)
3334
+ {
3335
+ napi_value argv[1];
3336
+ size_t argc = 1;
3337
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3338
+ void *monitor = NULL;
3339
+ napi_get_value_external (env, argv[0], &monitor);
3340
+
3341
+ zlink_monitor_status_t snapshot;
3342
+ memset (&snapshot, 0, sizeof (snapshot));
3343
+ int rc = zlink_monitor_status (monitor, &snapshot);
3344
+ if (rc != 0)
3345
+ return throw_last_error (env, "monitor_status failed");
3346
+ if (snapshot.abi_version != ZLINK_MONITOR_STATUS_ABI_VERSION
3347
+ || snapshot.struct_size != sizeof (zlink_monitor_status_t)) {
3348
+ napi_throw_error (env, NULL, "monitor_status returned an incompatible ABI snapshot");
3349
+ return NULL;
3350
+ }
3351
+
3352
+ return create_monitor_status_value (env, snapshot);
3353
+ }
3354
+
3355
+ napi_value monitor_close (napi_env env, napi_callback_info info)
3356
+ {
3357
+ napi_value argv[1];
3358
+ size_t argc = 1;
3359
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3360
+ void *monitor = NULL;
3361
+ napi_get_value_external (env, argv[0], &monitor);
3362
+ void *tmp = monitor;
3363
+ int rc = zlink_monitor_close (&tmp);
3364
+ if (rc != 0)
3365
+ return throw_last_error (env, "monitor_close failed");
3366
+ napi_value ok;
3367
+ napi_get_undefined (env, &ok);
3368
+ return ok;
3369
+ }
3370
+
3371
+ static napi_value create_external_or_null (napi_env env, void *ptr)
3372
+ {
3373
+ napi_value out;
3374
+ if (!ptr) {
3375
+ napi_get_null (env, &out);
3376
+ return out;
3377
+ }
3378
+ napi_create_external (env, ptr, NULL, NULL, &out);
3379
+ return out;
3380
+ }
3381
+
3382
+ static napi_value create_userdata_value (napi_env env, void *ptr)
3383
+ {
3384
+ napi_value out;
3385
+ if (!ptr) {
3386
+ napi_get_null (env, &out);
3387
+ return out;
3388
+ }
3389
+ napi_create_bigint_uint64 (env, static_cast<uint64_t> (reinterpret_cast<uintptr_t> (ptr)),
3390
+ &out);
3391
+ return out;
3392
+ }
3393
+
3394
+ static void *get_external_or_null (napi_env env, napi_value value)
3395
+ {
3396
+ if (!value)
3397
+ return NULL;
3398
+ napi_valuetype type;
3399
+ if (napi_typeof (env, value, &type) != napi_ok)
3400
+ return NULL;
3401
+ if (type == napi_null || type == napi_undefined)
3402
+ return NULL;
3403
+ if (type == napi_bigint) {
3404
+ uint64_t raw = 0;
3405
+ bool lossless = false;
3406
+ if (napi_get_value_bigint_uint64 (env, value, &raw, &lossless) == napi_ok && lossless)
3407
+ return reinterpret_cast<void *> (static_cast<uintptr_t> (raw));
3408
+ return NULL;
3409
+ }
3410
+ if (type == napi_number) {
3411
+ uint32_t raw = 0;
3412
+ if (napi_get_value_uint32 (env, value, &raw) == napi_ok)
3413
+ return reinterpret_cast<void *> (static_cast<uintptr_t> (raw));
3414
+ return NULL;
3415
+ }
3416
+ if (type != napi_external)
3417
+ return NULL;
3418
+ void *ptr = NULL;
3419
+ if (napi_get_value_external (env, value, &ptr) != napi_ok)
3420
+ return NULL;
3421
+ return ptr;
3422
+ }
3423
+
3424
+ static napi_value create_poller_event_value (napi_env env, const zlink_poller_event_t &event)
3425
+ {
3426
+ napi_value obj;
3427
+ napi_create_object (env, &obj);
3428
+
3429
+ napi_value value;
3430
+ napi_create_uint32 (env, static_cast<uint32_t> (event.source_kind), &value);
3431
+ napi_set_named_property (env, obj, "sourceKind", value);
3432
+ napi_set_named_property (env, obj, "socket", create_external_or_null (env, event.socket));
3433
+ napi_create_int64 (env, static_cast<int64_t> (event.fd), &value);
3434
+ napi_set_named_property (env, obj, "fd", value);
3435
+ napi_set_named_property (env, obj, "timer", create_external_or_null (env, event.timer));
3436
+ napi_set_named_property (env, obj, "userData", create_userdata_value (env, event.user_data));
3437
+ napi_create_int32 (env, static_cast<int32_t> (event.events), &value);
3438
+ napi_set_named_property (env, obj, "events", value);
3439
+ return obj;
3440
+ }
3441
+
3442
+ static napi_value
3443
+ create_poller_event_result (napi_env env, const zlink_poller_event_t &event, int rc)
3444
+ {
3445
+ if (rc <= 0) {
3446
+ napi_value none;
3447
+ napi_get_null (env, &none);
3448
+ return none;
3449
+ }
3450
+ return create_poller_event_value (env, event);
3451
+ }
3452
+
3453
+ static napi_value create_stopwatch_value (napi_env env, unsigned long value)
3454
+ {
3455
+ napi_value out;
3456
+ napi_create_double (env, static_cast<double> (value), &out);
3457
+ return out;
3458
+ }
3459
+
3460
+ napi_value poller_new (napi_env env, napi_callback_info info)
3461
+ {
3462
+ (void) info;
3463
+ void *poller = zlink_poller_new ();
3464
+ if (!poller)
3465
+ return throw_last_error (env, "poller_new failed");
3466
+ napi_value out;
3467
+ napi_create_external (env, poller, NULL, NULL, &out);
3468
+ return out;
3469
+ }
3470
+
3471
+ napi_value poller_destroy (napi_env env, napi_callback_info info)
3472
+ {
3473
+ napi_value argv[1];
3474
+ size_t argc = 1;
3475
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3476
+ void *poller = NULL;
3477
+ napi_get_value_external (env, argv[0], &poller);
3478
+ void *tmp = poller;
3479
+ int rc = zlink_poller_destroy (&tmp);
3480
+ if (rc != 0)
3481
+ return throw_last_error (env, "poller_destroy failed");
3482
+ napi_value ok;
3483
+ napi_get_undefined (env, &ok);
3484
+ return ok;
3485
+ }
3486
+
3487
+ napi_value poller_size (napi_env env, napi_callback_info info)
3488
+ {
3489
+ napi_value argv[1];
3490
+ size_t argc = 1;
3491
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3492
+ void *poller = NULL;
3493
+ napi_get_value_external (env, argv[0], &poller);
3494
+ zlink_config_result_t err = ZLINK_CONFIG_OK;
3495
+ int size = zlink_poller_size (poller, &err);
3496
+ if (err != ZLINK_CONFIG_OK)
3497
+ return throw_last_error (env, "poller_size failed");
3498
+ napi_value out;
3499
+ napi_create_int32 (env, size, &out);
3500
+ return out;
3501
+ }
3502
+
3503
+ napi_value poller_add (napi_env env, napi_callback_info info)
3504
+ {
3505
+ napi_value argv[4];
3506
+ size_t argc = 4;
3507
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3508
+ void *poller = NULL;
3509
+ void *socket = NULL;
3510
+ napi_get_value_external (env, argv[0], &poller);
3511
+ napi_get_value_external (env, argv[1], &socket);
3512
+ int32_t events = 0;
3513
+ napi_get_value_int32 (env, argv[3], &events);
3514
+ void *user_data = argc >= 3 ? get_external_or_null (env, argv[2]) : NULL;
3515
+ zlink_config_result_t rc = zlink_poller_add (poller, socket, user_data, (short) events);
3516
+ if (rc != ZLINK_CONFIG_OK)
3517
+ return throw_last_error (env, "poller_add failed");
3518
+ napi_value ok;
3519
+ napi_get_undefined (env, &ok);
3520
+ return ok;
3521
+ }
3522
+
3523
+ napi_value poller_modify (napi_env env, napi_callback_info info)
3524
+ {
3525
+ napi_value argv[3];
3526
+ size_t argc = 3;
3527
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3528
+ void *poller = NULL;
3529
+ void *socket = NULL;
3530
+ napi_get_value_external (env, argv[0], &poller);
3531
+ napi_get_value_external (env, argv[1], &socket);
3532
+ int32_t events = 0;
3533
+ napi_get_value_int32 (env, argv[2], &events);
3534
+ zlink_config_result_t rc = zlink_poller_modify (poller, socket, (short) events);
3535
+ if (rc != ZLINK_CONFIG_OK)
3536
+ return throw_last_error (env, "poller_modify failed");
3537
+ napi_value ok;
3538
+ napi_get_undefined (env, &ok);
3539
+ return ok;
3540
+ }
3541
+
3542
+ napi_value poller_remove (napi_env env, napi_callback_info info)
3543
+ {
3544
+ napi_value argv[2];
3545
+ size_t argc = 2;
3546
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3547
+ void *poller = NULL;
3548
+ void *socket = NULL;
3549
+ napi_get_value_external (env, argv[0], &poller);
3550
+ napi_get_value_external (env, argv[1], &socket);
3551
+ zlink_config_result_t rc = zlink_poller_remove (poller, socket);
3552
+ if (rc != ZLINK_CONFIG_OK)
3553
+ return throw_last_error (env, "poller_remove failed");
3554
+ napi_value ok;
3555
+ napi_get_undefined (env, &ok);
3556
+ return ok;
3557
+ }
3558
+
3559
+ napi_value poller_add_fd (napi_env env, napi_callback_info info)
3560
+ {
3561
+ napi_value argv[4];
3562
+ size_t argc = 4;
3563
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3564
+ void *poller = NULL;
3565
+ napi_get_value_external (env, argv[0], &poller);
3566
+ int32_t fd = 0, events = 0;
3567
+ napi_get_value_int32 (env, argv[1], &fd);
3568
+ napi_get_value_int32 (env, argv[3], &events);
3569
+ void *user_data = argc >= 3 ? get_external_or_null (env, argv[2]) : NULL;
3570
+ zlink_config_result_t rc = zlink_poller_add_fd (poller, fd, user_data, (short) events);
3571
+ if (rc != ZLINK_CONFIG_OK)
3572
+ return throw_last_error (env, "poller_add_fd failed");
3573
+ napi_value ok;
3574
+ napi_get_undefined (env, &ok);
3575
+ return ok;
3576
+ }
3577
+
3578
+ napi_value poller_modify_fd (napi_env env, napi_callback_info info)
3579
+ {
3580
+ napi_value argv[3];
3581
+ size_t argc = 3;
3582
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3583
+ void *poller = NULL;
3584
+ napi_get_value_external (env, argv[0], &poller);
3585
+ int32_t fd = 0, events = 0;
3586
+ napi_get_value_int32 (env, argv[1], &fd);
3587
+ napi_get_value_int32 (env, argv[2], &events);
3588
+ zlink_config_result_t rc = zlink_poller_modify_fd (poller, fd, (short) events);
3589
+ if (rc != ZLINK_CONFIG_OK)
3590
+ return throw_last_error (env, "poller_modify_fd failed");
3591
+ napi_value ok;
3592
+ napi_get_undefined (env, &ok);
3593
+ return ok;
3594
+ }
3595
+
3596
+ napi_value poller_remove_fd (napi_env env, napi_callback_info info)
3597
+ {
3598
+ napi_value argv[2];
3599
+ size_t argc = 2;
3600
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3601
+ void *poller = NULL;
3602
+ napi_get_value_external (env, argv[0], &poller);
3603
+ int32_t fd = 0;
3604
+ napi_get_value_int32 (env, argv[1], &fd);
3605
+ zlink_config_result_t rc = zlink_poller_remove_fd (poller, fd);
3606
+ if (rc != ZLINK_CONFIG_OK)
3607
+ return throw_last_error (env, "poller_remove_fd failed");
3608
+ napi_value ok;
3609
+ napi_get_undefined (env, &ok);
3610
+ return ok;
3611
+ }
3612
+
3613
+ napi_value poller_add_timer (napi_env env, napi_callback_info info)
3614
+ {
3615
+ napi_value argv[3];
3616
+ size_t argc = 3;
3617
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3618
+ void *poller = NULL;
3619
+ void *timer = NULL;
3620
+ napi_get_value_external (env, argv[0], &poller);
3621
+ napi_get_value_external (env, argv[1], &timer);
3622
+ void *user_data = argc >= 3 ? get_external_or_null (env, argv[2]) : NULL;
3623
+ zlink_config_result_t rc = zlink_poller_add_timer (poller, timer, user_data);
3624
+ if (rc != ZLINK_CONFIG_OK)
3625
+ return throw_last_error (env, "poller_add_timer failed");
3626
+ napi_value ok;
3627
+ napi_get_undefined (env, &ok);
3628
+ return ok;
3629
+ }
3630
+
3631
+ napi_value poller_remove_timer (napi_env env, napi_callback_info info)
3632
+ {
3633
+ napi_value argv[2];
3634
+ size_t argc = 2;
3635
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3636
+ void *poller = NULL;
3637
+ void *timer = NULL;
3638
+ napi_get_value_external (env, argv[0], &poller);
3639
+ napi_get_value_external (env, argv[1], &timer);
3640
+ zlink_config_result_t rc = zlink_poller_remove_timer (poller, timer);
3641
+ if (rc != ZLINK_CONFIG_OK)
3642
+ return throw_last_error (env, "poller_remove_timer failed");
3643
+ napi_value ok;
3644
+ napi_get_undefined (env, &ok);
3645
+ return ok;
3646
+ }
3647
+
3648
+ napi_value poller_wait (napi_env env, napi_callback_info info)
3649
+ {
3650
+ napi_value argv[2];
3651
+ size_t argc = 2;
3652
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3653
+ void *poller = NULL;
3654
+ napi_get_value_external (env, argv[0], &poller);
3655
+ int32_t timeout = 0;
3656
+ napi_get_value_int32 (env, argv[1], &timeout);
3657
+ zlink_poller_event_t event;
3658
+ memset (&event, 0, sizeof (event));
3659
+ zlink_config_result_t err = ZLINK_CONFIG_OK;
3660
+ int rc = zlink_poller_wait (poller, &event, 1, timeout, &err);
3661
+ if (err != ZLINK_CONFIG_OK)
3662
+ return throw_last_error (env, "poller_wait failed");
3663
+ return create_poller_event_result (env, event, rc);
3664
+ }
3665
+
3666
+ napi_value poll_events_new (napi_env env, napi_callback_info info)
3667
+ {
3668
+ napi_value argv[1];
3669
+ size_t argc = 1;
3670
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3671
+ int32_t capacity = 0;
3672
+ napi_get_value_int32 (env, argv[0], &capacity);
3673
+ if (capacity <= 0) {
3674
+ napi_throw_range_error (env, NULL, "capacity must be a positive integer");
3675
+ return NULL;
3676
+ }
3677
+ zlink_poller_event_t *events = new zlink_poller_event_t[static_cast<size_t> (capacity)]();
3678
+ napi_value out;
3679
+ napi_create_external (env, events, NULL, NULL, &out);
3680
+ return out;
3681
+ }
3682
+
3683
+ napi_value poll_events_destroy (napi_env env, napi_callback_info info)
3684
+ {
3685
+ napi_value argv[1];
3686
+ size_t argc = 1;
3687
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3688
+ void *ptr = NULL;
3689
+ napi_get_value_external (env, argv[0], &ptr);
3690
+ delete[] static_cast<zlink_poller_event_t *> (ptr);
3691
+ napi_value ok;
3692
+ napi_get_undefined (env, &ok);
3693
+ return ok;
3694
+ }
3695
+
3696
+ static bool poll_events_args (napi_env env,
3697
+ napi_callback_info info,
3698
+ zlink_poller_event_t **events,
3699
+ int32_t *index)
3700
+ {
3701
+ napi_value argv[2];
3702
+ size_t argc = 2;
3703
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3704
+ void *ptr = NULL;
3705
+ napi_get_value_external (env, argv[0], &ptr);
3706
+ napi_get_value_int32 (env, argv[1], index);
3707
+ if (!ptr || *index < 0) {
3708
+ napi_throw_range_error (env, NULL, "invalid poll event index");
3709
+ return false;
3710
+ }
3711
+ *events = static_cast<zlink_poller_event_t *> (ptr);
3712
+ return true;
3713
+ }
3714
+
3715
+ napi_value poll_events_source_kind (napi_env env, napi_callback_info info)
3716
+ {
3717
+ zlink_poller_event_t *events = NULL;
3718
+ int32_t index = 0;
3719
+ if (!poll_events_args (env, info, &events, &index))
3720
+ return NULL;
3721
+ napi_value out;
3722
+ napi_create_int32 (env, static_cast<int32_t> (events[index].source_kind), &out);
3723
+ return out;
3724
+ }
3725
+
3726
+ napi_value poll_events_slot (napi_env env, napi_callback_info info)
3727
+ {
3728
+ zlink_poller_event_t *events = NULL;
3729
+ int32_t index = 0;
3730
+ if (!poll_events_args (env, info, &events, &index))
3731
+ return NULL;
3732
+ napi_value out;
3733
+ napi_create_double (
3734
+ env, static_cast<double> (reinterpret_cast<uintptr_t> (events[index].user_data)), &out);
3735
+ return out;
3736
+ }
3737
+
3738
+ napi_value poll_events_revents (napi_env env, napi_callback_info info)
3739
+ {
3740
+ zlink_poller_event_t *events = NULL;
3741
+ int32_t index = 0;
3742
+ if (!poll_events_args (env, info, &events, &index))
3743
+ return NULL;
3744
+ napi_value out;
3745
+ napi_create_int32 (env, static_cast<int32_t> (events[index].events), &out);
3746
+ return out;
3747
+ }
3748
+
3749
+ napi_value poll_events_fd (napi_env env, napi_callback_info info)
3750
+ {
3751
+ zlink_poller_event_t *events = NULL;
3752
+ int32_t index = 0;
3753
+ if (!poll_events_args (env, info, &events, &index))
3754
+ return NULL;
3755
+ napi_value out;
3756
+ napi_create_int64 (env, static_cast<int64_t> (events[index].fd), &out);
3757
+ return out;
3758
+ }
3759
+
3760
+ napi_value poller_wait_into (napi_env env, napi_callback_info info)
3761
+ {
3762
+ napi_value argv[4];
3763
+ size_t argc = 4;
3764
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3765
+ void *poller = NULL;
3766
+ void *events_ptr = NULL;
3767
+ int32_t capacity = 0;
3768
+ int32_t timeout = 0;
3769
+ napi_get_value_external (env, argv[0], &poller);
3770
+ napi_get_value_external (env, argv[1], &events_ptr);
3771
+ napi_get_value_int32 (env, argv[2], &capacity);
3772
+ napi_get_value_int32 (env, argv[3], &timeout);
3773
+ if (!events_ptr || capacity <= 0) {
3774
+ napi_throw_range_error (env, NULL, "events capacity must be positive");
3775
+ return NULL;
3776
+ }
3777
+ zlink_config_result_t err = ZLINK_CONFIG_OK;
3778
+ int rc = zlink_poller_wait (poller, static_cast<zlink_poller_event_t *> (events_ptr), capacity,
3779
+ timeout, &err);
3780
+ if (err != ZLINK_CONFIG_OK)
3781
+ return throw_last_error (env, "poller_wait_into failed");
3782
+ napi_value out;
3783
+ napi_create_int32 (env, rc, &out);
3784
+ return out;
3785
+ }
3786
+
3787
+ napi_value timer_new (napi_env env, napi_callback_info info)
3788
+ {
3789
+ (void) info;
3790
+ void *timer = zlink_timer_new ();
3791
+ if (!timer)
3792
+ return throw_last_error (env, "timer_new failed");
3793
+ napi_value out;
3794
+ napi_create_external (env, timer, NULL, NULL, &out);
3795
+ return out;
3796
+ }
3797
+
3798
+ napi_value timer_destroy (napi_env env, napi_callback_info info)
3799
+ {
3800
+ napi_value argv[1];
3801
+ size_t argc = 1;
3802
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3803
+ void *timer = NULL;
3804
+ napi_get_value_external (env, argv[0], &timer);
3805
+ void *tmp = timer;
3806
+ int rc = zlink_timer_destroy (&tmp);
3807
+ if (rc != 0)
3808
+ return throw_last_error (env, "timer_destroy failed");
3809
+ napi_value ok;
3810
+ napi_get_undefined (env, &ok);
3811
+ return ok;
3812
+ }
3813
+
3814
+ napi_value timer_start (napi_env env, napi_callback_info info)
3815
+ {
3816
+ napi_value argv[3];
3817
+ size_t argc = 3;
3818
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3819
+ void *timer = NULL;
3820
+ napi_get_value_external (env, argv[0], &timer);
3821
+ bool lossless = false;
3822
+ uint64_t interval = 0;
3823
+ uint64_t repeat = 0;
3824
+ napi_get_value_bigint_uint64 (env, argv[1], &interval, &lossless);
3825
+ napi_get_value_bigint_uint64 (env, argv[2], &repeat, &lossless);
3826
+ zlink_config_result_t rc = zlink_timer_start (timer, interval, repeat);
3827
+ if (rc != ZLINK_CONFIG_OK)
3828
+ return throw_last_error (env, "timer_start failed");
3829
+ napi_value ok;
3830
+ napi_get_undefined (env, &ok);
3831
+ return ok;
3832
+ }
3833
+
3834
+ napi_value timer_stop (napi_env env, napi_callback_info info)
3835
+ {
3836
+ napi_value argv[1];
3837
+ size_t argc = 1;
3838
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3839
+ void *timer = NULL;
3840
+ napi_get_value_external (env, argv[0], &timer);
3841
+ zlink_config_result_t rc = zlink_timer_stop (timer);
3842
+ if (rc != ZLINK_CONFIG_OK)
3843
+ return throw_last_error (env, "timer_stop failed");
3844
+ napi_value ok;
3845
+ napi_get_undefined (env, &ok);
3846
+ return ok;
3847
+ }
3848
+
3849
+ napi_value timer_recv (napi_env env, napi_callback_info info)
3850
+ {
3851
+ napi_value argv[2];
3852
+ size_t argc = 2;
3853
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3854
+ void *timer = NULL;
3855
+ napi_get_value_external (env, argv[0], &timer);
3856
+ int32_t flags = 0;
3857
+ if (argc >= 2)
3858
+ napi_get_value_int32 (env, argv[1], &flags);
3859
+ uint64_t fire_count = 0;
3860
+ zlink_recv_result_t rc = zlink_timer_recv (timer, &fire_count);
3861
+ if (rc != ZLINK_RECV_OK)
3862
+ return throw_last_error (env, "timer_recv failed");
3863
+ napi_value out;
3864
+ napi_create_bigint_uint64 (env, fire_count, &out);
3865
+ return out;
3866
+ }
3867
+
3868
+ napi_value stopwatch_start (napi_env env, napi_callback_info info)
3869
+ {
3870
+ (void) info;
3871
+ void *watch = zlink_stopwatch_start ();
3872
+ if (!watch)
3873
+ return throw_last_error (env, "stopwatch_start failed");
3874
+ napi_value out;
3875
+ napi_create_external (env, watch, NULL, NULL, &out);
3876
+ return out;
3877
+ }
3878
+
3879
+ napi_value stopwatch_intermediate (napi_env env, napi_callback_info info)
3880
+ {
3881
+ napi_value argv[1];
3882
+ size_t argc = 1;
3883
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3884
+ void *watch = NULL;
3885
+ napi_get_value_external (env, argv[0], &watch);
3886
+ unsigned long value = zlink_stopwatch_intermediate (watch);
3887
+ return create_stopwatch_value (env, value);
3888
+ }
3889
+
3890
+ napi_value stopwatch_stop (napi_env env, napi_callback_info info)
3891
+ {
3892
+ napi_value argv[1];
3893
+ size_t argc = 1;
3894
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3895
+ void *watch = NULL;
3896
+ napi_get_value_external (env, argv[0], &watch);
3897
+ unsigned long value = zlink_stopwatch_stop (watch);
3898
+ return create_stopwatch_value (env, value);
3899
+ }
3900
+
3901
+ napi_value atomic_counter_new (napi_env env, napi_callback_info info)
3902
+ {
3903
+ (void) info;
3904
+ void *counter = zlink_atomic_counter_new ();
3905
+ if (!counter)
3906
+ return throw_last_error (env, "atomic_counter_new failed");
3907
+ napi_value out;
3908
+ napi_create_external (env, counter, NULL, NULL, &out);
3909
+ return out;
3910
+ }
3911
+
3912
+ napi_value atomic_counter_set (napi_env env, napi_callback_info info)
3913
+ {
3914
+ napi_value argv[2];
3915
+ size_t argc = 2;
3916
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3917
+ void *counter = NULL;
3918
+ int32_t value = 0;
3919
+ napi_get_value_external (env, argv[0], &counter);
3920
+ napi_get_value_int32 (env, argv[1], &value);
3921
+ zlink_atomic_counter_set (counter, value);
3922
+ napi_value ok;
3923
+ napi_get_undefined (env, &ok);
3924
+ return ok;
3925
+ }
3926
+
3927
+ napi_value atomic_counter_inc (napi_env env, napi_callback_info info)
3928
+ {
3929
+ napi_value argv[1];
3930
+ size_t argc = 1;
3931
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3932
+ void *counter = NULL;
3933
+ napi_get_value_external (env, argv[0], &counter);
3934
+ napi_value out;
3935
+ napi_create_int32 (env, zlink_atomic_counter_inc (counter), &out);
3936
+ return out;
3937
+ }
3938
+
3939
+ napi_value atomic_counter_dec (napi_env env, napi_callback_info info)
3940
+ {
3941
+ napi_value argv[1];
3942
+ size_t argc = 1;
3943
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3944
+ void *counter = NULL;
3945
+ napi_get_value_external (env, argv[0], &counter);
3946
+ napi_value out;
3947
+ napi_create_int32 (env, zlink_atomic_counter_dec (counter), &out);
3948
+ return out;
3949
+ }
3950
+
3951
+ napi_value atomic_counter_value (napi_env env, napi_callback_info info)
3952
+ {
3953
+ napi_value argv[1];
3954
+ size_t argc = 1;
3955
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3956
+ void *counter = NULL;
3957
+ napi_get_value_external (env, argv[0], &counter);
3958
+ napi_value out;
3959
+ napi_create_int32 (env, zlink_atomic_counter_value (counter), &out);
3960
+ return out;
3961
+ }
3962
+
3963
+ napi_value atomic_counter_destroy (napi_env env, napi_callback_info info)
3964
+ {
3965
+ napi_value argv[1];
3966
+ size_t argc = 1;
3967
+ napi_get_cb_info (env, info, &argc, argv, NULL, NULL);
3968
+ void *counter = NULL;
3969
+ napi_get_value_external (env, argv[0], &counter);
3970
+ zlink_atomic_counter_destroy (&counter);
3971
+ napi_value ok;
3972
+ napi_get_undefined (env, &ok);
3973
+ return ok;
3974
+ }