nng-ruby 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/nng/ffi.rb ADDED
@@ -0,0 +1,376 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+
5
+ module NNG
6
+ # FFI bindings for libnng
7
+ module FFI
8
+ extend ::FFI::Library
9
+
10
+ # Try to load libnng from various locations
11
+ @lib_paths = [
12
+ File.expand_path('../../ext/nng/libnng.so.1.8.0', __dir__),
13
+ '/usr/local/lib/libnng.so',
14
+ '/usr/lib/libnng.so',
15
+ '/usr/lib/x86_64-linux-gnu/libnng.so'
16
+ ]
17
+
18
+ @loaded_lib_path = nil
19
+ @lib_paths.each do |path|
20
+ if File.exist?(path)
21
+ begin
22
+ ffi_lib path
23
+ @loaded_lib_path = path
24
+ break
25
+ rescue LoadError
26
+ next
27
+ end
28
+ end
29
+ end
30
+
31
+ raise LoadError, "Could not find libnng.so in any of: #{@lib_paths.join(', ')}" unless @loaded_lib_path
32
+
33
+ def self.loaded_lib_path
34
+ @loaded_lib_path
35
+ end
36
+
37
+ # ============================================================================
38
+ # Constants
39
+ # ============================================================================
40
+
41
+ NNG_MAJOR_VERSION = 1
42
+ NNG_MINOR_VERSION = 8
43
+ NNG_PATCH_VERSION = 0
44
+
45
+ # Maximum address length
46
+ NNG_MAXADDRLEN = 128
47
+
48
+ # Error codes
49
+ NNG_OK = 0
50
+ NNG_EINTR = 1
51
+ NNG_ENOMEM = 2
52
+ NNG_EINVAL = 3
53
+ NNG_EBUSY = 4
54
+ NNG_ETIMEDOUT = 5
55
+ NNG_ECONNREFUSED = 6
56
+ NNG_ECLOSED = 7
57
+ NNG_EAGAIN = 8
58
+ NNG_ENOTSUP = 9
59
+ NNG_EADDRINUSE = 10
60
+ NNG_ESTATE = 11
61
+ NNG_ENOENT = 12
62
+ NNG_EPROTO = 13
63
+ NNG_EUNREACHABLE = 14
64
+ NNG_EADDRINVAL = 15
65
+ NNG_EPERM = 16
66
+ NNG_EMSGSIZE = 17
67
+ NNG_ECONNABORTED = 18
68
+ NNG_ECONNRESET = 19
69
+ NNG_ECANCELED = 20
70
+ NNG_ENOFILES = 21
71
+ NNG_ENOSPC = 22
72
+ NNG_EEXIST = 23
73
+ NNG_EREADONLY = 24
74
+ NNG_EWRITEONLY = 25
75
+ NNG_ECRYPTO = 26
76
+ NNG_EPEERAUTH = 27
77
+ NNG_ENOARG = 28
78
+ NNG_EAMBIGUOUS = 29
79
+ NNG_EBADTYPE = 30
80
+ NNG_ECONNSHUT = 31
81
+ NNG_EINTERNAL = 1000
82
+
83
+ # Flags
84
+ NNG_FLAG_ALLOC = 1 # Allocate receive buffer
85
+ NNG_FLAG_NONBLOCK = 2 # Non-blocking mode
86
+
87
+ # Socket address families
88
+ NNG_AF_UNSPEC = 0
89
+ NNG_AF_INPROC = 1
90
+ NNG_AF_IPC = 2
91
+ NNG_AF_INET = 3
92
+ NNG_AF_INET6 = 4
93
+ NNG_AF_ZT = 5
94
+ NNG_AF_ABSTRACT = 6
95
+
96
+ # Pipe events
97
+ NNG_PIPE_EV_ADD_PRE = 0
98
+ NNG_PIPE_EV_ADD_POST = 1
99
+ NNG_PIPE_EV_REM_POST = 2
100
+ NNG_PIPE_EV_NUM = 3
101
+
102
+ # ============================================================================
103
+ # Type definitions
104
+ # ============================================================================
105
+
106
+ # nng_socket structure
107
+ class NngSocket < ::FFI::Struct
108
+ layout :id, :uint32
109
+ end
110
+
111
+ # nng_dialer structure
112
+ class NngDialer < ::FFI::Struct
113
+ layout :id, :uint32
114
+ end
115
+
116
+ # nng_listener structure
117
+ class NngListener < ::FFI::Struct
118
+ layout :id, :uint32
119
+ end
120
+
121
+ # nng_ctx structure
122
+ class NngCtx < ::FFI::Struct
123
+ layout :id, :uint32
124
+ end
125
+
126
+ # nng_pipe structure
127
+ class NngPipe < ::FFI::Struct
128
+ layout :id, :uint32
129
+ end
130
+
131
+ # nng_duration - time interval in milliseconds
132
+ typedef :int32, :nng_duration
133
+
134
+ # nng_time - absolute time in milliseconds
135
+ typedef :uint64, :nng_time
136
+
137
+ # Opaque types
138
+ typedef :pointer, :nng_msg
139
+ typedef :pointer, :nng_aio
140
+ typedef :pointer, :nng_stat
141
+
142
+ # Socket address structures
143
+ class NngSockaddrInproc < ::FFI::Struct
144
+ layout :sa_family, :uint16,
145
+ :sa_name, [:char, NNG_MAXADDRLEN]
146
+ end
147
+
148
+ class NngSockaddrPath < ::FFI::Struct
149
+ layout :sa_family, :uint16,
150
+ :sa_path, [:char, NNG_MAXADDRLEN]
151
+ end
152
+
153
+ class NngSockaddrIn < ::FFI::Struct
154
+ layout :sa_family, :uint16,
155
+ :sa_port, :uint16,
156
+ :sa_addr, :uint32
157
+ end
158
+
159
+ class NngSockaddrIn6 < ::FFI::Struct
160
+ layout :sa_family, :uint16,
161
+ :sa_port, :uint16,
162
+ :sa_addr, [:uint8, 16],
163
+ :sa_scope, :uint32
164
+ end
165
+
166
+ class NngSockaddrStorage < ::FFI::Struct
167
+ layout :sa_family, :uint16,
168
+ :sa_pad, [:uint64, 16]
169
+ end
170
+
171
+ class NngSockaddr < ::FFI::Union
172
+ layout :s_family, :uint16,
173
+ :s_inproc, NngSockaddrInproc,
174
+ :s_ipc, NngSockaddrPath,
175
+ :s_in, NngSockaddrIn,
176
+ :s_in6, NngSockaddrIn6,
177
+ :s_storage, NngSockaddrStorage
178
+ end
179
+
180
+ # ============================================================================
181
+ # Core functions
182
+ # ============================================================================
183
+
184
+ # Library finalization
185
+ attach_function :nng_fini, [], :void
186
+
187
+ # Error handling
188
+ attach_function :nng_strerror, [:int], :string
189
+
190
+ # Socket functions
191
+ attach_function :nng_close, [NngSocket.by_value], :int
192
+ attach_function :nng_socket_id, [NngSocket.by_value], :int
193
+
194
+ # Socket options - set
195
+ attach_function :nng_setopt, [NngSocket.by_value, :string, :pointer, :size_t], :int
196
+ attach_function :nng_setopt_bool, [NngSocket.by_value, :string, :bool], :int
197
+ attach_function :nng_setopt_int, [NngSocket.by_value, :string, :int], :int
198
+ attach_function :nng_setopt_size, [NngSocket.by_value, :string, :size_t], :int
199
+ attach_function :nng_setopt_uint64, [NngSocket.by_value, :string, :uint64], :int
200
+ attach_function :nng_setopt_string, [NngSocket.by_value, :string, :string], :int
201
+ attach_function :nng_setopt_ptr, [NngSocket.by_value, :string, :pointer], :int
202
+ attach_function :nng_setopt_ms, [NngSocket.by_value, :string, :nng_duration], :int
203
+
204
+ # Socket options - get
205
+ attach_function :nng_getopt, [NngSocket.by_value, :string, :pointer, :pointer], :int
206
+ attach_function :nng_getopt_bool, [NngSocket.by_value, :string, :pointer], :int
207
+ attach_function :nng_getopt_int, [NngSocket.by_value, :string, :pointer], :int
208
+ attach_function :nng_getopt_size, [NngSocket.by_value, :string, :pointer], :int
209
+ attach_function :nng_getopt_uint64, [NngSocket.by_value, :string, :pointer], :int
210
+ attach_function :nng_getopt_string, [NngSocket.by_value, :string, :pointer], :int
211
+ attach_function :nng_getopt_ptr, [NngSocket.by_value, :string, :pointer], :int
212
+ attach_function :nng_getopt_ms, [NngSocket.by_value, :string, :pointer], :int
213
+
214
+ # Connection management
215
+ attach_function :nng_listen, [NngSocket.by_value, :string, :pointer, :int], :int
216
+ attach_function :nng_dial, [NngSocket.by_value, :string, :pointer, :int], :int
217
+
218
+ # Dialer functions
219
+ attach_function :nng_dialer_create, [:pointer, NngSocket.by_value, :string], :int
220
+ attach_function :nng_dialer_start, [NngDialer.by_value, :int], :int
221
+ attach_function :nng_dialer_close, [NngDialer.by_value], :int
222
+ attach_function :nng_dialer_id, [NngDialer.by_value], :int
223
+
224
+ # Listener functions
225
+ attach_function :nng_listener_create, [:pointer, NngSocket.by_value, :string], :int
226
+ attach_function :nng_listener_start, [NngListener.by_value, :int], :int
227
+ attach_function :nng_listener_close, [NngListener.by_value], :int
228
+ attach_function :nng_listener_id, [NngListener.by_value], :int
229
+
230
+ # Send and receive (synchronous)
231
+ attach_function :nng_send, [NngSocket.by_value, :pointer, :size_t, :int], :int
232
+ attach_function :nng_recv, [NngSocket.by_value, :pointer, :pointer, :int], :int
233
+
234
+ # Message-based send/receive
235
+ attach_function :nng_sendmsg, [NngSocket.by_value, :nng_msg, :int], :int
236
+ attach_function :nng_recvmsg, [NngSocket.by_value, :pointer, :int], :int
237
+
238
+ # Memory management
239
+ attach_function :nng_free, [:pointer, :size_t], :void
240
+ attach_function :nng_strfree, [:pointer], :void
241
+
242
+ # Message functions
243
+ attach_function :nng_msg_alloc, [:pointer, :size_t], :int
244
+ attach_function :nng_msg_free, [:nng_msg], :void
245
+ attach_function :nng_msg_realloc, [:nng_msg, :size_t], :int
246
+ attach_function :nng_msg_header, [:nng_msg], :pointer
247
+ attach_function :nng_msg_header_len, [:nng_msg], :size_t
248
+ attach_function :nng_msg_body, [:nng_msg], :pointer
249
+ attach_function :nng_msg_len, [:nng_msg], :size_t
250
+ attach_function :nng_msg_append, [:nng_msg, :pointer, :size_t], :int
251
+ attach_function :nng_msg_insert, [:nng_msg, :pointer, :size_t], :int
252
+ attach_function :nng_msg_trim, [:nng_msg, :size_t], :int
253
+ attach_function :nng_msg_chop, [:nng_msg, :size_t], :int
254
+ attach_function :nng_msg_header_append, [:nng_msg, :pointer, :size_t], :int
255
+ attach_function :nng_msg_header_insert, [:nng_msg, :pointer, :size_t], :int
256
+ attach_function :nng_msg_header_trim, [:nng_msg, :size_t], :int
257
+ attach_function :nng_msg_header_chop, [:nng_msg, :size_t], :int
258
+ attach_function :nng_msg_clear, [:nng_msg], :void
259
+ attach_function :nng_msg_header_clear, [:nng_msg], :void
260
+ attach_function :nng_msg_dup, [:pointer, :nng_msg], :int
261
+ attach_function :nng_msg_get_pipe, [:nng_msg], NngPipe.by_value
262
+ attach_function :nng_msg_set_pipe, [:nng_msg, NngPipe.by_value], :void
263
+
264
+ # Context functions
265
+ attach_function :nng_ctx_open, [:pointer, NngSocket.by_value], :int
266
+ attach_function :nng_ctx_close, [NngCtx.by_value], :int
267
+ attach_function :nng_ctx_id, [NngCtx.by_value], :int
268
+ attach_function :nng_ctx_send, [NngCtx.by_value, :nng_aio], :void
269
+ attach_function :nng_ctx_recv, [NngCtx.by_value, :nng_aio], :void
270
+ attach_function :nng_ctx_sendmsg, [NngCtx.by_value, :nng_msg, :int], :int
271
+ attach_function :nng_ctx_recvmsg, [NngCtx.by_value, :pointer, :int], :int
272
+
273
+ # Pipe functions
274
+ attach_function :nng_pipe_id, [NngPipe.by_value], :int
275
+ attach_function :nng_pipe_socket, [NngPipe.by_value], NngSocket.by_value
276
+ attach_function :nng_pipe_dialer, [NngPipe.by_value], NngDialer.by_value
277
+ attach_function :nng_pipe_listener, [NngPipe.by_value], NngListener.by_value
278
+ attach_function :nng_pipe_close, [NngPipe.by_value], :int
279
+
280
+ # Asynchronous I/O functions
281
+ attach_function :nng_aio_alloc, [:pointer, :pointer, :pointer], :int
282
+ attach_function :nng_aio_free, [:nng_aio], :void
283
+ attach_function :nng_aio_stop, [:nng_aio], :void
284
+ attach_function :nng_aio_result, [:nng_aio], :int
285
+ attach_function :nng_aio_count, [:nng_aio], :size_t
286
+ attach_function :nng_aio_cancel, [:nng_aio], :void
287
+ attach_function :nng_aio_abort, [:nng_aio, :int], :void
288
+ attach_function :nng_aio_wait, [:nng_aio], :void
289
+ attach_function :nng_aio_set_msg, [:nng_aio, :nng_msg], :void
290
+ attach_function :nng_aio_get_msg, [:nng_aio], :nng_msg
291
+ attach_function :nng_aio_set_input, [:nng_aio, :uint, :pointer], :int
292
+ attach_function :nng_aio_set_output, [:nng_aio, :uint, :pointer], :int
293
+ attach_function :nng_aio_set_timeout, [:nng_aio, :nng_duration], :void
294
+ attach_function :nng_aio_set_iov, [:nng_aio, :uint, :pointer], :int
295
+ attach_function :nng_aio_begin, [:nng_aio], :bool
296
+ attach_function :nng_aio_finish, [:nng_aio, :int], :void
297
+ attach_function :nng_aio_defer, [:nng_aio, :pointer, :pointer], :void
298
+
299
+ # Asynchronous send/receive
300
+ attach_function :nng_send_aio, [NngSocket.by_value, :nng_aio], :void
301
+ attach_function :nng_recv_aio, [NngSocket.by_value, :nng_aio], :void
302
+
303
+ # Statistics functions
304
+ attach_function :nng_stats_get, [:pointer], :int
305
+ attach_function :nng_stats_free, [:nng_stat], :void
306
+ attach_function :nng_stats_dump, [:nng_stat], :void
307
+ attach_function :nng_stat_next, [:nng_stat], :nng_stat
308
+ attach_function :nng_stat_child, [:nng_stat], :nng_stat
309
+ attach_function :nng_stat_name, [:nng_stat], :string
310
+ attach_function :nng_stat_type, [:nng_stat], :int
311
+ attach_function :nng_stat_unit, [:nng_stat], :int
312
+ attach_function :nng_stat_value, [:nng_stat], :uint64
313
+ attach_function :nng_stat_desc, [:nng_stat], :string
314
+
315
+ # Device functions (forwarder/reflector)
316
+ attach_function :nng_device, [NngSocket.by_value, NngSocket.by_value], :int
317
+
318
+ # Utility functions
319
+ attach_function :nng_sleep_aio, [:nng_duration, :nng_aio], :void
320
+ attach_function :nng_msleep, [:nng_duration], :void
321
+
322
+ # URL parsing
323
+ typedef :pointer, :nng_url
324
+ attach_function :nng_url_parse, [:pointer, :string], :int
325
+ attach_function :nng_url_free, [:nng_url], :void
326
+ attach_function :nng_url_clone, [:pointer, :nng_url], :int
327
+
328
+ # ============================================================================
329
+ # Protocol-specific functions (will be attached when protocol is loaded)
330
+ # ============================================================================
331
+
332
+ # These will be attached by specific protocol modules:
333
+ # - nng_pair0_open, nng_pair1_open
334
+ # - nng_push0_open, nng_pull0_open
335
+ # - nng_pub0_open, nng_sub0_open
336
+ # - nng_req0_open, nng_rep0_open
337
+ # - nng_surveyor0_open, nng_respondent0_open
338
+ # - nng_bus0_open
339
+
340
+ # ============================================================================
341
+ # Helper methods
342
+ # ============================================================================
343
+
344
+ # Check error code and raise exception if not OK
345
+ def self.check_error(ret, operation = "NNG operation")
346
+ return if ret == NNG_OK
347
+ error_msg = nng_strerror(ret)
348
+ raise NNG::Error, "#{operation} failed: #{error_msg} (code: #{ret})"
349
+ end
350
+
351
+ # Create initialized socket
352
+ def self.socket_initializer
353
+ NngSocket.new.tap { |s| s[:id] = 0 }
354
+ end
355
+
356
+ # Create initialized dialer
357
+ def self.dialer_initializer
358
+ NngDialer.new.tap { |d| d[:id] = 0 }
359
+ end
360
+
361
+ # Create initialized listener
362
+ def self.listener_initializer
363
+ NngListener.new.tap { |l| l[:id] = 0 }
364
+ end
365
+
366
+ # Create initialized context
367
+ def self.ctx_initializer
368
+ NngCtx.new.tap { |c| c[:id] = 0 }
369
+ end
370
+
371
+ # Create initialized pipe
372
+ def self.pipe_initializer
373
+ NngPipe.new.tap { |p| p[:id] = 0 }
374
+ end
375
+ end
376
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ # NNG message wrapper
5
+ class Message
6
+ attr_reader :msg_ptr
7
+
8
+ # Create a new message
9
+ # @param size [Integer] initial size
10
+ def initialize(size: 0)
11
+ @msg_ptr = ::FFI::MemoryPointer.new(:pointer)
12
+ ret = FFI.nng_msg_alloc(@msg_ptr, size)
13
+ FFI.check_error(ret, "Allocate message")
14
+ @msg = @msg_ptr.read_pointer
15
+ @freed = false
16
+ end
17
+
18
+ # Append data to message body
19
+ # @param data [String] data to append
20
+ # @return [self]
21
+ def append(data)
22
+ check_freed
23
+ data_str = data.to_s
24
+ data_ptr = ::FFI::MemoryPointer.new(:uint8, data_str.bytesize)
25
+ data_ptr.put_bytes(0, data_str)
26
+
27
+ ret = FFI.nng_msg_append(@msg, data_ptr, data_str.bytesize)
28
+ FFI.check_error(ret, "Append to message")
29
+ self
30
+ end
31
+
32
+ # Insert data at the beginning of message body
33
+ # @param data [String] data to insert
34
+ # @return [self]
35
+ def insert(data)
36
+ check_freed
37
+ data_str = data.to_s
38
+ data_ptr = ::FFI::MemoryPointer.new(:uint8, data_str.bytesize)
39
+ data_ptr.put_bytes(0, data_str)
40
+
41
+ ret = FFI.nng_msg_insert(@msg, data_ptr, data_str.bytesize)
42
+ FFI.check_error(ret, "Insert to message")
43
+ self
44
+ end
45
+
46
+ # Get message body
47
+ # @return [String] message body
48
+ def body
49
+ check_freed
50
+ body_ptr = FFI.nng_msg_body(@msg)
51
+ length = FFI.nng_msg_len(@msg)
52
+ body_ptr.read_bytes(length)
53
+ end
54
+
55
+ # Get message body length
56
+ # @return [Integer] length in bytes
57
+ def length
58
+ check_freed
59
+ FFI.nng_msg_len(@msg)
60
+ end
61
+ alias size length
62
+
63
+ # Get message header
64
+ # @return [String] message header
65
+ def header
66
+ check_freed
67
+ header_ptr = FFI.nng_msg_header(@msg)
68
+ length = FFI.nng_msg_header_len(@msg)
69
+ header_ptr.read_bytes(length)
70
+ end
71
+
72
+ # Get message header length
73
+ # @return [Integer] length in bytes
74
+ def header_length
75
+ check_freed
76
+ FFI.nng_msg_header_len(@msg)
77
+ end
78
+
79
+ # Append data to message header
80
+ # @param data [String] data to append
81
+ # @return [self]
82
+ def header_append(data)
83
+ check_freed
84
+ data_str = data.to_s
85
+ data_ptr = ::FFI::MemoryPointer.new(:uint8, data_str.bytesize)
86
+ data_ptr.put_bytes(0, data_str)
87
+
88
+ ret = FFI.nng_msg_header_append(@msg, data_ptr, data_str.bytesize)
89
+ FFI.check_error(ret, "Append to message header")
90
+ self
91
+ end
92
+
93
+ # Clear message body
94
+ # @return [self]
95
+ def clear
96
+ check_freed
97
+ FFI.nng_msg_clear(@msg)
98
+ self
99
+ end
100
+
101
+ # Clear message header
102
+ # @return [self]
103
+ def header_clear
104
+ check_freed
105
+ FFI.nng_msg_header_clear(@msg)
106
+ self
107
+ end
108
+
109
+ # Duplicate message
110
+ # @return [Message] duplicated message
111
+ def dup
112
+ check_freed
113
+ dup_ptr = ::FFI::MemoryPointer.new(:pointer)
114
+ ret = FFI.nng_msg_dup(dup_ptr, @msg)
115
+ FFI.check_error(ret, "Duplicate message")
116
+
117
+ new_msg = allocate
118
+ new_msg.instance_variable_set(:@msg, dup_ptr.read_pointer)
119
+ new_msg.instance_variable_set(:@msg_ptr, dup_ptr)
120
+ new_msg.instance_variable_set(:@freed, false)
121
+ new_msg
122
+ end
123
+
124
+ # Free the message
125
+ # @return [nil]
126
+ def free
127
+ return if @freed
128
+ FFI.nng_msg_free(@msg)
129
+ @freed = true
130
+ nil
131
+ end
132
+
133
+ # Check if message is freed
134
+ # @return [Boolean]
135
+ def freed?
136
+ @freed
137
+ end
138
+
139
+ # Get the internal message pointer (for use with send/recv)
140
+ # @return [FFI::Pointer]
141
+ def to_ptr
142
+ @msg
143
+ end
144
+
145
+ private
146
+
147
+ def check_freed
148
+ raise StateError, "Message has been freed" if @freed
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NNG
4
+ # Protocol-specific socket opening functions
5
+ module Protocols
6
+ # Dynamically load protocol functions from libnng
7
+ def self.attach_protocol_functions
8
+ extend ::FFI::Library
9
+
10
+ # Use the same library as FFI module
11
+ ffi_lib NNG::FFI.loaded_lib_path
12
+
13
+ # Pair protocols
14
+ attach_function :nng_pair0_open, [FFI::NngSocket.by_ref], :int
15
+ attach_function :nng_pair0_open_raw, [FFI::NngSocket.by_ref], :int
16
+ attach_function :nng_pair1_open, [FFI::NngSocket.by_ref], :int
17
+ attach_function :nng_pair1_open_raw, [FFI::NngSocket.by_ref], :int
18
+ attach_function :nng_pair1_open_poly, [FFI::NngSocket.by_ref], :int
19
+
20
+ # Push/Pull protocols
21
+ attach_function :nng_push0_open, [FFI::NngSocket.by_ref], :int
22
+ attach_function :nng_push0_open_raw, [FFI::NngSocket.by_ref], :int
23
+ attach_function :nng_pull0_open, [FFI::NngSocket.by_ref], :int
24
+ attach_function :nng_pull0_open_raw, [FFI::NngSocket.by_ref], :int
25
+
26
+ # Pub/Sub protocols
27
+ attach_function :nng_pub0_open, [FFI::NngSocket.by_ref], :int
28
+ attach_function :nng_pub0_open_raw, [FFI::NngSocket.by_ref], :int
29
+ attach_function :nng_sub0_open, [FFI::NngSocket.by_ref], :int
30
+ attach_function :nng_sub0_open_raw, [FFI::NngSocket.by_ref], :int
31
+
32
+ # Req/Rep protocols
33
+ attach_function :nng_req0_open, [FFI::NngSocket.by_ref], :int
34
+ attach_function :nng_req0_open_raw, [FFI::NngSocket.by_ref], :int
35
+ attach_function :nng_rep0_open, [FFI::NngSocket.by_ref], :int
36
+ attach_function :nng_rep0_open_raw, [FFI::NngSocket.by_ref], :int
37
+
38
+ # Surveyor/Respondent protocols
39
+ attach_function :nng_surveyor0_open, [FFI::NngSocket.by_ref], :int
40
+ attach_function :nng_surveyor0_open_raw, [FFI::NngSocket.by_ref], :int
41
+ attach_function :nng_respondent0_open, [FFI::NngSocket.by_ref], :int
42
+ attach_function :nng_respondent0_open_raw, [FFI::NngSocket.by_ref], :int
43
+
44
+ # Bus protocol
45
+ attach_function :nng_bus0_open, [FFI::NngSocket.by_ref], :int
46
+ attach_function :nng_bus0_open_raw, [FFI::NngSocket.by_ref], :int
47
+ rescue ::FFI::NotFoundError => e
48
+ # Some protocols might not be available in all builds
49
+ warn "Warning: Some NNG protocol functions not available: #{e.message}"
50
+ end
51
+
52
+ # Protocol name to function mapping
53
+ PROTOCOL_FUNCTIONS = {
54
+ pair0: :nng_pair0_open,
55
+ pair1: :nng_pair1_open,
56
+ pair: :nng_pair1_open, # alias for pair1
57
+ push0: :nng_push0_open,
58
+ push: :nng_push0_open,
59
+ pull0: :nng_pull0_open,
60
+ pull: :nng_pull0_open,
61
+ pub0: :nng_pub0_open,
62
+ pub: :nng_pub0_open,
63
+ sub0: :nng_sub0_open,
64
+ sub: :nng_sub0_open,
65
+ req0: :nng_req0_open,
66
+ req: :nng_req0_open,
67
+ rep0: :nng_rep0_open,
68
+ rep: :nng_rep0_open,
69
+ surveyor0: :nng_surveyor0_open,
70
+ surveyor: :nng_surveyor0_open,
71
+ respondent0: :nng_respondent0_open,
72
+ respondent: :nng_respondent0_open,
73
+ bus0: :nng_bus0_open,
74
+ bus: :nng_bus0_open
75
+ }.freeze
76
+
77
+ # Open a socket for the given protocol
78
+ # @param protocol [Symbol] protocol name (:pair0, :pair1, :push, :pull, etc.)
79
+ # @param raw [Boolean] open in raw mode
80
+ # @return [FFI::NngSocket] opened socket
81
+ def self.open_socket(protocol, raw: false)
82
+ func_name = PROTOCOL_FUNCTIONS[protocol]
83
+ raise ArgumentError, "Unknown protocol: #{protocol}" unless func_name
84
+
85
+ func_name = "#{func_name}_raw".to_sym if raw
86
+
87
+ socket = FFI.socket_initializer
88
+ ret = send(func_name, socket)
89
+ FFI.check_error(ret, "Opening #{protocol} socket")
90
+ socket
91
+ end
92
+ end
93
+ end
94
+
95
+ # Auto-load protocol functions
96
+ NNG::Protocols.attach_protocol_functions