amq-protocol 2.3.3 → 2.8.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.
- checksums.yaml +4 -4
- data/ChangeLog.md +85 -2
- data/LICENSE +1 -1
- data/README.md +2 -7
- data/lib/amq/bit_set.rb +2 -1
- data/lib/amq/endianness.rb +2 -0
- data/lib/amq/int_allocator.rb +3 -3
- data/lib/amq/pack.rb +33 -42
- data/lib/amq/protocol/channel_close.rb +24 -0
- data/lib/amq/protocol/client.rb +31 -40
- data/lib/amq/protocol/constants.rb +2 -0
- data/lib/amq/protocol/exceptions.rb +3 -1
- data/lib/amq/protocol/float_32bit.rb +2 -0
- data/lib/amq/protocol/frame.rb +16 -10
- data/lib/amq/protocol/table.rb +20 -17
- data/lib/amq/protocol/table_value_decoder.rb +49 -53
- data/lib/amq/protocol/table_value_encoder.rb +2 -1
- data/lib/amq/protocol/type_constants.rb +1 -0
- data/lib/amq/protocol/version.rb +1 -1
- data/lib/amq/protocol.rb +1 -0
- data/lib/amq/settings.rb +1 -0
- data/lib/amq/uri.rb +30 -17
- metadata +5 -45
- data/.github/ISSUE_TEMPLATE.md +0 -18
- data/.github/workflows/ci.yml +0 -31
- data/.gitignore +0 -18
- data/.gitmodules +0 -3
- data/.rspec +0 -1
- data/.travis.yml +0 -17
- data/Gemfile +0 -22
- data/Rakefile +0 -55
- data/amq-protocol.gemspec +0 -27
- data/benchmarks/int_allocator.rb +0 -34
- data/benchmarks/pure/body_framing_with_256k_payload.rb +0 -28
- data/benchmarks/pure/body_framing_with_2k_payload.rb +0 -28
- data/codegen/__init__.py +0 -0
- data/codegen/amqp_0.9.1_changes.json +0 -1
- data/codegen/codegen.py +0 -151
- data/codegen/codegen_helpers.py +0 -162
- data/codegen/protocol.rb.pytemplate +0 -320
- data/generate.rb +0 -24
- data/profiling/README.md +0 -9
- data/profiling/stackprof/body_framing_with_2k_payload.rb +0 -33
- data/spec/amq/bit_set_spec.rb +0 -227
- data/spec/amq/int_allocator_spec.rb +0 -113
- data/spec/amq/pack_spec.rb +0 -68
- data/spec/amq/protocol/basic_spec.rb +0 -325
- data/spec/amq/protocol/blank_body_encoding_spec.rb +0 -9
- data/spec/amq/protocol/channel_spec.rb +0 -127
- data/spec/amq/protocol/confirm_spec.rb +0 -41
- data/spec/amq/protocol/connection_spec.rb +0 -146
- data/spec/amq/protocol/constants_spec.rb +0 -10
- data/spec/amq/protocol/exchange_spec.rb +0 -106
- data/spec/amq/protocol/frame_spec.rb +0 -92
- data/spec/amq/protocol/method_spec.rb +0 -43
- data/spec/amq/protocol/queue_spec.rb +0 -126
- data/spec/amq/protocol/table_spec.rb +0 -259
- data/spec/amq/protocol/tx_spec.rb +0 -55
- data/spec/amq/protocol/value_decoder_spec.rb +0 -86
- data/spec/amq/protocol/value_encoder_spec.rb +0 -140
- data/spec/amq/protocol_spec.rb +0 -812
- data/spec/amq/settings_spec.rb +0 -22
- data/spec/amq/uri_parsing_spec.rb +0 -280
- data/spec/spec_helper.rb +0 -29
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7cd695fb0f7f7cc8c9d80109f349fdba92c500b5b8eea4ff6ea2c5e6dfdddd8d
|
|
4
|
+
data.tar.gz: 929be34e6f1bd9c2f84fded2933e9f3775286e19babbabac45ce0ccab1b3c38d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 990e85acbfc80b8dbfa1be036705afdef1f96fdae2b1c8270ff00c972483925361d9dbb7807309fca6a0db55c3d795f8da34d3255f153ff463cda2d12ceed918
|
|
7
|
+
data.tar.gz: 62f8b49269c978f162a21aced9eb22702ae676e28589da94f66b1560083c66878584a1999fa0611f4be1f205a402539b228f0d165107dc930d7a7112801cb8aa
|
data/ChangeLog.md
CHANGED
|
@@ -1,14 +1,97 @@
|
|
|
1
|
+
## Changes between 2.8.0 and 2.9.0 (unreleased)
|
|
2
|
+
|
|
3
|
+
No changes yet.
|
|
4
|
+
|
|
5
|
+
## Changes between 2.7.0 and 2.8.0 (Apr 25, 2026)
|
|
6
|
+
|
|
7
|
+
### Performance Improvements in Frame Decoding and Encoding
|
|
8
|
+
|
|
9
|
+
Replacing `x == nil` with `x.nil?` in the frame layer hot path yields a
|
|
10
|
+
consistent **+15–18% throughput improvement** in `Frame.decode_header`
|
|
11
|
+
(called on every received frame) and **+12–14%** in `HeartbeatFrame.encode`,
|
|
12
|
+
across Ruby 3.3, 3.4, and 4.0.
|
|
13
|
+
|
|
14
|
+
See benchmarks/BENCHMARKS.md for instructions on how to reproduce these numbers on your machine.
|
|
15
|
+
|
|
16
|
+
Contributed by @eglitobias.
|
|
17
|
+
|
|
18
|
+
GitHub issue: [#86](https://github.com/ruby-amqp/amq-protocol/pull/86)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Changes between 2.6.0 and 2.7.0 (Mar 31, 2026)
|
|
22
|
+
|
|
23
|
+
### Channel.Close Predicates Now Return True Boolean
|
|
24
|
+
|
|
25
|
+
And not just truthy values (values that are not `nil` or `false`).
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## Changes between 2.5.1 and 2.6.0 (Mar 30, 2026)
|
|
29
|
+
|
|
30
|
+
### Channel.Close Predicate Methods
|
|
31
|
+
|
|
32
|
+
`Channel::Close` now provides predicate methods for identifying common
|
|
33
|
+
channel closure reasons by reply code and text:
|
|
34
|
+
|
|
35
|
+
* `#delivery_ack_timeout?`: consumer [delivery acknowledgement timeout](https://www.rabbitmq.com/docs/consumers#acknowledgement-timeout)
|
|
36
|
+
* `#unknown_delivery_tag?`: unknown delivery tag (e.g. [double ack](https://www.rabbitmq.com/docs/channels#error-handling))
|
|
37
|
+
* `#message_too_large?`: message exceeded the configured max size
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## Changes between 2.5.0 and 2.5.1 (Jan 19, 2026)
|
|
41
|
+
|
|
42
|
+
### Windows Installation Fixes
|
|
43
|
+
|
|
44
|
+
`2.4.0` and `2.5.0` versions unintentionally included files that were
|
|
45
|
+
symlinks, which caused installation on Windows to fail.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Changes between 2.4.0 and 2.5.0 (Dec 31, 2025)
|
|
49
|
+
|
|
50
|
+
### Additional Consumer Hot Path Optimizations
|
|
51
|
+
|
|
52
|
+
A few more decode method optimizations for consumer delivery code paths.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
## Changes between 2.3.4 and 2.4.0 (Dec 30, 2025)
|
|
56
|
+
|
|
57
|
+
### Performance Improvements
|
|
58
|
+
|
|
59
|
+
Optimized encoding and decoding hot paths:
|
|
60
|
+
|
|
61
|
+
* Built-in `Q>`/`q>` packing/unpacking directives are 6-7x faster than the original implementation (that originally targeted Ruby 1.8.x)
|
|
62
|
+
* Switched to `unpack1` instead of `unpack().first` throughout
|
|
63
|
+
* Use `byteslice` instead of `slice` for binary string operations
|
|
64
|
+
* Use `getbyte` for single byte access (4x faster than alternatives)
|
|
65
|
+
* Adopted the `frozen_string_literal` pragma
|
|
66
|
+
|
|
67
|
+
The improvements on Ruby 3.4 are very meaningful:
|
|
68
|
+
|
|
69
|
+
* `AMQ::Pack.pack_uint64_big_endian`: about 6.6x faster
|
|
70
|
+
* `AMQ::Pack.unpack_uint64_big_endian`: about 7.2x faster
|
|
71
|
+
* `Basic.Deliver.decode`: about 1.7x faster
|
|
72
|
+
* `Basic.Ack/Nack/Reject.encode`: about 2.5x faster
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
## Changes between 2.3.3 and 2.3.4 (May 12, 2025)
|
|
76
|
+
|
|
77
|
+
### (Forward) Compatibility with Ruby 3.5
|
|
78
|
+
|
|
79
|
+
Contributed by @Earlopain.
|
|
80
|
+
|
|
81
|
+
GitHub issue: [#80](https://github.com/ruby-amqp/amq-protocol/pull/80)
|
|
82
|
+
|
|
83
|
+
|
|
1
84
|
## Changes between 2.3.2 and 2.3.3 (February 17, 2025)
|
|
2
85
|
|
|
3
86
|
### Improved Compatibility with Ruby 3.4
|
|
4
87
|
|
|
5
|
-
|
|
88
|
+
Contributed by @BenTalagan.
|
|
6
89
|
|
|
7
90
|
GitHub issue: [#79](https://github.com/ruby-amqp/amq-protocol/pull/79)
|
|
8
91
|
|
|
9
92
|
### Support Binary-Encoded Frozen Strings for Payloads
|
|
10
93
|
|
|
11
|
-
|
|
94
|
+
Contributed by @djrodgerspryor.
|
|
12
95
|
|
|
13
96
|
GitHub issue: [#78](https://github.com/ruby-amqp/amq-protocol/pull/78)
|
|
14
97
|
|
data/LICENSE
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Copyright (c) 2010 – 2011 Jakub Šťastný aka Botanicus
|
|
2
|
-
Copyright (c) 2011 –
|
|
2
|
+
Copyright (c) 2011 – 2026 Michael S. Klishin <michael@defprotocol.org>
|
|
3
3
|
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
5
5
|
a copy of this software and associated documentation files (the
|
data/README.md
CHANGED
|
@@ -9,6 +9,8 @@ needs for you, including RabbitMQ extensions to AMQP 0.9.1.
|
|
|
9
9
|
|
|
10
10
|
## Supported Ruby Versions
|
|
11
11
|
|
|
12
|
+
* amq-protocol `2.3.4` has fixes for Ruby 3.5 compatibility
|
|
13
|
+
* amq-protocol `2.3.3` has fixes for Ruby 3.4 compatibility
|
|
12
14
|
* amq-protocol `2.3.0` only supports Ruby 2.2+.
|
|
13
15
|
* amq-protocol `2.0.0` through `2.2.0` and later supports Ruby 2.0+.
|
|
14
16
|
* amq-protocol `1.9.2` was the last version to support Ruby 1.8 and 1.9.
|
|
@@ -45,21 +47,14 @@ To run tests, use
|
|
|
45
47
|
amq-protocol is maintained by [Michael Klishin](https://github.com/michaelklishin).
|
|
46
48
|
|
|
47
49
|
|
|
48
|
-
## CI Status
|
|
49
|
-
|
|
50
|
-
[](https://travis-ci.org/ruby-amqp/amq-protocol)
|
|
51
|
-
|
|
52
|
-
|
|
53
50
|
## Issues
|
|
54
51
|
|
|
55
52
|
Please report any issues you may find to our [Issue tracker](http://github.com/ruby-amqp/amq-protocol/issues) on GitHub.
|
|
56
53
|
|
|
57
|
-
|
|
58
54
|
## Mailing List
|
|
59
55
|
|
|
60
56
|
Any questions you may have should be sent to the [Ruby AMQP mailing list](http://groups.google.com/group/ruby-amqp).
|
|
61
57
|
|
|
62
|
-
|
|
63
58
|
## License
|
|
64
59
|
|
|
65
60
|
MIT (see LICENSE in the repository root).
|
data/lib/amq/bit_set.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module AMQ
|
|
4
5
|
# Very minimalistic, pure Ruby implementation of bit set. Inspired by java.util.BitSet,
|
|
@@ -112,7 +113,7 @@ module AMQ
|
|
|
112
113
|
|
|
113
114
|
def check_range(i)
|
|
114
115
|
if i < 0 || i >= @nbits
|
|
115
|
-
raise IndexError
|
|
116
|
+
raise IndexError, "Cannot access bit #{i} from a BitSet with #{@nbits} bits"
|
|
116
117
|
end
|
|
117
118
|
end # check_range
|
|
118
119
|
end # BitSet
|
data/lib/amq/endianness.rb
CHANGED
data/lib/amq/int_allocator.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
require "amq/bit_set"
|
|
4
5
|
|
|
@@ -25,13 +26,12 @@ module AMQ
|
|
|
25
26
|
# @param [Integer] hi Upper boundary of the integer range available for allocation
|
|
26
27
|
# @raise [ArgumentError] if upper boundary is not greater than the lower one
|
|
27
28
|
def initialize(lo, hi)
|
|
28
|
-
raise ArgumentError
|
|
29
|
+
raise ArgumentError, "upper boundary must be greater than the lower one (given: hi = #{hi}, lo = #{lo})" unless hi > lo
|
|
29
30
|
|
|
30
31
|
@hi = hi
|
|
31
32
|
@lo = lo
|
|
32
33
|
|
|
33
34
|
@number_of_bits = hi - lo
|
|
34
|
-
@range = Range.new(1, @number_of_bits)
|
|
35
35
|
@free_set = BitSet.new(@number_of_bits)
|
|
36
36
|
end # initialize(hi, lo)
|
|
37
37
|
|
|
@@ -46,7 +46,7 @@ module AMQ
|
|
|
46
46
|
|
|
47
47
|
if n = @free_set.next_clear_bit
|
|
48
48
|
|
|
49
|
-
if n < @hi - 1
|
|
49
|
+
if n < @hi - 1
|
|
50
50
|
@free_set.set(n)
|
|
51
51
|
n + 1
|
|
52
52
|
else
|
data/lib/amq/pack.rb
CHANGED
|
@@ -1,54 +1,45 @@
|
|
|
1
1
|
# encoding: binary
|
|
2
|
-
|
|
3
|
-
require 'amq/endianness'
|
|
2
|
+
# frozen_string_literal: true
|
|
4
3
|
|
|
5
4
|
module AMQ
|
|
6
|
-
# Implements pack to/unpack from 64bit string in network byte order
|
|
7
|
-
#
|
|
5
|
+
# Implements pack to/unpack from 64bit string in network byte order.
|
|
6
|
+
# Uses native Ruby pack directives with explicit endianness (Ruby 1.9.3+).
|
|
8
7
|
module Pack
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def self.pack_int16_big_endian(short)
|
|
23
|
-
[long_long].pack(INT16)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def self.unpack_int16_big_endian(data)
|
|
27
|
-
data.unpack(INT16)
|
|
28
|
-
end
|
|
29
|
-
else
|
|
30
|
-
def self.pack_uint64_big_endian(long_long)
|
|
31
|
-
result = [long_long].pack(UINT64)
|
|
32
|
-
result.bytes.to_a.reverse.map(&:chr).join
|
|
33
|
-
end
|
|
8
|
+
# Pack format strings - frozen for performance
|
|
9
|
+
UINT64_BE = 'Q>'.freeze # 64-bit unsigned, big-endian
|
|
10
|
+
INT64_BE = 'q>'.freeze # 64-bit signed, big-endian
|
|
11
|
+
INT16_BE = 's>'.freeze # 16-bit signed, big-endian
|
|
12
|
+
UINT16_BE = 'n'.freeze # 16-bit unsigned, big-endian (network order)
|
|
13
|
+
|
|
14
|
+
# Packs a 64-bit unsigned integer to big-endian binary string.
|
|
15
|
+
# @param long_long [Integer] The value to pack
|
|
16
|
+
# @return [String] 8-byte binary string in big-endian order
|
|
17
|
+
def self.pack_uint64_big_endian(long_long)
|
|
18
|
+
[long_long].pack(UINT64_BE)
|
|
19
|
+
end
|
|
34
20
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
21
|
+
# Unpacks a big-endian binary string to a 64-bit unsigned integer.
|
|
22
|
+
# @param data [String] 8-byte binary string in big-endian order
|
|
23
|
+
# @return [Array<Integer>] Single-element array containing the unpacked value
|
|
24
|
+
def self.unpack_uint64_big_endian(data)
|
|
25
|
+
data.unpack(UINT64_BE)
|
|
26
|
+
end
|
|
39
27
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
28
|
+
# Packs a 16-bit signed integer to big-endian binary string.
|
|
29
|
+
# @param short [Integer] The value to pack
|
|
30
|
+
# @return [String] 2-byte binary string in big-endian order
|
|
31
|
+
def self.pack_int16_big_endian(short)
|
|
32
|
+
[short].pack(INT16_BE)
|
|
33
|
+
end
|
|
44
34
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
35
|
+
# Unpacks a big-endian binary string to a 16-bit signed integer.
|
|
36
|
+
# @param data [String] 2-byte binary string in big-endian order
|
|
37
|
+
# @return [Array<Integer>] Single-element array containing the unpacked value
|
|
38
|
+
def self.unpack_int16_big_endian(data)
|
|
39
|
+
data.unpack(INT16_BE)
|
|
49
40
|
end
|
|
50
41
|
end
|
|
51
42
|
|
|
52
|
-
# Backwards compatibility
|
|
43
|
+
# Backwards compatibility alias
|
|
53
44
|
Hacks = Pack
|
|
54
45
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AMQ
|
|
4
|
+
module Protocol
|
|
5
|
+
class Channel
|
|
6
|
+
class Close
|
|
7
|
+
# @return [Boolean] true if the channel was closed due to a consumer delivery acknowledgement timeout
|
|
8
|
+
def delivery_ack_timeout?
|
|
9
|
+
reply_code == 406 && reply_text.match?(/delivery acknowledgement on channel \d+ timed out/)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @return [Boolean] true if the channel was closed due to an unknown delivery tag (e.g. double ack)
|
|
13
|
+
def unknown_delivery_tag?
|
|
14
|
+
reply_code == 406 && reply_text.match?(/unknown delivery tag/)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @return [Boolean] true if the channel was closed because a message exceeded the configured max size
|
|
18
|
+
def message_too_large?
|
|
19
|
+
reply_code == 406 && reply_text.match?(/larger than configured max size/)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/amq/protocol/client.rb
CHANGED
|
@@ -173,9 +173,7 @@ module AMQ
|
|
|
173
173
|
limit = frame_size - 8
|
|
174
174
|
return [BodyFrame.new(body, channel)] if body.bytesize < limit
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
# and we need bytes. MK.
|
|
178
|
-
body.force_encoding("ASCII-8BIT") if RUBY_VERSION.to_f >= 1.9 && body.encoding != Encoding::BINARY
|
|
176
|
+
body.force_encoding("ASCII-8BIT") if body.encoding != Encoding::BINARY
|
|
179
177
|
|
|
180
178
|
array = Array.new
|
|
181
179
|
while body && !body.empty?
|
|
@@ -1569,30 +1567,31 @@ module AMQ
|
|
|
1569
1567
|
0x0004,
|
|
1570
1568
|
]
|
|
1571
1569
|
|
|
1570
|
+
# Optimized decode_properties using getbyte and unpack1
|
|
1572
1571
|
def self.decode_properties(data)
|
|
1573
1572
|
offset, data_length, properties = 0, data.bytesize, {}
|
|
1574
1573
|
|
|
1575
|
-
compressed_index = data
|
|
1574
|
+
compressed_index = data.byteslice(offset, 2).unpack1(PACK_UINT16)
|
|
1576
1575
|
offset += 2
|
|
1577
1576
|
while data_length > offset
|
|
1578
1577
|
DECODE_PROPERTIES_KEYS.each do |key|
|
|
1579
1578
|
next unless compressed_index >= key
|
|
1580
1579
|
compressed_index -= key
|
|
1581
|
-
name = DECODE_PROPERTIES[key] || raise(RuntimeError.new("No property found for index #{
|
|
1580
|
+
name = DECODE_PROPERTIES[key] || raise(RuntimeError.new("No property found for index #{key.inspect}!"))
|
|
1582
1581
|
case DECODE_PROPERTIES_TYPE[key]
|
|
1583
1582
|
when :shortstr
|
|
1584
|
-
size = data
|
|
1583
|
+
size = data.getbyte(offset)
|
|
1585
1584
|
offset += 1
|
|
1586
|
-
result = data
|
|
1585
|
+
result = data.byteslice(offset, size)
|
|
1587
1586
|
when :octet
|
|
1588
1587
|
size = 1
|
|
1589
|
-
result = data
|
|
1588
|
+
result = data.getbyte(offset)
|
|
1590
1589
|
when :timestamp
|
|
1591
1590
|
size = 8
|
|
1592
|
-
result = Time.at(data
|
|
1591
|
+
result = Time.at(data.byteslice(offset, 8).unpack1(PACK_UINT64_BE))
|
|
1593
1592
|
when :table
|
|
1594
|
-
size = 4 + data
|
|
1595
|
-
result = Table.decode(data
|
|
1593
|
+
size = 4 + data.byteslice(offset, 4).unpack1(PACK_UINT32)
|
|
1594
|
+
result = Table.decode(data.byteslice(offset, size))
|
|
1596
1595
|
end
|
|
1597
1596
|
properties[name] = result
|
|
1598
1597
|
offset += size
|
|
@@ -1688,13 +1687,11 @@ module AMQ
|
|
|
1688
1687
|
@index = 0x003C0015 # 60, 21, 3932181
|
|
1689
1688
|
@packed_indexes = [60, 21].pack(PACK_UINT16_X2).freeze
|
|
1690
1689
|
|
|
1690
|
+
# Optimized decode using getbyte
|
|
1691
1691
|
# @return
|
|
1692
1692
|
def self.decode(data)
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
offset += 1
|
|
1696
|
-
consumer_tag = data[offset, length]
|
|
1697
|
-
offset += length
|
|
1693
|
+
length = data.getbyte(0)
|
|
1694
|
+
consumer_tag = data.byteslice(1, length)
|
|
1698
1695
|
self.new(consumer_tag)
|
|
1699
1696
|
end
|
|
1700
1697
|
|
|
@@ -1866,26 +1863,25 @@ module AMQ
|
|
|
1866
1863
|
@index = 0x003C003C # 60, 60, 3932220
|
|
1867
1864
|
@packed_indexes = [60, 60].pack(PACK_UINT16_X2).freeze
|
|
1868
1865
|
|
|
1866
|
+
# Optimized decode using getbyte and unpack1 for better performance
|
|
1869
1867
|
# @return
|
|
1870
1868
|
def self.decode(data)
|
|
1871
|
-
offset =
|
|
1872
|
-
length = data
|
|
1869
|
+
offset = 0
|
|
1870
|
+
length = data.getbyte(offset)
|
|
1873
1871
|
offset += 1
|
|
1874
|
-
consumer_tag = data
|
|
1872
|
+
consumer_tag = data.byteslice(offset, length)
|
|
1875
1873
|
offset += length
|
|
1876
|
-
delivery_tag =
|
|
1874
|
+
delivery_tag = data.byteslice(offset, 8).unpack1(PACK_UINT64_BE)
|
|
1877
1875
|
offset += 8
|
|
1878
|
-
|
|
1876
|
+
redelivered = (data.getbyte(offset) & 1) != 0
|
|
1879
1877
|
offset += 1
|
|
1880
|
-
|
|
1881
|
-
length = data[offset, 1].unpack(PACK_CHAR).first
|
|
1878
|
+
length = data.getbyte(offset)
|
|
1882
1879
|
offset += 1
|
|
1883
|
-
exchange = data
|
|
1880
|
+
exchange = data.byteslice(offset, length)
|
|
1884
1881
|
offset += length
|
|
1885
|
-
length = data
|
|
1882
|
+
length = data.getbyte(offset)
|
|
1886
1883
|
offset += 1
|
|
1887
|
-
routing_key = data
|
|
1888
|
-
offset += length
|
|
1884
|
+
routing_key = data.byteslice(offset, length)
|
|
1889
1885
|
self.new(consumer_tag, delivery_tag, redelivered, exchange, routing_key)
|
|
1890
1886
|
end
|
|
1891
1887
|
|
|
@@ -2009,14 +2005,11 @@ module AMQ
|
|
|
2009
2005
|
@index = 0x003C0050 # 60, 80, 3932240
|
|
2010
2006
|
@packed_indexes = [60, 80].pack(PACK_UINT16_X2).freeze
|
|
2011
2007
|
|
|
2008
|
+
# Optimized decode using unpack1 and getbyte
|
|
2012
2009
|
# @return
|
|
2013
2010
|
def self.decode(data)
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
offset += 8
|
|
2017
|
-
bit_buffer = data[offset, 1].unpack(PACK_CHAR).first
|
|
2018
|
-
offset += 1
|
|
2019
|
-
multiple = (bit_buffer & (1 << 0)) != 0
|
|
2011
|
+
delivery_tag = data.byteslice(0, 8).unpack1(PACK_UINT64_BE)
|
|
2012
|
+
multiple = (data.getbyte(8) & 1) != 0
|
|
2020
2013
|
self.new(delivery_tag, multiple)
|
|
2021
2014
|
end
|
|
2022
2015
|
|
|
@@ -2141,15 +2134,13 @@ module AMQ
|
|
|
2141
2134
|
@index = 0x003C0078 # 60, 120, 3932280
|
|
2142
2135
|
@packed_indexes = [60, 120].pack(PACK_UINT16_X2).freeze
|
|
2143
2136
|
|
|
2137
|
+
# Optimized decode using unpack1 and getbyte
|
|
2144
2138
|
# @return
|
|
2145
2139
|
def self.decode(data)
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
offset += 1
|
|
2151
|
-
multiple = (bit_buffer & (1 << 0)) != 0
|
|
2152
|
-
requeue = (bit_buffer & (1 << 1)) != 0
|
|
2140
|
+
delivery_tag = data.byteslice(0, 8).unpack1(PACK_UINT64_BE)
|
|
2141
|
+
bit_buffer = data.getbyte(8)
|
|
2142
|
+
multiple = (bit_buffer & 1) != 0
|
|
2143
|
+
requeue = (bit_buffer & 2) != 0
|
|
2153
2144
|
self.new(delivery_tag, multiple, requeue)
|
|
2154
2145
|
end
|
|
2155
2146
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module AMQ
|
|
2
4
|
module Protocol
|
|
3
5
|
class Error < StandardError
|
|
@@ -41,7 +43,7 @@ module AMQ
|
|
|
41
43
|
|
|
42
44
|
class BadResponseError < Protocol::Error
|
|
43
45
|
def initialize(argument, expected, actual)
|
|
44
|
-
super("Argument #{argument} has to be #{expected.inspect}, was #{
|
|
46
|
+
super("Argument #{argument} has to be #{expected.inspect}, was #{actual.inspect}")
|
|
45
47
|
end
|
|
46
48
|
end
|
|
47
49
|
|
data/lib/amq/protocol/frame.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: binary
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
module AMQ
|
|
4
5
|
module Protocol
|
|
@@ -9,6 +10,9 @@ module AMQ
|
|
|
9
10
|
CHANNEL_RANGE = (0..65535).freeze
|
|
10
11
|
FINAL_OCTET = "\xCE".freeze # 206
|
|
11
12
|
|
|
13
|
+
# Pack format for 64-bit unsigned big-endian
|
|
14
|
+
PACK_UINT64_BE = 'Q>'.freeze
|
|
15
|
+
|
|
12
16
|
def self.encoded_payload(payload)
|
|
13
17
|
if payload.respond_to?(:force_encoding) && payload.encoding.name != 'BINARY'
|
|
14
18
|
# Only copy if we have to.
|
|
@@ -19,8 +23,8 @@ module AMQ
|
|
|
19
23
|
|
|
20
24
|
# The channel number is 0 for all frames which are global to the connection and 1-65535 for frames that refer to specific channels.
|
|
21
25
|
def self.encode_to_array(type, payload, channel)
|
|
22
|
-
raise RuntimeError
|
|
23
|
-
raise RuntimeError
|
|
26
|
+
raise RuntimeError, "Channel has to be 0 or an integer in range 1..65535 but was #{channel.inspect}" unless CHANNEL_RANGE.include?(channel)
|
|
27
|
+
raise RuntimeError, "Payload can't be nil" if payload.nil?
|
|
24
28
|
components = []
|
|
25
29
|
components << [find_type(type), channel, payload.bytesize].pack(PACK_CHAR_UINT16_UINT32)
|
|
26
30
|
components << encoded_payload(payload)
|
|
@@ -43,25 +47,27 @@ module AMQ
|
|
|
43
47
|
end
|
|
44
48
|
|
|
45
49
|
def self.find_type(type)
|
|
46
|
-
type_id =
|
|
47
|
-
raise FrameTypeError
|
|
50
|
+
type_id = type.is_a?(Symbol) ? TYPES[type] : type
|
|
51
|
+
raise FrameTypeError, TYPES_OPTIONS if type.nil? || !TYPES_REVERSE.key?(type_id)
|
|
48
52
|
type_id
|
|
49
53
|
end
|
|
50
54
|
|
|
51
55
|
def self.decode(*)
|
|
52
|
-
raise NotImplementedError
|
|
56
|
+
raise NotImplementedError, <<-EOF
|
|
53
57
|
You are supposed to redefine this method, because it's dependent on used IO adapter.
|
|
54
58
|
|
|
55
59
|
This functionality is part of the https://github.com/ruby-amqp/amq-client library.
|
|
56
60
|
EOF
|
|
57
61
|
end
|
|
58
62
|
|
|
63
|
+
# Optimized header decode using unpack1 for single values where appropriate
|
|
59
64
|
def self.decode_header(header)
|
|
60
|
-
raise EmptyResponseError if header
|
|
65
|
+
raise EmptyResponseError if header.nil? || header.empty?
|
|
61
66
|
|
|
67
|
+
# Use unpack for multiple values - this is the optimal approach
|
|
62
68
|
type_id, channel, size = header.unpack(PACK_CHAR_UINT16_UINT32)
|
|
63
69
|
type = TYPES_REVERSE[type_id]
|
|
64
|
-
raise FrameTypeError
|
|
70
|
+
raise FrameTypeError, TYPES_OPTIONS unless type
|
|
65
71
|
[type, channel, size]
|
|
66
72
|
end
|
|
67
73
|
|
|
@@ -130,7 +136,7 @@ This functionality is part of the https://github.com/ruby-amqp/amq-client librar
|
|
|
130
136
|
end # final?
|
|
131
137
|
|
|
132
138
|
def decode_payload
|
|
133
|
-
self.method_class.decode(@payload
|
|
139
|
+
self.method_class.decode(@payload.byteslice(4..-1))
|
|
134
140
|
end
|
|
135
141
|
end
|
|
136
142
|
|
|
@@ -167,8 +173,8 @@ This functionality is part of the https://github.com/ruby-amqp/amq-client librar
|
|
|
167
173
|
# the total size of the content body, that is, the sum of the body sizes for the
|
|
168
174
|
# following content body frames. Zero indicates that there are no content body frames.
|
|
169
175
|
# So this is NOT related to this very header frame!
|
|
170
|
-
@body_size =
|
|
171
|
-
@data = @payload
|
|
176
|
+
@body_size = @payload.byteslice(4, 8).unpack1(PACK_UINT64_BE)
|
|
177
|
+
@data = @payload.byteslice(12..-1)
|
|
172
178
|
@properties = Basic.decode_properties(@data)
|
|
173
179
|
end
|
|
174
180
|
end
|