omq-backend-rust 0.3.0-java
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +123 -0
- data/LICENSE +15 -0
- data/README.md +86 -0
- data/lib/omq/backend/rust.rb +25 -0
- data/lib/omq/rust/java/engine.rb +642 -0
- data/lib/omq/rust/java/platform.rb +95 -0
- data/lib/omq/rust/version.rb +19 -0
- metadata +81 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9895c2e753f379324c116f3082c76ab5d562e9d5b7e00539073bdfdd90fa2ac9
|
|
4
|
+
data.tar.gz: 11cdd6699b6e7efa69a2e8e84a65641406732d3fffb33326311925c009b98c24
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 58c0e0103fe6cffd7d73373045cdce94f113f9c7d520e7a7de909e7a311618318790e5bf7c01685efb83f7bea927b5af7588474189942c29f1f38a4f2ea36d3b
|
|
7
|
+
data.tar.gz: 95dc600672be8c9ee8a5341b79ce090856ed8129053437fb4ca340d70e039cea9f980cc751ebffb1504f035ac827a1aee4ac837fc0da909e30db379e1c487a41
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [0.3.0] - 2026-08-26
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added JRuby support through OMQ.java Maven artifacts for Linux x86_64,
|
|
10
|
+
macOS aarch64, macOS x86_64, and Windows x86_64.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Replaced the private MRI/TruffleRuby native extension with the first-class
|
|
15
|
+
`omq-rs` binding.
|
|
16
|
+
|
|
17
|
+
## [0.2.0] - 2026-08-08
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Added TruffleRuby support for the Rust backend.
|
|
22
|
+
- Added a shared fallback file descriptor watcher for Rubies without a
|
|
23
|
+
native `Fiber.scheduler`.
|
|
24
|
+
- Added TruffleRuby CI coverage for the Rust backend.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Updated to `omq-tokio` 0.21.1 and `omq-proto` 0.25.1.
|
|
29
|
+
- Replaced Magnus with a small `rb-sys` helper layer that uses public
|
|
30
|
+
Ruby C API calls.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Raised Ruby exceptions through public C API entry points for
|
|
35
|
+
TruffleRuby compatibility.
|
|
36
|
+
- Rejected invalid negative numeric options before they can overflow Rust
|
|
37
|
+
sizes or panic on invalid durations.
|
|
38
|
+
|
|
39
|
+
## [0.1.7] - 2026-08-02
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- Restored Rust/Ruby backend interop coverage.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- Updated to `omq-tokio` 0.21.0.
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- Drained the Ruby-to-Tokio send pump before native socket close.
|
|
52
|
+
- Released Rust send ring slots before awaiting native sends, avoiding
|
|
53
|
+
high-throughput stalls under `omq-tokio` 0.21.0 backpressure.
|
|
54
|
+
- Waited for Rust peer registration before resolving `peer_connected`.
|
|
55
|
+
|
|
56
|
+
## [0.1.6] - 2026-08-01
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
|
|
60
|
+
- Updated to `omq-tokio` 0.20.3 and `omq-proto` 0.25.0.
|
|
61
|
+
- Enabled OMQ.rs `zstd+tcp://` by default.
|
|
62
|
+
- Forwarded compression bind/connect kwargs to current OMQ.rs options:
|
|
63
|
+
`dict:`, `auto_dict:`, `compression_threshold:`, `max_recv_dict_size:`,
|
|
64
|
+
`compression_offload_threshold:`, and zstd `level:`.
|
|
65
|
+
|
|
66
|
+
## [0.1.5] - 2026-07-31
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
|
|
70
|
+
- Used `IO#wait_readable` for Rust backend receive waits and routed
|
|
71
|
+
`close_read` wakeups through the native receive notifier.
|
|
72
|
+
- Updated to `omq-tokio` 0.20.2, fixing large byte-stream `PUB` fan-out
|
|
73
|
+
delivery over IPC and TCP.
|
|
74
|
+
|
|
75
|
+
## [0.1.4] - 2026-07-30
|
|
76
|
+
|
|
77
|
+
### Fixed
|
|
78
|
+
|
|
79
|
+
- Accepted Ruby string identities when materializing Rust sockets.
|
|
80
|
+
|
|
81
|
+
## [0.1.3] - 2026-07-30
|
|
82
|
+
|
|
83
|
+
### Added
|
|
84
|
+
|
|
85
|
+
- Added `require "omq/backend/rust"` as the canonical backend load path.
|
|
86
|
+
|
|
87
|
+
### Removed
|
|
88
|
+
|
|
89
|
+
- Removed the old `require "omq/rust"` load path.
|
|
90
|
+
|
|
91
|
+
## 0.1.2 - 2026-07-28
|
|
92
|
+
|
|
93
|
+
### Changed
|
|
94
|
+
|
|
95
|
+
- Updated to `omq-tokio` 0.20.1 and `omq-proto` 0.24.1.
|
|
96
|
+
- Marked the native extension Ractor-safe for Ruby 4 Ractors.
|
|
97
|
+
- Adapted CURVE server setup to `CurveServerOptions`.
|
|
98
|
+
|
|
99
|
+
## 0.1.1 - 2026-07-23
|
|
100
|
+
|
|
101
|
+
### Changed
|
|
102
|
+
|
|
103
|
+
- Updated to `omq-tokio` 0.19.3 and `omq-proto` 0.23.2.
|
|
104
|
+
- Removed obsolete Blake3ZMQ support.
|
|
105
|
+
- Moved release source to the `zeromq/omq.rb` monorepo.
|
|
106
|
+
|
|
107
|
+
## v0.1.0 — 2026-06-24
|
|
108
|
+
|
|
109
|
+
Initial release.
|
|
110
|
+
|
|
111
|
+
### Added
|
|
112
|
+
|
|
113
|
+
- **`OMQ::Rust::Engine`** — drop-in OMQ engine backed by omq-tokio. Pass
|
|
114
|
+
`backend: :rust` to any OMQ socket constructor.
|
|
115
|
+
- All standard socket types: REQ/REP, PUB/SUB, PUSH/PULL, DEALER/ROUTER,
|
|
116
|
+
XPUB/XSUB, PAIR.
|
|
117
|
+
- All draft socket types: CLIENT/SERVER, RADIO/DISH, SCATTER/GATHER, CHANNEL.
|
|
118
|
+
- TCP and IPC transports.
|
|
119
|
+
- CURVE (CurveZMQ) and BLAKE3ZMQ security mechanisms.
|
|
120
|
+
- Full cross-backend interop with the default Ruby engine.
|
|
121
|
+
- Lifecycle promises: `peer_connected`, `all_peers_gone`, `subscriber_joined`.
|
|
122
|
+
- Monitor event forwarding from the Tokio runtime.
|
|
123
|
+
- Configurable IO thread count via `OMQ::Rust.io_threads`.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026, Patrik Wenger
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# omq-backend-rust
|
|
2
|
+
|
|
3
|
+
[](https://github.com/zeromq/omq.rb/actions/workflows/ci.yml)
|
|
4
|
+
[](https://rubygems.org/gems/omq-backend-rust)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.ruby-lang.org)
|
|
7
|
+
|
|
8
|
+
Rust-backed engine for [OMQ](https://github.com/zeromq/omq.rb). Same socket API,
|
|
9
|
+
but networking runs through the first-class
|
|
10
|
+
[omq-rs](https://github.com/paddor/omq.rs/tree/main/bindings/ruby) binding on
|
|
11
|
+
MRI and TruffleRuby, or through OMQ.java on JRuby.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
The `omq-rs` dependency compiles OMQ.rs during installation on MRI and
|
|
16
|
+
TruffleRuby. JRuby uses OMQ.java on Java 25 or newer. The Java gem selects the
|
|
17
|
+
matching OMQ.java artifact on Linux x86_64, macOS aarch64, macOS x86_64, and
|
|
18
|
+
Windows x86_64.
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
# Gemfile
|
|
22
|
+
gem "omq-backend-rust"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
gem install omq-backend-rust
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
require "omq"
|
|
33
|
+
require "omq/backend/rust"
|
|
34
|
+
|
|
35
|
+
Async do
|
|
36
|
+
push = OMQ::PUSH.new(backend: :rust)
|
|
37
|
+
pull = OMQ::PULL.new(backend: :rust)
|
|
38
|
+
|
|
39
|
+
port = pull.bind("tcp://127.0.0.1:0").port
|
|
40
|
+
push.connect("tcp://127.0.0.1:#{port}")
|
|
41
|
+
push.peer_connected.wait
|
|
42
|
+
|
|
43
|
+
push << "hello from Rust"
|
|
44
|
+
p pull.receive # => ["hello from Rust"]
|
|
45
|
+
ensure
|
|
46
|
+
push&.close
|
|
47
|
+
pull&.close
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The `:rust` backend is fully interoperable with the default `:ruby` backend.
|
|
52
|
+
Mix backends freely within the same process.
|
|
53
|
+
|
|
54
|
+
## Supported socket types
|
|
55
|
+
|
|
56
|
+
All standard and draft ZMTP socket types: REQ/REP, PUB/SUB, PUSH/PULL,
|
|
57
|
+
DEALER/ROUTER, XPUB/XSUB, PAIR, CLIENT/SERVER, RADIO/DISH,
|
|
58
|
+
SCATTER/GATHER, CHANNEL.
|
|
59
|
+
|
|
60
|
+
## Security mechanisms
|
|
61
|
+
|
|
62
|
+
- **NULL** (default)
|
|
63
|
+
- **CURVE** (CurveZMQ, via [Nuckle](https://github.com/paddor/nuckle))
|
|
64
|
+
|
|
65
|
+
## Compressed transports
|
|
66
|
+
|
|
67
|
+
The Rust backend supports `lz4+tcp://` and `zstd+tcp://`. Compression
|
|
68
|
+
configuration uses the same bind/connect kwargs as the Ruby transports:
|
|
69
|
+
`level:` for zstd compression level (`-8..4`), `dict:` for a static
|
|
70
|
+
send-side dictionary, and `auto_dict: true` for automatic dictionary
|
|
71
|
+
training.
|
|
72
|
+
|
|
73
|
+
Automatic dictionary training is off by default.
|
|
74
|
+
OMQ.rs stores compression configuration per socket, so pass these kwargs on
|
|
75
|
+
the first `bind` or `connect` for a Rust-backed socket.
|
|
76
|
+
|
|
77
|
+
## Development
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
OMQ_RS_PATH=/path/to/omq.rs/bindings/ruby bundle install
|
|
81
|
+
OMQ_RS_PATH=/path/to/omq.rs/bindings/ruby bundle exec rake
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
[ISC](LICENSE)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "omq"
|
|
4
|
+
require_relative "../rust/version"
|
|
5
|
+
|
|
6
|
+
if RUBY_ENGINE == "jruby"
|
|
7
|
+
require_relative "../rust/java/engine"
|
|
8
|
+
else
|
|
9
|
+
require "omq/rs"
|
|
10
|
+
require_relative "../rust/engine"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
if RUBY_ENGINE == "jruby"
|
|
14
|
+
module OMQ
|
|
15
|
+
module Rust
|
|
16
|
+
@io_threads = 1
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
attr_accessor :io_threads
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
OMQ::Backend.register(:rust, OMQ::Rust::Engine)
|
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "jar_dependencies"
|
|
4
|
+
require "thread"
|
|
5
|
+
require "uri"
|
|
6
|
+
require "java"
|
|
7
|
+
|
|
8
|
+
require_relative "../version"
|
|
9
|
+
require_relative "platform"
|
|
10
|
+
|
|
11
|
+
require_jar "io.github.paddor", "omq-java",
|
|
12
|
+
OMQ::Rust::Java.classifier, OMQ::Rust::OMQ_JAVA_VERSION
|
|
13
|
+
|
|
14
|
+
module OMQ
|
|
15
|
+
module Rust
|
|
16
|
+
module Java
|
|
17
|
+
Duration = ::Java::JavaTime::Duration
|
|
18
|
+
JavaOMQ = ::Java::IoOmq::OMQ
|
|
19
|
+
SocketOptions = ::Java::IoOmq::SocketOptions
|
|
20
|
+
SocketType = ::Java::IoOmq::SocketType
|
|
21
|
+
Message = ::Java::IoOmq::Message
|
|
22
|
+
CurveKeypair = ::Java::IoOmq::CurveKeypair
|
|
23
|
+
OnMute = ::Java::IoOmq::OnMute
|
|
24
|
+
@context_mutex = Mutex.new
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
def context
|
|
28
|
+
@context_mutex.synchronize do
|
|
29
|
+
@context ||= JavaOMQ.context(OMQ::Rust.io_threads.to_i).tap do
|
|
30
|
+
at_exit { @context&.close rescue nil } unless @context_at_exit
|
|
31
|
+
@context_at_exit = true
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class Promise
|
|
38
|
+
def initialize(&resolver)
|
|
39
|
+
@mutex = Mutex.new
|
|
40
|
+
@cv = ConditionVariable.new
|
|
41
|
+
@resolved = false
|
|
42
|
+
@value = nil
|
|
43
|
+
@resolver = resolver
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def resolved?
|
|
48
|
+
@mutex.synchronize { @resolved }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def resolve(value = nil)
|
|
53
|
+
@mutex.synchronize do
|
|
54
|
+
return @value if @resolved
|
|
55
|
+
|
|
56
|
+
@resolved = true
|
|
57
|
+
@value = value
|
|
58
|
+
@cv.broadcast
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def wait
|
|
64
|
+
until resolved?
|
|
65
|
+
if @resolver
|
|
66
|
+
@resolver.call(self)
|
|
67
|
+
else
|
|
68
|
+
@mutex.synchronize { @cv.wait(@mutex, 0.05) unless @resolved }
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
@value
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
class Engine
|
|
77
|
+
POLL_SECONDS = 0.005
|
|
78
|
+
POLL_DURATION = Duration.ofMillis((POLL_SECONDS * 1000).to_i)
|
|
79
|
+
|
|
80
|
+
attr_reader :options, :connections, :routing, :socket_type
|
|
81
|
+
attr_reader :peer_connected, :all_peers_gone, :parent_task
|
|
82
|
+
attr_reader :on_io_thread
|
|
83
|
+
alias on_io_thread? on_io_thread
|
|
84
|
+
attr_writer :reconnect_enabled
|
|
85
|
+
attr_accessor :subscriber_joined
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def initialize(socket_type, options)
|
|
89
|
+
@socket_type = socket_type
|
|
90
|
+
@options = options
|
|
91
|
+
@connections = {}
|
|
92
|
+
@closed = false
|
|
93
|
+
@parent_task = nil
|
|
94
|
+
@on_io_thread = false
|
|
95
|
+
@materialized = false
|
|
96
|
+
@recv_sentinels = 0
|
|
97
|
+
@compression_options = {}
|
|
98
|
+
|
|
99
|
+
@peer_connected = Promise.new do |promise|
|
|
100
|
+
wait_connected(1)
|
|
101
|
+
promise.resolve(true) unless @closed
|
|
102
|
+
end
|
|
103
|
+
@all_peers_gone = Promise.new do |promise|
|
|
104
|
+
wait_all_peers_gone
|
|
105
|
+
promise.resolve(true) unless @closed
|
|
106
|
+
end
|
|
107
|
+
@subscriber_joined = Promise.new do |promise|
|
|
108
|
+
wait_subscribed(1)
|
|
109
|
+
promise.resolve(true) unless @closed
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
@routing = RoutingStub.new(self)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def capture_parent_task(parent: nil)
|
|
117
|
+
@parent_task ||= parent
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def bind(endpoint, parent: nil, **opts)
|
|
122
|
+
capture_parent_task(parent: parent)
|
|
123
|
+
apply_endpoint_options!(opts)
|
|
124
|
+
ensure_materialized
|
|
125
|
+
URI.parse(with_java_errors { @native.bind(endpoint) })
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def connect(endpoint, parent: nil, **opts)
|
|
130
|
+
capture_parent_task(parent: parent)
|
|
131
|
+
apply_endpoint_options!(opts)
|
|
132
|
+
ensure_materialized
|
|
133
|
+
with_java_errors { @native.connect(endpoint) }
|
|
134
|
+
resolve_peer_connected if endpoint.start_with?("inproc://")
|
|
135
|
+
URI.parse(endpoint)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def disconnect(endpoint)
|
|
140
|
+
ensure_materialized
|
|
141
|
+
with_java_errors { @native.disconnect(endpoint) }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def unbind(endpoint)
|
|
146
|
+
ensure_materialized
|
|
147
|
+
with_java_errors { @native.unbind(endpoint) }
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def enqueue_send(parts)
|
|
152
|
+
ensure_materialized
|
|
153
|
+
msg = java_message(parts)
|
|
154
|
+
|
|
155
|
+
if (timeout = @options.write_timeout)
|
|
156
|
+
ok = with_java_errors { @native.send(msg, duration_from_seconds("write_timeout", timeout)) }
|
|
157
|
+
raise IO::TimeoutError, "operation timed out" unless ok
|
|
158
|
+
else
|
|
159
|
+
with_java_errors { @native.send(msg) }
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
nil
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def dequeue_recv
|
|
167
|
+
ensure_materialized
|
|
168
|
+
@recv_deadline = monotonic_time + @options.read_timeout.to_f if @options.read_timeout
|
|
169
|
+
|
|
170
|
+
loop do
|
|
171
|
+
return take_recv_sentinel if @recv_sentinels.positive?
|
|
172
|
+
|
|
173
|
+
optional = with_java_errors { @native.tryReceive }
|
|
174
|
+
return ruby_parts(optional.get) if optional.isPresent
|
|
175
|
+
|
|
176
|
+
raise IO::TimeoutError, "operation timed out" if recv_deadline_expired?
|
|
177
|
+
|
|
178
|
+
sleep recv_poll_seconds
|
|
179
|
+
end
|
|
180
|
+
ensure
|
|
181
|
+
@recv_deadline = nil
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def dequeue_recv_sentinel
|
|
186
|
+
@recv_sentinels += 1
|
|
187
|
+
nil
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def close
|
|
192
|
+
return if @closed
|
|
193
|
+
|
|
194
|
+
@closed = true
|
|
195
|
+
@peer_connected.resolve(nil)
|
|
196
|
+
@all_peers_gone.resolve(nil)
|
|
197
|
+
@subscriber_joined.resolve(nil)
|
|
198
|
+
with_java_errors { @native&.close }
|
|
199
|
+
@connections.clear
|
|
200
|
+
nil
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
alias stop close
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def closed?
|
|
208
|
+
@closed
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def subscribe(prefix)
|
|
213
|
+
@routing.subscribe(prefix)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def unsubscribe(prefix)
|
|
218
|
+
@routing.unsubscribe(prefix)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def emit_monitor_event(_type, endpoint: nil, detail: nil)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def monitor_queue=(queue)
|
|
227
|
+
@monitor_queue = queue
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def verbose_monitor=(val)
|
|
232
|
+
@verbose_monitor = val
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
private
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def ensure_materialized
|
|
240
|
+
return if @materialized
|
|
241
|
+
|
|
242
|
+
@native = Java.context.socket(SocketType.valueOf(@socket_type.to_s), java_options)
|
|
243
|
+
@materialized = true
|
|
244
|
+
@routing.replay_pending(@native)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def java_message(parts)
|
|
249
|
+
frames = parts.map { |part| part.to_java_bytes }
|
|
250
|
+
return Message.of(frames.first) if frames.size == 1
|
|
251
|
+
|
|
252
|
+
Message.multipart(*frames)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def ruby_parts(message)
|
|
257
|
+
parts = []
|
|
258
|
+
message.partCount.times do |index|
|
|
259
|
+
parts << String.from_java_bytes(message.part(index)).b.freeze
|
|
260
|
+
end
|
|
261
|
+
parts.freeze
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def take_recv_sentinel
|
|
266
|
+
@recv_sentinels -= 1
|
|
267
|
+
nil
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def recv_deadline_expired?
|
|
272
|
+
return false unless @options.read_timeout
|
|
273
|
+
|
|
274
|
+
monotonic_time >= @recv_deadline
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def recv_poll_seconds
|
|
279
|
+
return POLL_SECONDS unless @options.read_timeout
|
|
280
|
+
|
|
281
|
+
[[@recv_deadline - monotonic_time, POLL_SECONDS].min, 0].max
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def monotonic_time
|
|
286
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def wait_connected(count)
|
|
291
|
+
ensure_materialized
|
|
292
|
+
|
|
293
|
+
loop do
|
|
294
|
+
result = @native.waitConnected(count, POLL_DURATION)
|
|
295
|
+
resolve_peer_connected if result.to_i.positive?
|
|
296
|
+
return result
|
|
297
|
+
rescue ::Java::IoOmq::TimeoutException
|
|
298
|
+
return nil if @closed
|
|
299
|
+
rescue ::Java::IoOmq::ClosedException
|
|
300
|
+
return nil
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def resolve_peer_connected
|
|
306
|
+
@connections[:peer] = true
|
|
307
|
+
@peer_connected.resolve(true) unless @peer_connected.resolved?
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def wait_subscribed(count)
|
|
312
|
+
ensure_materialized
|
|
313
|
+
|
|
314
|
+
loop do
|
|
315
|
+
result = @native.waitSubscribed(count, POLL_DURATION)
|
|
316
|
+
return result
|
|
317
|
+
rescue ::Java::IoOmq::TimeoutException
|
|
318
|
+
return nil if @closed
|
|
319
|
+
rescue ::Java::IoOmq::ClosedException
|
|
320
|
+
return nil
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def wait_all_peers_gone
|
|
326
|
+
@peer_connected.wait
|
|
327
|
+
|
|
328
|
+
loop do
|
|
329
|
+
@native.waitConnected(1, POLL_DURATION)
|
|
330
|
+
sleep POLL_SECONDS
|
|
331
|
+
rescue ::Java::IoOmq::TimeoutException
|
|
332
|
+
@connections.clear
|
|
333
|
+
return true
|
|
334
|
+
rescue ::Java::IoOmq::ClosedException
|
|
335
|
+
return nil
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def java_options
|
|
341
|
+
builder = SocketOptions.builder
|
|
342
|
+
apply_socket_options(builder)
|
|
343
|
+
apply_compression_options(builder)
|
|
344
|
+
apply_mechanism(builder)
|
|
345
|
+
builder.build
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def apply_socket_options(builder)
|
|
350
|
+
builder.sendHighWaterMark([integer_option("send_hwm", @options.send_hwm), 0].max)
|
|
351
|
+
builder.receiveHighWaterMark([integer_option("recv_hwm", @options.recv_hwm), 0].max)
|
|
352
|
+
|
|
353
|
+
if @options.linger == Float::INFINITY
|
|
354
|
+
builder.lingerForever
|
|
355
|
+
else
|
|
356
|
+
builder.linger(duration_from_seconds("linger", @options.linger))
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
builder.identity(bytes(@options.identity)) if @options.identity && !@options.identity.empty?
|
|
360
|
+
builder.routerMandatory(!!@options.router_mandatory)
|
|
361
|
+
builder.conflate(!!@options.conflate)
|
|
362
|
+
builder.maxMessageSize(nonnegative_integer("max_message_size", @options.max_message_size)) if @options.max_message_size
|
|
363
|
+
builder.sendBufferSize(nonnegative_integer("sndbuf", @options.sndbuf)) if @options.sndbuf
|
|
364
|
+
builder.receiveBufferSize(nonnegative_integer("rcvbuf", @options.rcvbuf)) if @options.rcvbuf
|
|
365
|
+
builder.onMute(on_mute)
|
|
366
|
+
|
|
367
|
+
apply_duration_option(builder, :heartbeatInterval, "heartbeat_interval", @options.heartbeat_interval)
|
|
368
|
+
apply_duration_option(builder, :heartbeatTtl, "heartbeat_ttl", @options.heartbeat_ttl)
|
|
369
|
+
apply_duration_option(builder, :heartbeatTimeout, "heartbeat_timeout", @options.heartbeat_timeout)
|
|
370
|
+
apply_reconnect_option(builder)
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def apply_reconnect_option(builder)
|
|
375
|
+
reconnect = @options.reconnect_interval
|
|
376
|
+
|
|
377
|
+
case reconnect
|
|
378
|
+
when Range
|
|
379
|
+
builder.reconnectExponential(
|
|
380
|
+
duration_from_seconds("reconnect_interval min", reconnect.begin),
|
|
381
|
+
duration_from_seconds("reconnect_interval max", reconnect.end),
|
|
382
|
+
)
|
|
383
|
+
when nil, false
|
|
384
|
+
builder.reconnectDisabled
|
|
385
|
+
else
|
|
386
|
+
builder.reconnectInterval(duration_from_seconds("reconnect_interval", reconnect))
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def apply_duration_option(builder, method, label, value)
|
|
392
|
+
builder.public_send(method, duration_from_seconds(label, value)) if value
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def apply_compression_options(builder)
|
|
397
|
+
if @compression_options.key?("compression_auto_train")
|
|
398
|
+
builder.compressionAutoTrain(!!@compression_options["compression_auto_train"])
|
|
399
|
+
end
|
|
400
|
+
if (value = @compression_options["compression_threshold"])
|
|
401
|
+
builder.compressionThreshold(nonnegative_integer("compression_threshold", value))
|
|
402
|
+
end
|
|
403
|
+
if (value = @compression_options["compression_level"])
|
|
404
|
+
builder.compressionLevel(integer_option("compression_level", value))
|
|
405
|
+
end
|
|
406
|
+
if (value = @compression_options["compression_dict"])
|
|
407
|
+
builder.compressionDict(bytes(value))
|
|
408
|
+
end
|
|
409
|
+
if (value = @compression_options["compression_dict_capacity"])
|
|
410
|
+
builder.compressionDictCapacity(nonnegative_integer("compression_dict_capacity", value))
|
|
411
|
+
end
|
|
412
|
+
if (value = @compression_options["max_recv_dict_size"])
|
|
413
|
+
builder.maxReceiveDictSize(nonnegative_integer("max_recv_dict_size", value))
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
return unless @compression_options.key?("compression_offload_threshold")
|
|
417
|
+
|
|
418
|
+
value = @compression_options["compression_offload_threshold"]
|
|
419
|
+
if value.nil? || value.to_i.negative?
|
|
420
|
+
builder.noCompressionOffload
|
|
421
|
+
else
|
|
422
|
+
builder.compressionOffloadThreshold(nonnegative_integer("compression_offload_threshold", value))
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def apply_mechanism(builder)
|
|
428
|
+
mech = @options.mechanism
|
|
429
|
+
klass = mech.class.name
|
|
430
|
+
return unless klass&.include?("Curve")
|
|
431
|
+
|
|
432
|
+
require "protocol/zmtp/z85"
|
|
433
|
+
|
|
434
|
+
keypair = CurveKeypair.new(
|
|
435
|
+
z85_key(mech.instance_variable_get(:@permanent_public), "public key"),
|
|
436
|
+
z85_key(mech.instance_variable_get(:@permanent_secret), "secret key"),
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
if mech.instance_variable_get(:@as_server)
|
|
440
|
+
builder.curveServer(keypair)
|
|
441
|
+
else
|
|
442
|
+
builder.curveClient(keypair, z85_key(mech.instance_variable_get(:@server_public), "server key"))
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def z85_key(key, label)
|
|
448
|
+
raw = key&.to_s&.b
|
|
449
|
+
raise ArgumentError, "#{label} must be exactly 32 bytes" unless raw&.bytesize == 32
|
|
450
|
+
|
|
451
|
+
Protocol::ZMTP::Z85.encode(raw)
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def on_mute
|
|
456
|
+
case @options.on_mute&.to_sym
|
|
457
|
+
when :drop_newest, :drop
|
|
458
|
+
OnMute::DROP_NEWEST
|
|
459
|
+
when :drop_oldest
|
|
460
|
+
OnMute::DROP_OLDEST
|
|
461
|
+
else
|
|
462
|
+
OnMute::BLOCK
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def duration_from_seconds(label, value)
|
|
468
|
+
seconds = Float(value)
|
|
469
|
+
raise ArgumentError, "#{label} must be finite and non-negative" unless seconds.finite? && !seconds.negative?
|
|
470
|
+
|
|
471
|
+
Duration.ofNanos((seconds * 1_000_000_000).round)
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def nonnegative_integer(label, value)
|
|
476
|
+
value = integer_option(label, value)
|
|
477
|
+
raise ArgumentError, "#{label} must be non-negative" if value.negative?
|
|
478
|
+
|
|
479
|
+
value
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def integer_option(label, value)
|
|
484
|
+
Integer(value)
|
|
485
|
+
rescue ArgumentError, TypeError
|
|
486
|
+
raise ArgumentError, "#{label} must be an Integer"
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def bytes(value)
|
|
491
|
+
value.to_s.b.to_java_bytes
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def apply_endpoint_options!(opts)
|
|
496
|
+
compression = extract_endpoint_compression_options(opts)
|
|
497
|
+
return if compression.empty?
|
|
498
|
+
|
|
499
|
+
if @materialized
|
|
500
|
+
existing = compression.keys.to_h do |key|
|
|
501
|
+
[key, @compression_options.fetch(key, default_compression_option(key))]
|
|
502
|
+
end
|
|
503
|
+
return if compression == existing
|
|
504
|
+
|
|
505
|
+
raise ArgumentError,
|
|
506
|
+
"Rust backend compression options must be set before first bind/connect"
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
@compression_options.merge!(compression)
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
def extract_endpoint_compression_options(opts)
|
|
514
|
+
out = {}
|
|
515
|
+
|
|
516
|
+
if opts.key?(:level)
|
|
517
|
+
validate_zstd_level!(opts[:level])
|
|
518
|
+
out["compression_level"] = opts[:level]
|
|
519
|
+
end
|
|
520
|
+
out["compression_dict"] = opts[:dict].b if opts.key?(:dict) && opts[:dict]
|
|
521
|
+
|
|
522
|
+
if opts.key?(:auto_dict)
|
|
523
|
+
auto_dict = opts[:auto_dict]
|
|
524
|
+
if auto_dict && opts[:dict]
|
|
525
|
+
raise ArgumentError, "cannot combine auto_dict: and dict:"
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
case auto_dict
|
|
529
|
+
when nil, false
|
|
530
|
+
out["compression_auto_train"] = false
|
|
531
|
+
when true
|
|
532
|
+
out["compression_auto_train"] = true
|
|
533
|
+
when Hash
|
|
534
|
+
if auto_dict.key?(:trigger)
|
|
535
|
+
raise ArgumentError,
|
|
536
|
+
"Rust backend does not support auto_dict: trigger"
|
|
537
|
+
end
|
|
538
|
+
validate_positive!("auto_dict capacity", auto_dict[:capacity]) if auto_dict[:capacity]
|
|
539
|
+
out["compression_auto_train"] = true
|
|
540
|
+
out["compression_dict_capacity"] = auto_dict[:capacity] if auto_dict[:capacity]
|
|
541
|
+
else
|
|
542
|
+
raise TypeError, "auto_dict: must be true, false, or a Hash; got #{auto_dict.class}"
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
out["compression_threshold"] = opts[:compression_threshold] if opts.key?(:compression_threshold)
|
|
547
|
+
out["max_recv_dict_size"] = opts[:max_recv_dict_size] if opts.key?(:max_recv_dict_size)
|
|
548
|
+
if opts.key?(:compression_offload_threshold)
|
|
549
|
+
out["compression_offload_threshold"] = opts[:compression_offload_threshold] || -1
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
out
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def default_compression_option(key)
|
|
557
|
+
key == "compression_auto_train" ? false : nil
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def validate_positive!(label, value)
|
|
562
|
+
return if value.respond_to?(:positive?) && value.positive?
|
|
563
|
+
|
|
564
|
+
raise ArgumentError, "#{label} must be positive"
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def validate_zstd_level!(level)
|
|
569
|
+
return if level.is_a?(Integer) && (-8..4).cover?(level)
|
|
570
|
+
|
|
571
|
+
raise ArgumentError, "zstd compression level must be -8..4, got #{level.inspect}"
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def with_java_errors
|
|
576
|
+
yield
|
|
577
|
+
rescue ::Java::IoOmq::TimeoutException => error
|
|
578
|
+
raise IO::TimeoutError, error.message
|
|
579
|
+
rescue ::Java::IoOmq::ClosedException => error
|
|
580
|
+
raise IOError, error.message
|
|
581
|
+
rescue ::Java::IoOmq::InvalidEndpointException => error
|
|
582
|
+
raise ArgumentError, error.message
|
|
583
|
+
rescue ::Java::IoOmq::OMQException => error
|
|
584
|
+
raise RuntimeError, error.message
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
class RoutingStub
|
|
588
|
+
def initialize(engine)
|
|
589
|
+
@engine = engine
|
|
590
|
+
@pending_subscribe = []
|
|
591
|
+
@pending_join = []
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def subscriber_joined
|
|
596
|
+
@engine.subscriber_joined
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
def subscribe(prefix)
|
|
601
|
+
native = @engine.instance_variable_get(:@native)
|
|
602
|
+
if @engine.instance_variable_get(:@materialized)
|
|
603
|
+
native.subscribe(prefix.b.to_java_bytes)
|
|
604
|
+
else
|
|
605
|
+
@pending_subscribe << prefix.b
|
|
606
|
+
end
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def unsubscribe(prefix)
|
|
611
|
+
@engine.instance_variable_get(:@native).unsubscribe(prefix.b.to_java_bytes)
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
def join(group)
|
|
616
|
+
native = @engine.instance_variable_get(:@native)
|
|
617
|
+
if @engine.instance_variable_get(:@materialized)
|
|
618
|
+
native.join(group.b.to_java_bytes)
|
|
619
|
+
else
|
|
620
|
+
@pending_join << group.b
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
def leave(group)
|
|
626
|
+
@engine.instance_variable_get(:@native).leave(group.b.to_java_bytes)
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
def replay_pending(native)
|
|
631
|
+
@pending_subscribe.each { |prefix| native.subscribe(prefix.to_java_bytes) }
|
|
632
|
+
@pending_subscribe.clear
|
|
633
|
+
@pending_join.each { |group| native.join(group.to_java_bytes) }
|
|
634
|
+
@pending_join.clear
|
|
635
|
+
end
|
|
636
|
+
end
|
|
637
|
+
end
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
Engine = Java::Engine
|
|
641
|
+
end
|
|
642
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbconfig"
|
|
4
|
+
|
|
5
|
+
require_relative "../version"
|
|
6
|
+
|
|
7
|
+
module OMQ
|
|
8
|
+
module Rust
|
|
9
|
+
module Java
|
|
10
|
+
class UnsupportedPlatformError < LoadError; end
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def classifier
|
|
14
|
+
override = ENV.fetch("OMQ_JAVA_CLASSIFIER", "").strip
|
|
15
|
+
return validate_classifier(override) unless override.empty?
|
|
16
|
+
|
|
17
|
+
classifier_for(os_name: host_os_name, os_arch: host_os_arch)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def classifier_for(os_name:, os_arch:)
|
|
22
|
+
os = normalize_os(os_name)
|
|
23
|
+
arch = normalize_arch(os_arch)
|
|
24
|
+
|
|
25
|
+
classifier = "#{os}-#{arch}" if os && arch
|
|
26
|
+
return classifier if OMQ::Rust::OMQ_JAVA_CLASSIFIERS.include?(classifier)
|
|
27
|
+
|
|
28
|
+
raise UnsupportedPlatformError,
|
|
29
|
+
"unsupported OMQ.java platform: #{os_name.inspect}/#{os_arch.inspect} " \
|
|
30
|
+
"(supported: #{OMQ::Rust::OMQ_JAVA_CLASSIFIERS.join(", ")})"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def validate_classifier(classifier)
|
|
38
|
+
return classifier if OMQ::Rust::OMQ_JAVA_CLASSIFIERS.include?(classifier)
|
|
39
|
+
|
|
40
|
+
raise UnsupportedPlatformError,
|
|
41
|
+
"unsupported OMQ.java classifier: #{classifier.inspect} " \
|
|
42
|
+
"(supported: #{OMQ::Rust::OMQ_JAVA_CLASSIFIERS.join(", ")})"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def host_os_name
|
|
47
|
+
java_system_property("os.name") ||
|
|
48
|
+
RbConfig::CONFIG["host_os"] ||
|
|
49
|
+
RbConfig::CONFIG["target_os"] ||
|
|
50
|
+
RUBY_PLATFORM
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def host_os_arch
|
|
55
|
+
java_system_property("os.arch") ||
|
|
56
|
+
RbConfig::CONFIG["host_cpu"] ||
|
|
57
|
+
RbConfig::CONFIG["target_cpu"] ||
|
|
58
|
+
RUBY_PLATFORM
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def java_system_property(name)
|
|
63
|
+
return unless RUBY_ENGINE == "jruby"
|
|
64
|
+
|
|
65
|
+
require "java"
|
|
66
|
+
::Java::JavaLang::System.get_property(name)
|
|
67
|
+
rescue LoadError, NameError
|
|
68
|
+
nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def normalize_os(value)
|
|
73
|
+
case value.to_s.downcase
|
|
74
|
+
when /linux/
|
|
75
|
+
"linux"
|
|
76
|
+
when /darwin|mac\s*os|macos/
|
|
77
|
+
"macos"
|
|
78
|
+
when /windows|mswin|mingw|cygwin/
|
|
79
|
+
"windows"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def normalize_arch(value)
|
|
85
|
+
case value.to_s.downcase
|
|
86
|
+
when "amd64", "x64", "x86-64", "x86_64"
|
|
87
|
+
"x86_64"
|
|
88
|
+
when "aarch64", "arm64"
|
|
89
|
+
"aarch64"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OMQ
|
|
4
|
+
module Backend
|
|
5
|
+
module Rust
|
|
6
|
+
VERSION = "0.3.0"
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module Rust
|
|
11
|
+
OMQ_JAVA_VERSION = "0.3.0"
|
|
12
|
+
OMQ_JAVA_CLASSIFIERS = [
|
|
13
|
+
"linux-x86_64",
|
|
14
|
+
"macos-aarch64",
|
|
15
|
+
"macos-x86_64",
|
|
16
|
+
"windows-x86_64",
|
|
17
|
+
].freeze
|
|
18
|
+
end
|
|
19
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: omq-backend-rust
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.0
|
|
5
|
+
platform: java
|
|
6
|
+
authors:
|
|
7
|
+
- Patrik Wenger
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: omq
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.28'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.28'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: jar-dependencies
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 0.5.7
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 0.5.7
|
|
40
|
+
description: Drop-in Rust backend for OMQ on JRuby. Same socket API (REQ/REP, PUB/SUB,
|
|
41
|
+
PUSH/PULL, DEALER/ROUTER, and all draft types), but networking runs through the
|
|
42
|
+
OMQ.java Maven artifact.
|
|
43
|
+
email:
|
|
44
|
+
- paddor@gmail.com
|
|
45
|
+
executables: []
|
|
46
|
+
extensions: []
|
|
47
|
+
extra_rdoc_files: []
|
|
48
|
+
files:
|
|
49
|
+
- CHANGELOG.md
|
|
50
|
+
- LICENSE
|
|
51
|
+
- README.md
|
|
52
|
+
- lib/omq/backend/rust.rb
|
|
53
|
+
- lib/omq/rust/java/engine.rb
|
|
54
|
+
- lib/omq/rust/java/platform.rb
|
|
55
|
+
- lib/omq/rust/version.rb
|
|
56
|
+
homepage: https://github.com/zeromq/omq.rb/tree/main/gems/omq-backend-rust
|
|
57
|
+
licenses:
|
|
58
|
+
- ISC
|
|
59
|
+
metadata: {}
|
|
60
|
+
rdoc_options: []
|
|
61
|
+
require_paths:
|
|
62
|
+
- lib
|
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '3.3'
|
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '0'
|
|
73
|
+
requirements:
|
|
74
|
+
- jar io.github.paddor:omq-java:linux-x86_64, 0.3.0
|
|
75
|
+
- jar io.github.paddor:omq-java:macos-aarch64, 0.3.0
|
|
76
|
+
- jar io.github.paddor:omq-java:macos-x86_64, 0.3.0
|
|
77
|
+
- jar io.github.paddor:omq-java:windows-x86_64, 0.3.0
|
|
78
|
+
rubygems_version: 4.0.16
|
|
79
|
+
specification_version: 4
|
|
80
|
+
summary: Rust-backed engine for OMQ using OMQ.java on JRuby
|
|
81
|
+
test_files: []
|