amq-protocol 2.5.0 → 2.5.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +5 -2
  3. data/lib/amq/protocol/version.rb +1 -1
  4. metadata +3 -55
  5. data/.github/ISSUE_TEMPLATE.md +0 -18
  6. data/.github/workflows/ci.yml +0 -31
  7. data/.gitignore +0 -18
  8. data/.gitmodules +0 -3
  9. data/.rspec +0 -1
  10. data/.travis.yml +0 -17
  11. data/AGENTS.md +0 -23
  12. data/CLAUDE.md +0 -1
  13. data/GEMINI.md +0 -1
  14. data/Gemfile +0 -27
  15. data/Rakefile +0 -55
  16. data/amq-protocol.gemspec +0 -27
  17. data/benchmarks/frame_encoding.rb +0 -75
  18. data/benchmarks/int_allocator.rb +0 -34
  19. data/benchmarks/method_encoding.rb +0 -198
  20. data/benchmarks/pack_unpack.rb +0 -158
  21. data/benchmarks/pure/body_framing_with_256k_payload.rb +0 -28
  22. data/benchmarks/pure/body_framing_with_2k_payload.rb +0 -28
  23. data/benchmarks/run_all.rb +0 -64
  24. data/benchmarks/table_encoding.rb +0 -110
  25. data/codegen/__init__.py +0 -0
  26. data/codegen/amqp_0.9.1_changes.json +0 -1
  27. data/codegen/codegen.py +0 -151
  28. data/codegen/codegen_helpers.py +0 -162
  29. data/codegen/protocol.rb.pytemplate +0 -320
  30. data/generate.rb +0 -24
  31. data/profiling/README.md +0 -9
  32. data/profiling/stackprof/body_framing_with_2k_payload.rb +0 -33
  33. data/spec/amq/bit_set_spec.rb +0 -249
  34. data/spec/amq/endianness_spec.rb +0 -23
  35. data/spec/amq/int_allocator_spec.rb +0 -136
  36. data/spec/amq/pack_spec.rb +0 -58
  37. data/spec/amq/protocol/basic_spec.rb +0 -325
  38. data/spec/amq/protocol/blank_body_encoding_spec.rb +0 -9
  39. data/spec/amq/protocol/channel_spec.rb +0 -127
  40. data/spec/amq/protocol/confirm_spec.rb +0 -41
  41. data/spec/amq/protocol/connection_spec.rb +0 -146
  42. data/spec/amq/protocol/constants_spec.rb +0 -10
  43. data/spec/amq/protocol/exceptions_spec.rb +0 -70
  44. data/spec/amq/protocol/exchange_spec.rb +0 -106
  45. data/spec/amq/protocol/float_32bit_spec.rb +0 -27
  46. data/spec/amq/protocol/frame_spec.rb +0 -156
  47. data/spec/amq/protocol/method_spec.rb +0 -43
  48. data/spec/amq/protocol/queue_spec.rb +0 -126
  49. data/spec/amq/protocol/table_spec.rb +0 -291
  50. data/spec/amq/protocol/tx_spec.rb +0 -55
  51. data/spec/amq/protocol/value_decoder_spec.rb +0 -183
  52. data/spec/amq/protocol/value_encoder_spec.rb +0 -161
  53. data/spec/amq/protocol_spec.rb +0 -812
  54. data/spec/amq/settings_spec.rb +0 -58
  55. data/spec/amq/uri_parsing_spec.rb +0 -287
  56. data/spec/spec_helper.rb +0 -29
