nng-ruby 0.1.2 → 1.0.1

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 CHANGED
@@ -1,445 +1,508 @@
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
- # Load install-time configuration if available
11
- @install_config = {}
12
- config_file = File.expand_path('../../ext/nng/nng_config.rb', __dir__)
13
- if File.exist?(config_file)
14
- require config_file
15
- @install_config = NNG::InstallConfig::CONFIG rescue {}
16
- end
17
-
18
- # Build library search paths with priority order:
19
- # 1. ENV['NNG_LIB_PATH'] - Direct path to library file
20
- # 2. ENV['NNG_LIB_DIR'] - Directory containing libnng.so*
21
- # 3. Install config (from gem install --with-nng-*)
22
- # 4. Bundled library
23
- # 5. System default paths
24
- def self.build_lib_paths
25
- paths = []
26
-
27
- # Priority 1: Direct library path from environment
28
- if ENV['NNG_LIB_PATH'] && !ENV['NNG_LIB_PATH'].empty?
29
- paths << ENV['NNG_LIB_PATH']
30
- puts "NNG: Using library from NNG_LIB_PATH: #{ENV['NNG_LIB_PATH']}" if ENV['NNG_DEBUG']
31
- end
32
-
33
- # Priority 2: Library directory from environment
34
- if ENV['NNG_LIB_DIR'] && !ENV['NNG_LIB_DIR'].empty?
35
- dir = ENV['NNG_LIB_DIR']
36
- # Search for libnng.so* in the directory
37
- Dir.glob(File.join(dir, 'libnng.so*')).each { |lib| paths << lib }
38
- puts "NNG: Searching in NNG_LIB_DIR: #{dir}" if ENV['NNG_DEBUG']
39
- end
40
-
41
- # Priority 3: Install-time configuration
42
- if @install_config[:nng_lib]
43
- paths << @install_config[:nng_lib]
44
- puts "NNG: Using library from install config: #{@install_config[:nng_lib]}" if ENV['NNG_DEBUG']
45
- elsif @install_config[:nng_dir]
46
- nng_dir = @install_config[:nng_dir]
47
- # Try common subdirectories
48
- %w[lib lib64 lib/x86_64-linux-gnu].each do |subdir|
49
- lib_dir = File.join(nng_dir, subdir)
50
- Dir.glob(File.join(lib_dir, 'libnng.so*')).each { |lib| paths << lib }
51
- end
52
- puts "NNG: Searching in install config dir: #{nng_dir}" if ENV['NNG_DEBUG']
53
- end
54
-
55
- # Priority 4: Bundled library
56
- bundled_lib = File.expand_path('../../ext/nng/libnng.so.1.8.0', __dir__)
57
- paths << bundled_lib
58
-
59
- # Priority 5: System default paths
60
- paths += [
61
- '/usr/local/lib/libnng.so',
62
- '/usr/lib/libnng.so',
63
- '/usr/lib/x86_64-linux-gnu/libnng.so',
64
- '/usr/lib/aarch64-linux-gnu/libnng.so'
65
- ]
66
-
67
- paths.uniq
68
- end
69
-
70
- @lib_paths = build_lib_paths
71
- @loaded_lib_path = nil
72
-
73
- @lib_paths.each do |path|
74
- if File.exist?(path)
75
- begin
76
- ffi_lib path
77
- @loaded_lib_path = path
78
- puts "NNG: Successfully loaded library from: #{path}" if ENV['NNG_DEBUG']
79
- break
80
- rescue LoadError => e
81
- puts "NNG: Failed to load #{path}: #{e.message}" if ENV['NNG_DEBUG']
82
- next
83
- end
84
- end
85
- end
86
-
87
- unless @loaded_lib_path
88
- error_msg = "Could not find libnng.so in any of the following locations:\n"
89
- error_msg += @lib_paths.map { |p| " - #{p}" }.join("\n")
90
- error_msg += "\n\nYou can specify a custom path using:\n"
91
- error_msg += " - Environment variable: export NNG_LIB_PATH=/path/to/libnng.so\n"
92
- error_msg += " - Environment variable: export NNG_LIB_DIR=/path/to/lib\n"
93
- error_msg += " - Gem install option: gem install nng-ruby -- --with-nng-dir=/path/to/nng\n"
94
- error_msg += " - Gem install option: gem install nng-ruby -- --with-nng-lib=/path/to/libnng.so"
95
- raise LoadError, error_msg
96
- end
97
-
98
- def self.loaded_lib_path
99
- @loaded_lib_path
100
- end
101
-
102
- def self.install_config
103
- @install_config
104
- end
105
-
106
- # ============================================================================
107
- # Constants
108
- # ============================================================================
109
-
110
- NNG_MAJOR_VERSION = 1
111
- NNG_MINOR_VERSION = 8
112
- NNG_PATCH_VERSION = 0
113
-
114
- # Maximum address length
115
- NNG_MAXADDRLEN = 128
116
-
117
- # Error codes
118
- NNG_OK = 0
119
- NNG_EINTR = 1
120
- NNG_ENOMEM = 2
121
- NNG_EINVAL = 3
122
- NNG_EBUSY = 4
123
- NNG_ETIMEDOUT = 5
124
- NNG_ECONNREFUSED = 6
125
- NNG_ECLOSED = 7
126
- NNG_EAGAIN = 8
127
- NNG_ENOTSUP = 9
128
- NNG_EADDRINUSE = 10
129
- NNG_ESTATE = 11
130
- NNG_ENOENT = 12
131
- NNG_EPROTO = 13
132
- NNG_EUNREACHABLE = 14
133
- NNG_EADDRINVAL = 15
134
- NNG_EPERM = 16
135
- NNG_EMSGSIZE = 17
136
- NNG_ECONNABORTED = 18
137
- NNG_ECONNRESET = 19
138
- NNG_ECANCELED = 20
139
- NNG_ENOFILES = 21
140
- NNG_ENOSPC = 22
141
- NNG_EEXIST = 23
142
- NNG_EREADONLY = 24
143
- NNG_EWRITEONLY = 25
144
- NNG_ECRYPTO = 26
145
- NNG_EPEERAUTH = 27
146
- NNG_ENOARG = 28
147
- NNG_EAMBIGUOUS = 29
148
- NNG_EBADTYPE = 30
149
- NNG_ECONNSHUT = 31
150
- NNG_EINTERNAL = 1000
151
-
152
- # Flags
153
- NNG_FLAG_ALLOC = 1 # Allocate receive buffer
154
- NNG_FLAG_NONBLOCK = 2 # Non-blocking mode
155
-
156
- # Socket address families
157
- NNG_AF_UNSPEC = 0
158
- NNG_AF_INPROC = 1
159
- NNG_AF_IPC = 2
160
- NNG_AF_INET = 3
161
- NNG_AF_INET6 = 4
162
- NNG_AF_ZT = 5
163
- NNG_AF_ABSTRACT = 6
164
-
165
- # Pipe events
166
- NNG_PIPE_EV_ADD_PRE = 0
167
- NNG_PIPE_EV_ADD_POST = 1
168
- NNG_PIPE_EV_REM_POST = 2
169
- NNG_PIPE_EV_NUM = 3
170
-
171
- # ============================================================================
172
- # Type definitions
173
- # ============================================================================
174
-
175
- # nng_socket structure
176
- class NngSocket < ::FFI::Struct
177
- layout :id, :uint32
178
- end
179
-
180
- # nng_dialer structure
181
- class NngDialer < ::FFI::Struct
182
- layout :id, :uint32
183
- end
184
-
185
- # nng_listener structure
186
- class NngListener < ::FFI::Struct
187
- layout :id, :uint32
188
- end
189
-
190
- # nng_ctx structure
191
- class NngCtx < ::FFI::Struct
192
- layout :id, :uint32
193
- end
194
-
195
- # nng_pipe structure
196
- class NngPipe < ::FFI::Struct
197
- layout :id, :uint32
198
- end
199
-
200
- # nng_duration - time interval in milliseconds
201
- typedef :int32, :nng_duration
202
-
203
- # nng_time - absolute time in milliseconds
204
- typedef :uint64, :nng_time
205
-
206
- # Opaque types
207
- typedef :pointer, :nng_msg
208
- typedef :pointer, :nng_aio
209
- typedef :pointer, :nng_stat
210
-
211
- # Socket address structures
212
- class NngSockaddrInproc < ::FFI::Struct
213
- layout :sa_family, :uint16,
214
- :sa_name, [:char, NNG_MAXADDRLEN]
215
- end
216
-
217
- class NngSockaddrPath < ::FFI::Struct
218
- layout :sa_family, :uint16,
219
- :sa_path, [:char, NNG_MAXADDRLEN]
220
- end
221
-
222
- class NngSockaddrIn < ::FFI::Struct
223
- layout :sa_family, :uint16,
224
- :sa_port, :uint16,
225
- :sa_addr, :uint32
226
- end
227
-
228
- class NngSockaddrIn6 < ::FFI::Struct
229
- layout :sa_family, :uint16,
230
- :sa_port, :uint16,
231
- :sa_addr, [:uint8, 16],
232
- :sa_scope, :uint32
233
- end
234
-
235
- class NngSockaddrStorage < ::FFI::Struct
236
- layout :sa_family, :uint16,
237
- :sa_pad, [:uint64, 16]
238
- end
239
-
240
- class NngSockaddr < ::FFI::Union
241
- layout :s_family, :uint16,
242
- :s_inproc, NngSockaddrInproc,
243
- :s_ipc, NngSockaddrPath,
244
- :s_in, NngSockaddrIn,
245
- :s_in6, NngSockaddrIn6,
246
- :s_storage, NngSockaddrStorage
247
- end
248
-
249
- # ============================================================================
250
- # Core functions
251
- # ============================================================================
252
-
253
- # Library finalization
254
- attach_function :nng_fini, [], :void
255
-
256
- # Error handling
257
- attach_function :nng_strerror, [:int], :string
258
-
259
- # Socket functions
260
- attach_function :nng_close, [NngSocket.by_value], :int
261
- attach_function :nng_socket_id, [NngSocket.by_value], :int
262
-
263
- # Socket options - set
264
- attach_function :nng_setopt, [NngSocket.by_value, :string, :pointer, :size_t], :int
265
- attach_function :nng_setopt_bool, [NngSocket.by_value, :string, :bool], :int
266
- attach_function :nng_setopt_int, [NngSocket.by_value, :string, :int], :int
267
- attach_function :nng_setopt_size, [NngSocket.by_value, :string, :size_t], :int
268
- attach_function :nng_setopt_uint64, [NngSocket.by_value, :string, :uint64], :int
269
- attach_function :nng_setopt_string, [NngSocket.by_value, :string, :string], :int
270
- attach_function :nng_setopt_ptr, [NngSocket.by_value, :string, :pointer], :int
271
- attach_function :nng_setopt_ms, [NngSocket.by_value, :string, :nng_duration], :int
272
-
273
- # Socket options - get
274
- attach_function :nng_getopt, [NngSocket.by_value, :string, :pointer, :pointer], :int
275
- attach_function :nng_getopt_bool, [NngSocket.by_value, :string, :pointer], :int
276
- attach_function :nng_getopt_int, [NngSocket.by_value, :string, :pointer], :int
277
- attach_function :nng_getopt_size, [NngSocket.by_value, :string, :pointer], :int
278
- attach_function :nng_getopt_uint64, [NngSocket.by_value, :string, :pointer], :int
279
- attach_function :nng_getopt_string, [NngSocket.by_value, :string, :pointer], :int
280
- attach_function :nng_getopt_ptr, [NngSocket.by_value, :string, :pointer], :int
281
- attach_function :nng_getopt_ms, [NngSocket.by_value, :string, :pointer], :int
282
-
283
- # Connection management
284
- attach_function :nng_listen, [NngSocket.by_value, :string, :pointer, :int], :int
285
- attach_function :nng_dial, [NngSocket.by_value, :string, :pointer, :int], :int
286
-
287
- # Dialer functions
288
- attach_function :nng_dialer_create, [:pointer, NngSocket.by_value, :string], :int
289
- attach_function :nng_dialer_start, [NngDialer.by_value, :int], :int
290
- attach_function :nng_dialer_close, [NngDialer.by_value], :int
291
- attach_function :nng_dialer_id, [NngDialer.by_value], :int
292
-
293
- # Listener functions
294
- attach_function :nng_listener_create, [:pointer, NngSocket.by_value, :string], :int
295
- attach_function :nng_listener_start, [NngListener.by_value, :int], :int
296
- attach_function :nng_listener_close, [NngListener.by_value], :int
297
- attach_function :nng_listener_id, [NngListener.by_value], :int
298
-
299
- # Send and receive (synchronous)
300
- attach_function :nng_send, [NngSocket.by_value, :pointer, :size_t, :int], :int
301
- attach_function :nng_recv, [NngSocket.by_value, :pointer, :pointer, :int], :int
302
-
303
- # Message-based send/receive
304
- attach_function :nng_sendmsg, [NngSocket.by_value, :nng_msg, :int], :int
305
- attach_function :nng_recvmsg, [NngSocket.by_value, :pointer, :int], :int
306
-
307
- # Memory management
308
- attach_function :nng_free, [:pointer, :size_t], :void
309
- attach_function :nng_strfree, [:pointer], :void
310
-
311
- # Message functions
312
- attach_function :nng_msg_alloc, [:pointer, :size_t], :int
313
- attach_function :nng_msg_free, [:nng_msg], :void
314
- attach_function :nng_msg_realloc, [:nng_msg, :size_t], :int
315
- attach_function :nng_msg_header, [:nng_msg], :pointer
316
- attach_function :nng_msg_header_len, [:nng_msg], :size_t
317
- attach_function :nng_msg_body, [:nng_msg], :pointer
318
- attach_function :nng_msg_len, [:nng_msg], :size_t
319
- attach_function :nng_msg_append, [:nng_msg, :pointer, :size_t], :int
320
- attach_function :nng_msg_insert, [:nng_msg, :pointer, :size_t], :int
321
- attach_function :nng_msg_trim, [:nng_msg, :size_t], :int
322
- attach_function :nng_msg_chop, [:nng_msg, :size_t], :int
323
- attach_function :nng_msg_header_append, [:nng_msg, :pointer, :size_t], :int
324
- attach_function :nng_msg_header_insert, [:nng_msg, :pointer, :size_t], :int
325
- attach_function :nng_msg_header_trim, [:nng_msg, :size_t], :int
326
- attach_function :nng_msg_header_chop, [:nng_msg, :size_t], :int
327
- attach_function :nng_msg_clear, [:nng_msg], :void
328
- attach_function :nng_msg_header_clear, [:nng_msg], :void
329
- attach_function :nng_msg_dup, [:pointer, :nng_msg], :int
330
- attach_function :nng_msg_get_pipe, [:nng_msg], NngPipe.by_value
331
- attach_function :nng_msg_set_pipe, [:nng_msg, NngPipe.by_value], :void
332
-
333
- # Context functions
334
- attach_function :nng_ctx_open, [:pointer, NngSocket.by_value], :int
335
- attach_function :nng_ctx_close, [NngCtx.by_value], :int
336
- attach_function :nng_ctx_id, [NngCtx.by_value], :int
337
- attach_function :nng_ctx_send, [NngCtx.by_value, :nng_aio], :void
338
- attach_function :nng_ctx_recv, [NngCtx.by_value, :nng_aio], :void
339
- attach_function :nng_ctx_sendmsg, [NngCtx.by_value, :nng_msg, :int], :int
340
- attach_function :nng_ctx_recvmsg, [NngCtx.by_value, :pointer, :int], :int
341
-
342
- # Pipe functions
343
- attach_function :nng_pipe_id, [NngPipe.by_value], :int
344
- attach_function :nng_pipe_socket, [NngPipe.by_value], NngSocket.by_value
345
- attach_function :nng_pipe_dialer, [NngPipe.by_value], NngDialer.by_value
346
- attach_function :nng_pipe_listener, [NngPipe.by_value], NngListener.by_value
347
- attach_function :nng_pipe_close, [NngPipe.by_value], :int
348
-
349
- # Asynchronous I/O functions
350
- attach_function :nng_aio_alloc, [:pointer, :pointer, :pointer], :int
351
- attach_function :nng_aio_free, [:nng_aio], :void
352
- attach_function :nng_aio_stop, [:nng_aio], :void
353
- attach_function :nng_aio_result, [:nng_aio], :int
354
- attach_function :nng_aio_count, [:nng_aio], :size_t
355
- attach_function :nng_aio_cancel, [:nng_aio], :void
356
- attach_function :nng_aio_abort, [:nng_aio, :int], :void
357
- attach_function :nng_aio_wait, [:nng_aio], :void
358
- attach_function :nng_aio_set_msg, [:nng_aio, :nng_msg], :void
359
- attach_function :nng_aio_get_msg, [:nng_aio], :nng_msg
360
- attach_function :nng_aio_set_input, [:nng_aio, :uint, :pointer], :int
361
- attach_function :nng_aio_set_output, [:nng_aio, :uint, :pointer], :int
362
- attach_function :nng_aio_set_timeout, [:nng_aio, :nng_duration], :void
363
- attach_function :nng_aio_set_iov, [:nng_aio, :uint, :pointer], :int
364
- attach_function :nng_aio_begin, [:nng_aio], :bool
365
- attach_function :nng_aio_finish, [:nng_aio, :int], :void
366
- attach_function :nng_aio_defer, [:nng_aio, :pointer, :pointer], :void
367
-
368
- # Asynchronous send/receive
369
- attach_function :nng_send_aio, [NngSocket.by_value, :nng_aio], :void
370
- attach_function :nng_recv_aio, [NngSocket.by_value, :nng_aio], :void
371
-
372
- # Statistics functions
373
- attach_function :nng_stats_get, [:pointer], :int
374
- attach_function :nng_stats_free, [:nng_stat], :void
375
- attach_function :nng_stats_dump, [:nng_stat], :void
376
- attach_function :nng_stat_next, [:nng_stat], :nng_stat
377
- attach_function :nng_stat_child, [:nng_stat], :nng_stat
378
- attach_function :nng_stat_name, [:nng_stat], :string
379
- attach_function :nng_stat_type, [:nng_stat], :int
380
- attach_function :nng_stat_unit, [:nng_stat], :int
381
- attach_function :nng_stat_value, [:nng_stat], :uint64
382
- attach_function :nng_stat_desc, [:nng_stat], :string
383
-
384
- # Device functions (forwarder/reflector)
385
- attach_function :nng_device, [NngSocket.by_value, NngSocket.by_value], :int
386
-
387
- # Utility functions
388
- attach_function :nng_sleep_aio, [:nng_duration, :nng_aio], :void
389
- attach_function :nng_msleep, [:nng_duration], :void
390
-
391
- # URL parsing
392
- typedef :pointer, :nng_url
393
- attach_function :nng_url_parse, [:pointer, :string], :int
394
- attach_function :nng_url_free, [:nng_url], :void
395
- attach_function :nng_url_clone, [:pointer, :nng_url], :int
396
-
397
- # ============================================================================
398
- # Protocol-specific functions (will be attached when protocol is loaded)
399
- # ============================================================================
400
-
401
- # These will be attached by specific protocol modules:
402
- # - nng_pair0_open, nng_pair1_open
403
- # - nng_push0_open, nng_pull0_open
404
- # - nng_pub0_open, nng_sub0_open
405
- # - nng_req0_open, nng_rep0_open
406
- # - nng_surveyor0_open, nng_respondent0_open
407
- # - nng_bus0_open
408
-
409
- # ============================================================================
410
- # Helper methods
411
- # ============================================================================
412
-
413
- # Check error code and raise exception if not OK
414
- def self.check_error(ret, operation = "NNG operation")
415
- return if ret == NNG_OK
416
- error_msg = nng_strerror(ret)
417
- raise NNG::Error, "#{operation} failed: #{error_msg} (code: #{ret})"
418
- end
419
-
420
- # Create initialized socket
421
- def self.socket_initializer
422
- NngSocket.new.tap { |s| s[:id] = 0 }
423
- end
424
-
425
- # Create initialized dialer
426
- def self.dialer_initializer
427
- NngDialer.new.tap { |d| d[:id] = 0 }
428
- end
429
-
430
- # Create initialized listener
431
- def self.listener_initializer
432
- NngListener.new.tap { |l| l[:id] = 0 }
433
- end
434
-
435
- # Create initialized context
436
- def self.ctx_initializer
437
- NngCtx.new.tap { |c| c[:id] = 0 }
438
- end
439
-
440
- # Create initialized pipe
441
- def self.pipe_initializer
442
- NngPipe.new.tap { |p| p[:id] = 0 }
443
- end
444
- end
445
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+ require 'rbconfig'
5
+
6
+ module NNG
7
+ # FFI bindings for libnng
8
+ module FFI
9
+ extend ::FFI::Library
10
+
11
+ # Load install-time configuration if available
12
+ @install_config = {}
13
+ config_file = File.expand_path('../../ext/nng/nng_config.rb', __dir__)
14
+ if File.exist?(config_file)
15
+ require config_file
16
+ @install_config = NNG::InstallConfig::CONFIG rescue {}
17
+ end
18
+
19
+ # Detect platform and return library file patterns
20
+ def self.platform_info
21
+ case RbConfig::CONFIG['host_os']
22
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
23
+ { os: :windows, lib_pattern: 'nng*.dll', lib_name: 'nng.dll' }
24
+ when /darwin|mac os/
25
+ { os: :macos, lib_pattern: 'libnng*.dylib', lib_name: 'libnng.dylib' }
26
+ else
27
+ { os: :linux, lib_pattern: 'libnng.so*', lib_name: 'libnng.so' }
28
+ end
29
+ end
30
+
31
+ # Build library search paths with priority order:
32
+ # 1. ENV['NNG_LIB_PATH'] - Direct path to library file
33
+ # 2. ENV['NNG_LIB_DIR'] - Directory containing library
34
+ # 3. Install config (from gem install --with-nng-*)
35
+ # 4. Bundled library
36
+ # 5. System default paths
37
+ def self.build_lib_paths
38
+ paths = []
39
+ platform = platform_info
40
+
41
+ # Priority 1: Direct library path from environment
42
+ if ENV['NNG_LIB_PATH'] && !ENV['NNG_LIB_PATH'].empty?
43
+ paths << ENV['NNG_LIB_PATH']
44
+ puts "NNG: Using library from NNG_LIB_PATH: #{ENV['NNG_LIB_PATH']}" if ENV['NNG_DEBUG']
45
+ end
46
+
47
+ # Priority 2: Library directory from environment
48
+ if ENV['NNG_LIB_DIR'] && !ENV['NNG_LIB_DIR'].empty?
49
+ dir = ENV['NNG_LIB_DIR']
50
+ # Search for platform-specific library files in the directory
51
+ Dir.glob(File.join(dir, platform[:lib_pattern])).each { |lib| paths << lib }
52
+ puts "NNG: Searching in NNG_LIB_DIR: #{dir}" if ENV['NNG_DEBUG']
53
+ end
54
+
55
+ # Priority 3: Install-time configuration
56
+ if @install_config[:nng_lib]
57
+ paths << @install_config[:nng_lib]
58
+ puts "NNG: Using library from install config: #{@install_config[:nng_lib]}" if ENV['NNG_DEBUG']
59
+ elsif @install_config[:nng_dir]
60
+ nng_dir = @install_config[:nng_dir]
61
+ # Try common subdirectories based on platform
62
+ subdirs = case platform[:os]
63
+ when :windows
64
+ %w[bin lib]
65
+ when :macos
66
+ %w[lib]
67
+ else
68
+ %w[lib lib64 lib/x86_64-linux-gnu]
69
+ end
70
+
71
+ subdirs.each do |subdir|
72
+ lib_dir = File.join(nng_dir, subdir)
73
+ Dir.glob(File.join(lib_dir, platform[:lib_pattern])).each { |lib| paths << lib }
74
+ end
75
+ puts "NNG: Searching in install config dir: #{nng_dir}" if ENV['NNG_DEBUG']
76
+ end
77
+
78
+ # Priority 4: Bundled library (platform-specific)
79
+ bundled_libs = case platform[:os]
80
+ when :windows
81
+ [
82
+ File.expand_path('../../ext/nng/nng.dll', __dir__),
83
+ File.expand_path('../../ext/nng/libnng.dll', __dir__)
84
+ ]
85
+ when :macos
86
+ [
87
+ File.expand_path('../../ext/nng/libnng.1.11.0.dylib', __dir__),
88
+ File.expand_path('../../ext/nng/libnng.dylib', __dir__)
89
+ ]
90
+ else
91
+ [
92
+ File.expand_path('../../ext/nng/libnng.so.1.11.0', __dir__),
93
+ File.expand_path('../../ext/nng/libnng.so', __dir__)
94
+ ]
95
+ end
96
+ paths += bundled_libs
97
+
98
+ # Priority 5: System default paths (platform-specific)
99
+ case platform[:os]
100
+ when :windows
101
+ # Windows: check common DLL locations
102
+ paths += [
103
+ File.join(ENV['WINDIR'] || 'C:/Windows', 'System32/nng.dll'),
104
+ File.join(ENV['ProgramFiles'] || 'C:/Program Files', 'nng/bin/nng.dll')
105
+ ]
106
+ when :macos
107
+ # macOS: check common library locations
108
+ paths += [
109
+ '/usr/local/lib/libnng.dylib',
110
+ '/opt/homebrew/lib/libnng.dylib',
111
+ '/usr/lib/libnng.dylib'
112
+ ]
113
+ else
114
+ # Linux: check common library locations
115
+ paths += [
116
+ '/usr/local/lib/libnng.so',
117
+ '/usr/lib/libnng.so',
118
+ '/usr/lib/x86_64-linux-gnu/libnng.so',
119
+ '/usr/lib/aarch64-linux-gnu/libnng.so'
120
+ ]
121
+ end
122
+
123
+ paths.uniq
124
+ end
125
+
126
+ @lib_paths = build_lib_paths
127
+ @loaded_lib_path = nil
128
+
129
+ @lib_paths.each do |path|
130
+ if File.exist?(path)
131
+ begin
132
+ ffi_lib path
133
+ @loaded_lib_path = path
134
+ puts "NNG: Successfully loaded library from: #{path}" if ENV['NNG_DEBUG']
135
+ break
136
+ rescue LoadError => e
137
+ puts "NNG: Failed to load #{path}: #{e.message}" if ENV['NNG_DEBUG']
138
+ next
139
+ end
140
+ end
141
+ end
142
+
143
+ unless @loaded_lib_path
144
+ platform = platform_info
145
+ lib_name = platform[:lib_name]
146
+ error_msg = "Could not find NNG library (#{lib_name}) in any of the following locations:\n"
147
+ error_msg += @lib_paths.map { |p| " - #{p}" }.join("\n")
148
+ error_msg += "\n\nYou can specify a custom path using:\n"
149
+ error_msg += " - Environment variable: export NNG_LIB_PATH=/path/to/#{lib_name}\n"
150
+ error_msg += " - Environment variable: export NNG_LIB_DIR=/path/to/lib\n"
151
+ error_msg += " - Gem install option: gem install nng-ruby -- --with-nng-dir=/path/to/nng\n"
152
+ error_msg += " - Gem install option: gem install nng-ruby -- --with-nng-lib=/path/to/#{lib_name}"
153
+ raise LoadError, error_msg
154
+ end
155
+
156
+ def self.loaded_lib_path
157
+ @loaded_lib_path
158
+ end
159
+
160
+ def self.install_config
161
+ @install_config
162
+ end
163
+
164
+ # ============================================================================
165
+ # Constants
166
+ # ============================================================================
167
+
168
+ # NOTE: These are compile-time version constants for reference.
169
+ # Use nng_version() to get the actual runtime library version.
170
+ NNG_MAJOR_VERSION = 1
171
+ NNG_MINOR_VERSION = 8
172
+ NNG_PATCH_VERSION = 0
173
+
174
+ # Maximum address length
175
+ NNG_MAXADDRLEN = 128
176
+
177
+ # Error codes
178
+ NNG_OK = 0
179
+ NNG_EINTR = 1
180
+ NNG_ENOMEM = 2
181
+ NNG_EINVAL = 3
182
+ NNG_EBUSY = 4
183
+ NNG_ETIMEDOUT = 5
184
+ NNG_ECONNREFUSED = 6
185
+ NNG_ECLOSED = 7
186
+ NNG_EAGAIN = 8
187
+ NNG_ENOTSUP = 9
188
+ NNG_EADDRINUSE = 10
189
+ NNG_ESTATE = 11
190
+ NNG_ENOENT = 12
191
+ NNG_EPROTO = 13
192
+ NNG_EUNREACHABLE = 14
193
+ NNG_EADDRINVAL = 15
194
+ NNG_EPERM = 16
195
+ NNG_EMSGSIZE = 17
196
+ NNG_ECONNABORTED = 18
197
+ NNG_ECONNRESET = 19
198
+ NNG_ECANCELED = 20
199
+ NNG_ENOFILES = 21
200
+ NNG_ENOSPC = 22
201
+ NNG_EEXIST = 23
202
+ NNG_EREADONLY = 24
203
+ NNG_EWRITEONLY = 25
204
+ NNG_ECRYPTO = 26
205
+ NNG_EPEERAUTH = 27
206
+ NNG_ENOARG = 28
207
+ NNG_EAMBIGUOUS = 29
208
+ NNG_EBADTYPE = 30
209
+ NNG_ECONNSHUT = 31
210
+ NNG_EINTERNAL = 1000
211
+
212
+ # Flags
213
+ NNG_FLAG_ALLOC = 1 # Allocate receive buffer
214
+ NNG_FLAG_NONBLOCK = 2 # Non-blocking mode
215
+
216
+ # Socket address families
217
+ NNG_AF_UNSPEC = 0
218
+ NNG_AF_INPROC = 1
219
+ NNG_AF_IPC = 2
220
+ NNG_AF_INET = 3
221
+ NNG_AF_INET6 = 4
222
+ NNG_AF_ZT = 5
223
+ NNG_AF_ABSTRACT = 6
224
+
225
+ # Pipe events
226
+ NNG_PIPE_EV_ADD_PRE = 0
227
+ NNG_PIPE_EV_ADD_POST = 1
228
+ NNG_PIPE_EV_REM_POST = 2
229
+ NNG_PIPE_EV_NUM = 3
230
+
231
+ # ============================================================================
232
+ # Type definitions
233
+ # ============================================================================
234
+
235
+ # nng_socket structure
236
+ class NngSocket < ::FFI::Struct
237
+ layout :id, :uint32
238
+ end
239
+
240
+ # nng_dialer structure
241
+ class NngDialer < ::FFI::Struct
242
+ layout :id, :uint32
243
+ end
244
+
245
+ # nng_listener structure
246
+ class NngListener < ::FFI::Struct
247
+ layout :id, :uint32
248
+ end
249
+
250
+ # nng_ctx structure
251
+ class NngCtx < ::FFI::Struct
252
+ layout :id, :uint32
253
+ end
254
+
255
+ # nng_pipe structure
256
+ class NngPipe < ::FFI::Struct
257
+ layout :id, :uint32
258
+ end
259
+
260
+ # nng_duration - time interval in milliseconds
261
+ typedef :int32, :nng_duration
262
+
263
+ # nng_time - absolute time in milliseconds
264
+ typedef :uint64, :nng_time
265
+
266
+ # Opaque types
267
+ typedef :pointer, :nng_msg
268
+ typedef :pointer, :nng_aio
269
+ typedef :pointer, :nng_stat
270
+
271
+ # Socket address structures
272
+ class NngSockaddrInproc < ::FFI::Struct
273
+ layout :sa_family, :uint16,
274
+ :sa_name, [:char, NNG_MAXADDRLEN]
275
+ end
276
+
277
+ class NngSockaddrPath < ::FFI::Struct
278
+ layout :sa_family, :uint16,
279
+ :sa_path, [:char, NNG_MAXADDRLEN]
280
+ end
281
+
282
+ class NngSockaddrIn < ::FFI::Struct
283
+ layout :sa_family, :uint16,
284
+ :sa_port, :uint16,
285
+ :sa_addr, :uint32
286
+ end
287
+
288
+ class NngSockaddrIn6 < ::FFI::Struct
289
+ layout :sa_family, :uint16,
290
+ :sa_port, :uint16,
291
+ :sa_addr, [:uint8, 16],
292
+ :sa_scope, :uint32
293
+ end
294
+
295
+ class NngSockaddrStorage < ::FFI::Struct
296
+ layout :sa_family, :uint16,
297
+ :sa_pad, [:uint64, 16]
298
+ end
299
+
300
+ class NngSockaddr < ::FFI::Union
301
+ layout :s_family, :uint16,
302
+ :s_inproc, NngSockaddrInproc,
303
+ :s_ipc, NngSockaddrPath,
304
+ :s_in, NngSockaddrIn,
305
+ :s_in6, NngSockaddrIn6,
306
+ :s_storage, NngSockaddrStorage
307
+ end
308
+
309
+ # ============================================================================
310
+ # Core functions
311
+ # ============================================================================
312
+
313
+ # Library version
314
+ attach_function :nng_version, [], :string
315
+
316
+ # Library finalization
317
+ attach_function :nng_fini, [], :void
318
+
319
+ # Error handling
320
+ attach_function :nng_strerror, [:int], :string
321
+
322
+ # Socket functions
323
+ attach_function :nng_close, [NngSocket.by_value], :int
324
+ attach_function :nng_socket_id, [NngSocket.by_value], :int
325
+
326
+ # Socket options - set
327
+ attach_function :nng_setopt, [NngSocket.by_value, :string, :pointer, :size_t], :int
328
+ attach_function :nng_setopt_bool, [NngSocket.by_value, :string, :bool], :int
329
+ attach_function :nng_setopt_int, [NngSocket.by_value, :string, :int], :int
330
+ attach_function :nng_setopt_size, [NngSocket.by_value, :string, :size_t], :int
331
+ attach_function :nng_setopt_uint64, [NngSocket.by_value, :string, :uint64], :int
332
+ attach_function :nng_setopt_string, [NngSocket.by_value, :string, :string], :int
333
+ attach_function :nng_setopt_ptr, [NngSocket.by_value, :string, :pointer], :int
334
+ attach_function :nng_setopt_ms, [NngSocket.by_value, :string, :nng_duration], :int
335
+
336
+ # Socket options - get
337
+ attach_function :nng_getopt, [NngSocket.by_value, :string, :pointer, :pointer], :int
338
+ attach_function :nng_getopt_bool, [NngSocket.by_value, :string, :pointer], :int
339
+ attach_function :nng_getopt_int, [NngSocket.by_value, :string, :pointer], :int
340
+ attach_function :nng_getopt_size, [NngSocket.by_value, :string, :pointer], :int
341
+ attach_function :nng_getopt_uint64, [NngSocket.by_value, :string, :pointer], :int
342
+ attach_function :nng_getopt_string, [NngSocket.by_value, :string, :pointer], :int
343
+ attach_function :nng_getopt_ptr, [NngSocket.by_value, :string, :pointer], :int
344
+ attach_function :nng_getopt_ms, [NngSocket.by_value, :string, :pointer], :int
345
+
346
+ # Connection management
347
+ attach_function :nng_listen, [NngSocket.by_value, :string, :pointer, :int], :int
348
+ attach_function :nng_dial, [NngSocket.by_value, :string, :pointer, :int], :int
349
+
350
+ # Dialer functions
351
+ attach_function :nng_dialer_create, [:pointer, NngSocket.by_value, :string], :int
352
+ attach_function :nng_dialer_start, [NngDialer.by_value, :int], :int
353
+ attach_function :nng_dialer_close, [NngDialer.by_value], :int
354
+ attach_function :nng_dialer_id, [NngDialer.by_value], :int
355
+
356
+ # Listener functions
357
+ attach_function :nng_listener_create, [:pointer, NngSocket.by_value, :string], :int
358
+ attach_function :nng_listener_start, [NngListener.by_value, :int], :int
359
+ attach_function :nng_listener_close, [NngListener.by_value], :int
360
+ attach_function :nng_listener_id, [NngListener.by_value], :int
361
+
362
+ # Send and receive (synchronous)
363
+ attach_function :nng_send, [NngSocket.by_value, :pointer, :size_t, :int], :int
364
+ attach_function :nng_recv, [NngSocket.by_value, :pointer, :pointer, :int], :int
365
+
366
+ # Message-based send/receive
367
+ attach_function :nng_sendmsg, [NngSocket.by_value, :nng_msg, :int], :int
368
+ attach_function :nng_recvmsg, [NngSocket.by_value, :pointer, :int], :int
369
+
370
+ # Memory management
371
+ attach_function :nng_free, [:pointer, :size_t], :void
372
+ attach_function :nng_strfree, [:pointer], :void
373
+
374
+ # Message functions
375
+ attach_function :nng_msg_alloc, [:pointer, :size_t], :int
376
+ attach_function :nng_msg_free, [:nng_msg], :void
377
+ attach_function :nng_msg_realloc, [:nng_msg, :size_t], :int
378
+ attach_function :nng_msg_header, [:nng_msg], :pointer
379
+ attach_function :nng_msg_header_len, [:nng_msg], :size_t
380
+ attach_function :nng_msg_body, [:nng_msg], :pointer
381
+ attach_function :nng_msg_len, [:nng_msg], :size_t
382
+ attach_function :nng_msg_append, [:nng_msg, :pointer, :size_t], :int
383
+ attach_function :nng_msg_insert, [:nng_msg, :pointer, :size_t], :int
384
+ attach_function :nng_msg_trim, [:nng_msg, :size_t], :int
385
+ attach_function :nng_msg_chop, [:nng_msg, :size_t], :int
386
+ attach_function :nng_msg_header_append, [:nng_msg, :pointer, :size_t], :int
387
+ attach_function :nng_msg_header_insert, [:nng_msg, :pointer, :size_t], :int
388
+ attach_function :nng_msg_header_trim, [:nng_msg, :size_t], :int
389
+ attach_function :nng_msg_header_chop, [:nng_msg, :size_t], :int
390
+ attach_function :nng_msg_clear, [:nng_msg], :void
391
+ attach_function :nng_msg_header_clear, [:nng_msg], :void
392
+ attach_function :nng_msg_dup, [:pointer, :nng_msg], :int
393
+ attach_function :nng_msg_get_pipe, [:nng_msg], NngPipe.by_value
394
+ attach_function :nng_msg_set_pipe, [:nng_msg, NngPipe.by_value], :void
395
+
396
+ # Context functions
397
+ attach_function :nng_ctx_open, [:pointer, NngSocket.by_value], :int
398
+ attach_function :nng_ctx_close, [NngCtx.by_value], :int
399
+ attach_function :nng_ctx_id, [NngCtx.by_value], :int
400
+ attach_function :nng_ctx_send, [NngCtx.by_value, :nng_aio], :void
401
+ attach_function :nng_ctx_recv, [NngCtx.by_value, :nng_aio], :void
402
+ attach_function :nng_ctx_sendmsg, [NngCtx.by_value, :nng_msg, :int], :int
403
+ attach_function :nng_ctx_recvmsg, [NngCtx.by_value, :pointer, :int], :int
404
+
405
+ # Pipe functions
406
+ attach_function :nng_pipe_id, [NngPipe.by_value], :int
407
+ attach_function :nng_pipe_socket, [NngPipe.by_value], NngSocket.by_value
408
+ attach_function :nng_pipe_dialer, [NngPipe.by_value], NngDialer.by_value
409
+ attach_function :nng_pipe_listener, [NngPipe.by_value], NngListener.by_value
410
+ attach_function :nng_pipe_close, [NngPipe.by_value], :int
411
+
412
+ # Asynchronous I/O functions
413
+ attach_function :nng_aio_alloc, [:pointer, :pointer, :pointer], :int
414
+ attach_function :nng_aio_free, [:nng_aio], :void
415
+ attach_function :nng_aio_stop, [:nng_aio], :void
416
+ attach_function :nng_aio_result, [:nng_aio], :int
417
+ attach_function :nng_aio_count, [:nng_aio], :size_t
418
+ attach_function :nng_aio_cancel, [:nng_aio], :void
419
+ attach_function :nng_aio_abort, [:nng_aio, :int], :void
420
+ attach_function :nng_aio_wait, [:nng_aio], :void
421
+ attach_function :nng_aio_set_msg, [:nng_aio, :nng_msg], :void
422
+ attach_function :nng_aio_get_msg, [:nng_aio], :nng_msg
423
+ attach_function :nng_aio_set_input, [:nng_aio, :uint, :pointer], :int
424
+ attach_function :nng_aio_set_output, [:nng_aio, :uint, :pointer], :int
425
+ attach_function :nng_aio_set_timeout, [:nng_aio, :nng_duration], :void
426
+ attach_function :nng_aio_set_iov, [:nng_aio, :uint, :pointer], :int
427
+ attach_function :nng_aio_begin, [:nng_aio], :bool
428
+ attach_function :nng_aio_finish, [:nng_aio, :int], :void
429
+ attach_function :nng_aio_defer, [:nng_aio, :pointer, :pointer], :void
430
+
431
+ # Asynchronous send/receive
432
+ attach_function :nng_send_aio, [NngSocket.by_value, :nng_aio], :void
433
+ attach_function :nng_recv_aio, [NngSocket.by_value, :nng_aio], :void
434
+
435
+ # Statistics functions
436
+ attach_function :nng_stats_get, [:pointer], :int
437
+ attach_function :nng_stats_free, [:nng_stat], :void
438
+ attach_function :nng_stats_dump, [:nng_stat], :void
439
+ attach_function :nng_stat_next, [:nng_stat], :nng_stat
440
+ attach_function :nng_stat_child, [:nng_stat], :nng_stat
441
+ attach_function :nng_stat_name, [:nng_stat], :string
442
+ attach_function :nng_stat_type, [:nng_stat], :int
443
+ attach_function :nng_stat_unit, [:nng_stat], :int
444
+ attach_function :nng_stat_value, [:nng_stat], :uint64
445
+ attach_function :nng_stat_desc, [:nng_stat], :string
446
+
447
+ # Device functions (forwarder/reflector)
448
+ attach_function :nng_device, [NngSocket.by_value, NngSocket.by_value], :int
449
+
450
+ # Utility functions
451
+ attach_function :nng_sleep_aio, [:nng_duration, :nng_aio], :void
452
+ attach_function :nng_msleep, [:nng_duration], :void
453
+
454
+ # URL parsing
455
+ typedef :pointer, :nng_url
456
+ attach_function :nng_url_parse, [:pointer, :string], :int
457
+ attach_function :nng_url_free, [:nng_url], :void
458
+ attach_function :nng_url_clone, [:pointer, :nng_url], :int
459
+
460
+ # ============================================================================
461
+ # Protocol-specific functions (will be attached when protocol is loaded)
462
+ # ============================================================================
463
+
464
+ # These will be attached by specific protocol modules:
465
+ # - nng_pair0_open, nng_pair1_open
466
+ # - nng_push0_open, nng_pull0_open
467
+ # - nng_pub0_open, nng_sub0_open
468
+ # - nng_req0_open, nng_rep0_open
469
+ # - nng_surveyor0_open, nng_respondent0_open
470
+ # - nng_bus0_open
471
+
472
+ # ============================================================================
473
+ # Helper methods
474
+ # ============================================================================
475
+
476
+ # Check error code and raise exception if not OK
477
+ def self.check_error(ret, operation = "NNG operation")
478
+ return if ret == NNG_OK
479
+ error_msg = nng_strerror(ret)
480
+ raise NNG::Error, "#{operation} failed: #{error_msg} (code: #{ret})"
481
+ end
482
+
483
+ # Create initialized socket
484
+ def self.socket_initializer
485
+ NngSocket.new.tap { |s| s[:id] = 0 }
486
+ end
487
+
488
+ # Create initialized dialer
489
+ def self.dialer_initializer
490
+ NngDialer.new.tap { |d| d[:id] = 0 }
491
+ end
492
+
493
+ # Create initialized listener
494
+ def self.listener_initializer
495
+ NngListener.new.tap { |l| l[:id] = 0 }
496
+ end
497
+
498
+ # Create initialized context
499
+ def self.ctx_initializer
500
+ NngCtx.new.tap { |c| c[:id] = 0 }
501
+ end
502
+
503
+ # Create initialized pipe
504
+ def self.pipe_initializer
505
+ NngPipe.new.tap { |p| p[:id] = 0 }
506
+ end
507
+ end
508
+ end