@@ -1,158 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: utf-8
3
- # frozen_string_literal: true
4
-
5
- $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
6
-
7
- require "amq/protocol/client"
8
- require "benchmark/ips"
9
-
10
- puts
11
- puts "-" * 80
12
- puts "Pack/Unpack Micro-benchmarks on #{RUBY_DESCRIPTION}"
13
- puts "-" * 80
14
-
15
- # Test data
16
- UINT64_VALUE = 0x123456789ABCDEF0
17
- UINT32_VALUE = 0x12345678
18
- UINT16_VALUE = 0x1234
19
-
20
- PACKED_UINT64_BE = (+"\x12\x34\x56\x78\x9A\xBC\xDE\xF0").force_encoding('BINARY').freeze
21
- PACKED_UINT32_BE = (+"\x12\x34\x56\x78").force_encoding('BINARY').freeze
22
- PACKED_UINT16_BE = (+"\x12\x34").force_encoding('BINARY').freeze
23
-
24
- puts "=== Pack Operations ==="
25
-
26
- Benchmark.ips do |x|
27
- x.config(time: 5, warmup: 2)
28
-
29
- x.report("AMQ::Pack.pack_uint64_big_endian") do
30
- AMQ::Pack.pack_uint64_big_endian(UINT64_VALUE)
31
- end
32
-
33
- # Alternative: direct pack with 'Q>' directive (Ruby 1.9.3+)
34
- x.report("[val].pack('Q>')") do
35
- [UINT64_VALUE].pack('Q>')
36
- end
37
-
38
- x.report("[val].pack('N') uint32") do
39
- [UINT32_VALUE].pack('N')
40
- end
41
-
42
- x.report("[val].pack('n') uint16") do
43
- [UINT16_VALUE].pack('n')
44
- end
45
-
46
- x.compare!
47
- end
48
-
49
- puts
50
- puts "=== Unpack Operations ==="
51
-
52
- Benchmark.ips do |x|
53
- x.config(time: 5, warmup: 2)
54
-
55
- x.report("AMQ::Pack.unpack_uint64_big_endian") do
56
- AMQ::Pack.unpack_uint64_big_endian(PACKED_UINT64_BE)
57
- end
58
-
59
- # Alternative: direct unpack with 'Q>' directive
60
- x.report("data.unpack('Q>')") do
61
- PACKED_UINT64_BE.unpack('Q>')
62
- end
63
-
64
- x.report("data.unpack1('Q>')") do
65
- PACKED_UINT64_BE.unpack1('Q>')
66
- end
67
-
68
- x.report("data.unpack('N').first") do
69
- PACKED_UINT32_BE.unpack('N').first
70
- end
71
-
72
- x.report("data.unpack1('N')") do
73
- PACKED_UINT32_BE.unpack1('N')
74
- end
75
-
76
- x.compare!
77
- end
78
-
79
- puts
80
- puts "=== String Slicing ==="
81
-
82
- DATA = ("x" * 1000).force_encoding('BINARY').freeze
83
-
84
- Benchmark.ips do |x|
85
- x.config(time: 5, warmup: 2)
86
-
87
- x.report("data[offset, length]") do
88
- DATA[100, 50]
89
- end
90
-
91
- x.report("data.byteslice(offset, length)") do
92
- DATA.byteslice(100, 50)
93
- end
94
-
95
- x.report("data.slice(offset, length)") do
96
- DATA.slice(100, 50)
97
- end
98
-
99
- x.compare!
100
- end
101
-
102
- puts
103
- puts "=== Single Byte Access ==="
104
-
105
- Benchmark.ips do |x|
106
- x.config(time: 5, warmup: 2)
107
-
108
- x.report("data[0, 1].unpack('C').first") do
109
- DATA[0, 1].unpack('C').first
110
- end
111
-
112
- x.report("data.getbyte(0)") do
113
- DATA.getbyte(0)
114
- end
115
-
116
- x.report("data[0].ord") do
117
- DATA[0].ord
118
- end
119
-
120
- x.report("data.unpack1('C')") do
121
- DATA.unpack1('C')
122
- end
123
-
124
- x.compare!
125
- end
126
-
127
- puts
128
- puts "=== Buffer Building ==="
129
-
130
- Benchmark.ips do |x|
131
- x.config(time: 5, warmup: 2)
132
-
133
- x.report("String.new + <<") do
134
- buf = String.new
135
- buf << "hello"
136
- buf << "world"
137
- buf << [1234].pack('N')
138
- buf
139
- end
140
-
141
- x.report("+'' + <<") do
142
- buf = +''
143
- buf << "hello"
144
- buf << "world"
145
- buf << [1234].pack('N')
146
- buf
147
- end
148
-
149
- x.report("Array#join") do
150
- parts = []
151
- parts << "hello"
152
- parts << "world"
153
- parts << [1234].pack('N')
154
- parts.join
155
- end
156
-
157
- x.compare!
158
- end
@@ -1,28 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: utf-8
3
-
4
- $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib"))
5
-
6
- require "amq/protocol/client"
7
- require "benchmark"
8
-
9
- FRAME_SIZE = 128 * 1024
10
-
11
- puts
12
- puts "-" * 80
13
- puts "Benchmarking on #{RUBY_DESCRIPTION}"
14
-
15
- n = 250_000
16
-
17
- # warm up the JIT, etc
18
- puts "Doing a warmup run..."
19
- 15_000.times { AMQ::Protocol::Method.encode_body("a" * 256 * 1024, 1, FRAME_SIZE) }
20
-
21
- t = Benchmark.realtime do
22
- n.times { AMQ::Protocol::Method.encode_body("a" * 256 * 1024, 1, FRAME_SIZE) }
23
- end
24
- r = (n.to_f/t.to_f)
25
-
26
- puts "AMQ::Protocol::Method.encode_body rate: #{(r / 1000).round(2)} KGHz"
27
- puts
28
- puts "-" * 80
@@ -1,28 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: utf-8
3
-
4
- $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib"))
5
-
6
- require "amq/protocol/client"
7
- require "benchmark"
8
-
9
- FRAME_SIZE = 128 * 1024
10
-
11
- puts
12
- puts "-" * 80
13
- puts "Benchmarking on #{RUBY_DESCRIPTION}"
14
-
15
- n = 250_000
16
-
17
- # warm up the JIT, etc
18
- puts "Doing a warmup run..."
19
- 15_000.times { AMQ::Protocol::Method.encode_body("ab" * 1024, 1, FRAME_SIZE) }
20
-
21
- t = Benchmark.realtime do
22
- n.times { AMQ::Protocol::Method.encode_body("ab" * 1024, 1, FRAME_SIZE) }
23
- end
24
- r = (n.to_f/t.to_f)
25
-
26
- puts "AMQ::Protocol::Method.encode_body rate: #{(r / 1000).round(2)} KGHz"
27
- puts
28
- puts "-" * 80
@@ -1,64 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: utf-8
3
- # frozen_string_literal: true
4
-
5
- # Master benchmark runner
6
- # Usage: ruby benchmarks/run_all.rb
7
-
8
- require 'fileutils'
9
-
10
- BENCHMARK_DIR = File.dirname(__FILE__)
11
- RESULTS_DIR = File.join(BENCHMARK_DIR, "results")
12
-
13
- FileUtils.mkdir_p(RESULTS_DIR)
14
-
15
- benchmarks = %w[
16
- pack_unpack.rb
17
- frame_encoding.rb
18
- table_encoding.rb
19
- method_encoding.rb
20
- ]
21
-
22
- timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
23
- ruby_version = RUBY_VERSION.gsub('.', '_')
24
- results_file = File.join(RESULTS_DIR, "benchmark_#{ruby_version}_#{timestamp}.txt")
25
-
26
- puts "=" * 80
27
- puts "AMQ-Protocol Benchmark Suite"
28
- puts "=" * 80
29
- puts "Ruby: #{RUBY_DESCRIPTION}"
30
- puts "Time: #{Time.now}"
31
- puts "Results will be saved to: #{results_file}"
32
- puts "=" * 80
33
- puts
34
-
35
- File.open(results_file, 'w') do |f|
36
- f.puts "AMQ-Protocol Benchmark Results"
37
- f.puts "Ruby: #{RUBY_DESCRIPTION}"
38
- f.puts "Time: #{Time.now}"
39
- f.puts "=" * 80
40
- f.puts
41
-
42
- benchmarks.each do |benchmark|
43
- benchmark_path = File.join(BENCHMARK_DIR, benchmark)
44
-
45
- if File.exist?(benchmark_path)
46
- puts "\n>>> Running #{benchmark}..."
47
- puts
48
-
49
- output = `ruby #{benchmark_path} 2>&1`
50
- puts output
51
-
52
- f.puts ">>> #{benchmark}"
53
- f.puts output
54
- f.puts
55
- else
56
- puts "Warning: #{benchmark_path} not found, skipping..."
57
- end
58
- end
59
- end
60
-
61
- puts
62
- puts "=" * 80
63
- puts "Benchmark complete! Results saved to: #{results_file}"
64
- puts "=" * 80
@@ -1,110 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: utf-8
3
- # frozen_string_literal: true
4
-
5
- $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
6
-
7
- require "amq/protocol/client"
8
- require "benchmark/ips"
9
-
10
- puts
11
- puts "-" * 80
12
- puts "Table Encoding/Decoding Benchmarks on #{RUBY_DESCRIPTION}"
13
- puts "-" * 80
14
-
15
- # Test data - various table sizes and types
16
- EMPTY_TABLE = {}
17
-
18
- SIMPLE_TABLE = {
19
- "key1" => "value1",
20
- "key2" => 42,
21
- "key3" => true
22
- }.freeze
23
-
24
- TYPICAL_HEADERS = {
25
- "content_type" => "application/json",
26
- "content_encoding" => "utf-8",
27
- "x-custom-header" => "some-value",
28
- "x-retry-count" => 3,
29
- "x-timestamp" => Time.now.to_i
30
- }.freeze
31
-
32
- COMPLEX_TABLE = {
33
- "string" => "hello world",
34
- "integer" => 123456789,
35
- "float" => 3.14159,
36
- "boolean_true" => true,
37
- "boolean_false" => false,
38
- "nested" => {
39
- "inner_key" => "inner_value",
40
- "inner_number" => 999
41
- },
42
- "array" => [1, 2, 3, "four", true]
43
- }.freeze
44
-
45
- LARGE_TABLE = (1..50).to_h { |i| ["key_#{i}", "value_#{i}"] }.freeze
46
-
47
- # Pre-encode tables for decode benchmarks
48
- ENCODED_EMPTY = AMQ::Protocol::Table.encode(EMPTY_TABLE)
49
- ENCODED_SIMPLE = AMQ::Protocol::Table.encode(SIMPLE_TABLE)
50
- ENCODED_TYPICAL = AMQ::Protocol::Table.encode(TYPICAL_HEADERS)
51
- ENCODED_COMPLEX = AMQ::Protocol::Table.encode(COMPLEX_TABLE)
52
- ENCODED_LARGE = AMQ::Protocol::Table.encode(LARGE_TABLE)
53
-
54
- puts "Table sizes (bytes): empty=#{ENCODED_EMPTY.bytesize}, simple=#{ENCODED_SIMPLE.bytesize}, typical=#{ENCODED_TYPICAL.bytesize}, complex=#{ENCODED_COMPLEX.bytesize}, large=#{ENCODED_LARGE.bytesize}"
55
- puts
56
-
57
- puts "=== Table Encoding ==="
58
- Benchmark.ips do |x|
59
- x.config(time: 5, warmup: 2)
60
-
61
- x.report("encode empty") do
62
- AMQ::Protocol::Table.encode(EMPTY_TABLE)
63
- end
64
-
65
- x.report("encode simple (3 keys)") do
66
- AMQ::Protocol::Table.encode(SIMPLE_TABLE)
67
- end
68
-
69
- x.report("encode typical headers (5 keys)") do
70
- AMQ::Protocol::Table.encode(TYPICAL_HEADERS)
71
- end
72
-
73
- x.report("encode complex (nested/array)") do
74
- AMQ::Protocol::Table.encode(COMPLEX_TABLE)
75
- end
76
-
77
- x.report("encode large (50 keys)") do
78
- AMQ::Protocol::Table.encode(LARGE_TABLE)
79
- end
80
-
81
- x.compare!
82
- end
83
-
84
- puts
85
- puts "=== Table Decoding ==="
86
- Benchmark.ips do |x|
87
- x.config(time: 5, warmup: 2)
88
-
89
- x.report("decode empty") do
90
- AMQ::Protocol::Table.decode(ENCODED_EMPTY)
91
- end
92
-
93
- x.report("decode simple (3 keys)") do
94
- AMQ::Protocol::Table.decode(ENCODED_SIMPLE)
95
- end
96
-
97
- x.report("decode typical headers (5 keys)") do
98
- AMQ::Protocol::Table.decode(ENCODED_TYPICAL)
99
- end
100
-
101
- x.report("decode complex (nested/array)") do
102
- AMQ::Protocol::Table.decode(ENCODED_COMPLEX)
103
- end
104
-
105
- x.report("decode large (50 keys)") do
106
- AMQ::Protocol::Table.decode(ENCODED_LARGE)
107
- end
108
-
109
- x.compare!
110
- end
data/codegen/__init__.py DELETED
File without changes
@@ -1 +0,0 @@
1
- {"tx": {"select-ok": ["client"], "rollback": ["server"], "commit": ["server"], "rollback-ok": ["client"], "select": ["server"], "commit-ok": ["client"]}, "exchange": {"delete-ok": ["client"], "declare-ok": ["client"], "declare": ["server"], "delete": ["server"], "bind": ["server"], "bind-ok": ["client"], "unbind": ["server"], "unbind-ok": ["client"]}, "queue": {"unbind": ["server"], "unbind-ok": ["client"], "purge-ok": ["client"], "bind": ["server"], "purge": ["server"], "declare-ok": ["client"], "delete-ok": ["client"], "delete": ["server"], "declare": ["server"], "bind-ok": ["client"]}, "connection": {"secure": ["client"], "secure-ok": ["server"], "open-ok": ["client"], "close-ok": ["client", "server"], "start": ["client"], "tune": ["client"], "start-ok": ["server"], "close": ["client", "server"], "open": ["server"], "tune-ok": ["server"]}, "basic": {"qos": ["server"], "consume": ["server"], "reject": ["server"], "get": ["server"], "ack": ["client", "server"], "get-ok": ["client"], "consume-ok": ["client"], "deliver": ["client"], "recover-ok": ["client"], "publish": ["server"], "cancel": ["server", "client"], "recover-async": ["server"], "get-empty": ["client"], "qos-ok": ["client"], "return": ["client"], "recover": ["server"], "cancel-ok": ["client"]}, "channel": {"flow-ok": ["server", "client"], "flow": ["server", "client"], "open-ok": ["client"], "close-ok": ["client", "server"], "close": ["client", "server"], "open": ["server"]}}
data/codegen/codegen.py DELETED
@@ -1,151 +0,0 @@
1
- #!/usr/bin/env python
2
- # -*- coding: utf-8 -*-
3
-
4
- # Documentation for Mako templates:
5
- # http://www.makotemplates.org/docs/syntax.html
6
-
7
- import os, sys, re
8
-
9
- sys.path.append(os.path.join("codegen", "rabbitmq-codegen"))
10
-
11
- from amqp_codegen import *
12
- try:
13
- from mako.template import Template
14
- except ImportError:
15
- print("Mako isn't installed. Please install mako via pip or similar.")
16
- sys.exit(1)
17
-
18
- # main class
19
- class AmqpSpecObject(AmqpSpec):
20
- IGNORED_CLASSES = ["access"]
21
- IGNORED_FIELDS = {
22
- 'ticket': 0,
23
- 'capabilities': '',
24
- 'insist' : 0,
25
- 'out_of_band': '',
26
- 'known_hosts': '',
27
- }
28
-
29
- def __init__(self, path):
30
- AmqpSpec.__init__(self, path)
31
-
32
- def extend_field(field):
33
- field.ruby_name = re.sub("[- ]", "_", field.name)
34
- field.type = self.resolveDomain(field.domain)
35
- field.ignored = bool(field.name in self.__class__.IGNORED_FIELDS) # I. e. deprecated
36
-
37
- for klass in self.classes:
38
- klass.ignored = bool(klass.name in self.__class__.IGNORED_CLASSES)
39
-
40
- for field in klass.fields:
41
- extend_field(field)
42
-
43
- for method in klass.methods:
44
- for field in method.arguments:
45
- extend_field(field)
46
-
47
- self.classes = filter(lambda klass: not klass.ignored, self.classes)
48
-
49
- original_init = AmqpEntity.__init__
50
- def new_init(self, arg):
51
- original_init(self, arg)
52
- constant_name = ""
53
- for chunk in self.name.split("-"):
54
- constant_name += chunk.capitalize()
55
- self.constant_name = constant_name
56
- AmqpEntity.__init__ = new_init
57
-
58
- # method.accepted_by("server")
59
- # method.accepted_by("client", "server")
60
- accepted_by_update = json.loads(open("codegen/amqp_0.9.1_changes.json").read())
61
-
62
- def accepted_by(self, *receivers):
63
- def get_accepted_by(self):
64
- try:
65
- return accepted_by_update[self.klass.name][self.name]
66
- except KeyError:
67
- return ["server", "client"]
68
-
69
- actual_receivers = get_accepted_by(self)
70
- return all(map(lambda receiver: receiver in actual_receivers, receivers))
71
-
72
- AmqpMethod.accepted_by = accepted_by
73
-
74
- def convert_value_to_ruby(value):
75
- values = {None: "nil", False: "false", True: "true", "": "EMPTY_STRING"}
76
-
77
- try:
78
- return values[value]
79
- except:
80
- return value.__repr__()
81
-
82
- def convert_to_ruby(field):
83
- name = re.sub("-", "_", field.name) # TODO: use ruby_name
84
- if name == "ticket":
85
- return "%s = %s" % (name, field.defaultvalue) # we want to keep it as an int, not as a boolean
86
- else:
87
- return "%s = %s" % (name, convert_value_to_ruby(field.defaultvalue))
88
-
89
- def not_ignored_args(self):
90
- if self.hasContent:
91
- return ["payload", "user_headers"] + map(lambda argument: argument.ruby_name, filter(lambda argument: not argument.ignored, self.arguments)) + ["frame_size"]
92
- else:
93
- return map(lambda argument: argument.ruby_name, filter(lambda argument: not argument.ignored, self.arguments))
94
-
95
- AmqpMethod.not_ignored_args = not_ignored_args
96
-
97
- def ignored_args(self):
98
- return filter(lambda argument: argument.ignored, self.arguments)
99
-
100
- AmqpMethod.ignored_args = ignored_args
101
-
102
- # helpers
103
- def to_ruby_name(name):
104
- return re.sub("[- ]", "_", name)
105
-
106
- def to_ruby_class_name(name):
107
- parts = re.split("[- ]", name)
108
- ruby_class_name = ""
109
- for part in parts:
110
- ruby_class_name = ruby_class_name + part[0].upper() + part[1:].lower()
111
- return ruby_class_name
112
-
113
- def params(self):
114
- buffer = []
115
- for f in self.arguments:
116
- buffer.append(convert_to_ruby(f))
117
- if self.hasContent:
118
- buffer.append("user_headers = nil")
119
- buffer.append("payload = \"\"")
120
- buffer.append("frame_size = nil")
121
- return buffer
122
-
123
- AmqpMethod.params = params
124
-
125
- def args(self):
126
- return map(lambda item: item.split(" ")[0], self.params())
127
-
128
- AmqpMethod.args = args
129
-
130
- def binary(self):
131
- method_id = self.klass.index << 16 | self.index
132
- return "0x%08X # %i, %i, %i" % (method_id, self.klass.index, self.index, method_id)
133
-
134
- AmqpMethod.binary = binary
135
-
136
- # helpers
137
- def render(path, **context):
138
- file = open(path)
139
- template = Template(file.read())
140
- return template.render(**context)
141
-
142
- def generateMain(type):
143
- def main(json_spec_path):
144
- spec = AmqpSpecObject(json_spec_path)
145
- spec.type = type
146
- print(render("codegen/protocol.rb.pytemplate", spec = spec))
147
-
148
- return main
149
-
150
- if __name__ == "__main__":
151
- do_main_dict({"client": generateMain("client")})