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
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
# encoding: binary
|
|
3
|
-
# frozen_string_literal: true
|
|
4
|
-
|
|
5
|
-
# THIS IS AN AUTOGENERATED FILE, DO NOT MODIFY
|
|
6
|
-
# IT DIRECTLY ! FOR CHANGES, PLEASE UPDATE FILES
|
|
7
|
-
# IN THE ./codegen DIRECTORY OF THE AMQ-PROTOCOL REPOSITORY.<% import codegen_helpers as helpers %><% import re, os, codegen %>
|
|
8
|
-
|
|
9
|
-
require "amq/pack"
|
|
10
|
-
|
|
11
|
-
require "amq/protocol/table"
|
|
12
|
-
require "amq/protocol/frame"
|
|
13
|
-
|
|
14
|
-
require "amq/protocol/constants"
|
|
15
|
-
require "amq/protocol/exceptions"
|
|
16
|
-
|
|
17
|
-
module AMQ
|
|
18
|
-
module Protocol
|
|
19
|
-
PROTOCOL_VERSION = "${spec.major}.${spec.minor}.${spec.revision}".freeze
|
|
20
|
-
PREAMBLE = "${'AMQP\\x00\\x%02x\\x%02x\\x%02x' % (spec.major, spec.minor, spec.revision)}".freeze
|
|
21
|
-
DEFAULT_PORT = ${spec.port}
|
|
22
|
-
|
|
23
|
-
# @return [Array] Collection of subclasses of AMQ::Protocol::Class.
|
|
24
|
-
def self.classes
|
|
25
|
-
Protocol::Class.classes
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# @return [Array] Collection of subclasses of AMQ::Protocol::Method.
|
|
29
|
-
def self.methods
|
|
30
|
-
Protocol::Method.methods
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
% for tuple in spec.constants:
|
|
34
|
-
% if tuple[2] == "soft-error" or tuple[2] == "hard-error":
|
|
35
|
-
class ${codegen.to_ruby_class_name(tuple[0])} < ${codegen.to_ruby_class_name(tuple[2])}
|
|
36
|
-
VALUE = ${tuple[1]}
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
% endif
|
|
40
|
-
% endfor
|
|
41
|
-
|
|
42
|
-
class Class
|
|
43
|
-
@classes = Array.new
|
|
44
|
-
|
|
45
|
-
def self.method_id
|
|
46
|
-
@method_id
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def self.name
|
|
50
|
-
@name
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def self.inherited(base)
|
|
54
|
-
if self == Protocol::Class
|
|
55
|
-
@classes << base
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def self.classes
|
|
60
|
-
@classes
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
class Method
|
|
65
|
-
@methods = Array.new
|
|
66
|
-
def self.method_id
|
|
67
|
-
@method_id
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def self.name
|
|
71
|
-
@name
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def self.index
|
|
75
|
-
@index
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def self.inherited(base)
|
|
79
|
-
if self == Protocol::Method
|
|
80
|
-
@methods << base
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def self.methods
|
|
85
|
-
@methods
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def self.split_headers(user_headers)
|
|
89
|
-
properties, headers = {}, {}
|
|
90
|
-
user_headers.each do |key, value|
|
|
91
|
-
# key MUST be a symbol since symbols are not garbage-collected
|
|
92
|
-
if Basic::PROPERTIES.include?(key)
|
|
93
|
-
properties[key] = value
|
|
94
|
-
else
|
|
95
|
-
headers[key] = value
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
return [properties, headers]
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def self.encode_body(body, channel, frame_size)
|
|
103
|
-
return [] if body.empty?
|
|
104
|
-
|
|
105
|
-
# 8 = 1 + 2 + 4 + 1
|
|
106
|
-
# 1 byte of frame type
|
|
107
|
-
# 2 bytes of channel number
|
|
108
|
-
# 4 bytes of frame payload length
|
|
109
|
-
# 1 byte of payload trailer FRAME_END byte
|
|
110
|
-
limit = frame_size - 8
|
|
111
|
-
return [BodyFrame.new(body, channel)] if body.bytesize < limit
|
|
112
|
-
|
|
113
|
-
# Otherwise String#slice on 1.9 will operate with code points,
|
|
114
|
-
# and we need bytes. MK.
|
|
115
|
-
body.force_encoding("ASCII-8BIT") if RUBY_VERSION.to_f >= 1.9
|
|
116
|
-
|
|
117
|
-
array = Array.new
|
|
118
|
-
while body && !body.empty?
|
|
119
|
-
payload, body = body[0, limit], body[limit, body.length - limit]
|
|
120
|
-
array << BodyFrame.new(payload, channel)
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
array
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def self.instantiate(*args, &block)
|
|
127
|
-
self.new(*args, &block)
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
% for klass in spec.classes :
|
|
132
|
-
class ${klass.constant_name} < Protocol::Class
|
|
133
|
-
@name = "${klass.name}"
|
|
134
|
-
@method_id = ${klass.index}
|
|
135
|
-
|
|
136
|
-
% if klass.fields: ## only the Basic class has fields (refered as properties in the JSON)
|
|
137
|
-
PROPERTIES = [
|
|
138
|
-
% for field in klass.fields:
|
|
139
|
-
:${field.ruby_name}, # ${spec.resolveDomain(field.domain)}
|
|
140
|
-
% endfor
|
|
141
|
-
]
|
|
142
|
-
|
|
143
|
-
% for f in klass.fields:
|
|
144
|
-
# <% i = klass.fields.index(f) %>1 << ${15 - i}
|
|
145
|
-
def self.encode_${f.ruby_name}(value)
|
|
146
|
-
buffer = +''
|
|
147
|
-
% for line in helpers.genSingleEncode(spec, "value", f.domain):
|
|
148
|
-
${line}
|
|
149
|
-
% endfor
|
|
150
|
-
[${i}, ${"0x%04x" % ( 1 << (15-i),)}, buffer]
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
% endfor
|
|
154
|
-
|
|
155
|
-
% endif
|
|
156
|
-
|
|
157
|
-
% if klass.name == "basic" :
|
|
158
|
-
def self.encode_properties(body_size, properties)
|
|
159
|
-
pieces, flags = [], 0
|
|
160
|
-
|
|
161
|
-
properties.reject {|key, value| value.nil?}.each do |key, value|
|
|
162
|
-
i, f, result = self.__send__(:"encode_#{key}", value)
|
|
163
|
-
flags |= f
|
|
164
|
-
pieces[i] = result
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
# result = [${klass.index}, 0, body_size, flags].pack('n2Qn')
|
|
168
|
-
result = [${klass.index}, 0].pack(PACK_UINT16_X2)
|
|
169
|
-
result += AMQ::Pack.pack_uint64_big_endian(body_size)
|
|
170
|
-
result += [flags].pack(PACK_UINT16)
|
|
171
|
-
pieces_joined = pieces.join(EMPTY_STRING)
|
|
172
|
-
result.force_encoding(pieces_joined.encoding) + pieces_joined
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
# THIS DECODES ONLY FLAGS
|
|
176
|
-
DECODE_PROPERTIES = {
|
|
177
|
-
% for f in klass.fields:
|
|
178
|
-
${"0x%04x" % ( 1 << (15 - klass.fields.index(f)),)} => :${f.ruby_name},
|
|
179
|
-
% endfor
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
DECODE_PROPERTIES_TYPE = {
|
|
183
|
-
% for f in klass.fields:
|
|
184
|
-
${"0x%04x" % ( 1 << (15 - klass.fields.index(f)),)} => :${spec.resolveDomain(f.domain)},
|
|
185
|
-
% endfor
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
# Hash doesn't give any guarantees on keys order, we will do it in a
|
|
189
|
-
# straightforward way
|
|
190
|
-
DECODE_PROPERTIES_KEYS = [
|
|
191
|
-
% for f in klass.fields:
|
|
192
|
-
${"0x%04x" % ( 1 << (15 - klass.fields.index(f)),)},
|
|
193
|
-
% endfor
|
|
194
|
-
]
|
|
195
|
-
|
|
196
|
-
def self.decode_properties(data)
|
|
197
|
-
offset, data_length, properties = 0, data.bytesize, {}
|
|
198
|
-
|
|
199
|
-
compressed_index = data[offset, 2].unpack(PACK_UINT16)[0]
|
|
200
|
-
offset += 2
|
|
201
|
-
while data_length > offset
|
|
202
|
-
DECODE_PROPERTIES_KEYS.each do |key|
|
|
203
|
-
next unless compressed_index >= key
|
|
204
|
-
compressed_index -= key
|
|
205
|
-
name = DECODE_PROPERTIES[key] || raise(RuntimeError.new("No property found for index #{index.inspect}!"))
|
|
206
|
-
case DECODE_PROPERTIES_TYPE[key]
|
|
207
|
-
when :shortstr
|
|
208
|
-
size = data[offset, 1].unpack(PACK_CHAR)[0]
|
|
209
|
-
offset += 1
|
|
210
|
-
result = data[offset, size]
|
|
211
|
-
when :octet
|
|
212
|
-
size = 1
|
|
213
|
-
result = data[offset, size].unpack(PACK_CHAR).first
|
|
214
|
-
when :timestamp
|
|
215
|
-
size = 8
|
|
216
|
-
result = Time.at(data[offset, size].unpack(PACK_UINT64_BE).last)
|
|
217
|
-
when :table
|
|
218
|
-
size = 4 + data[offset, 4].unpack(PACK_UINT32)[0]
|
|
219
|
-
result = Table.decode(data[offset, size])
|
|
220
|
-
end
|
|
221
|
-
properties[name] = result
|
|
222
|
-
offset += size
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
properties
|
|
227
|
-
end
|
|
228
|
-
% endif
|
|
229
|
-
|
|
230
|
-
% for method in klass.methods:
|
|
231
|
-
class ${method.constant_name} < Protocol::Method
|
|
232
|
-
@name = "${klass.name}.${method.name}"
|
|
233
|
-
@method_id = ${method.index}
|
|
234
|
-
@index = ${method.binary()}
|
|
235
|
-
@packed_indexes = [${klass.index}, ${method.index}].pack(PACK_UINT16_X2).freeze
|
|
236
|
-
|
|
237
|
-
% if (spec.type == "client" and method.accepted_by("client")) or (spec.type == "server" and method.accepted_by("server") or spec.type == "all"):
|
|
238
|
-
# @return
|
|
239
|
-
def self.decode(data)
|
|
240
|
-
offset = offset = 0 # self-assigning offset to eliminate "assigned but unused variable" warning even if offset is not used in this method
|
|
241
|
-
% for line in helpers.genDecodeMethodDefinition(spec, method):
|
|
242
|
-
${line}
|
|
243
|
-
% endfor
|
|
244
|
-
% if (method.klass.name == "connection" or method.klass.name == "channel") and method.name == "close":
|
|
245
|
-
self.new(${', '.join([f.ruby_name for f in method.arguments])})
|
|
246
|
-
% else:
|
|
247
|
-
self.new(${', '.join([f.ruby_name for f in method.arguments])})
|
|
248
|
-
% endif
|
|
249
|
-
end
|
|
250
|
-
|
|
251
|
-
% if len(method.arguments) > 0:
|
|
252
|
-
attr_reader ${', '.join([":" + f.ruby_name for f in method.arguments])}
|
|
253
|
-
% endif
|
|
254
|
-
def initialize(${', '.join([f.ruby_name for f in method.arguments])})
|
|
255
|
-
% for f in method.arguments:
|
|
256
|
-
@${f.ruby_name} = ${f.ruby_name}
|
|
257
|
-
% endfor
|
|
258
|
-
end
|
|
259
|
-
% endif
|
|
260
|
-
|
|
261
|
-
def self.has_content?
|
|
262
|
-
% if method.hasContent:
|
|
263
|
-
true
|
|
264
|
-
% else:
|
|
265
|
-
false
|
|
266
|
-
% endif
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
% if (spec.type == "client" and method.accepted_by("server")) or (spec.type == "server" and method.accepted_by("client")) or spec.type == "all":
|
|
270
|
-
# @return
|
|
271
|
-
# ${method.params()}
|
|
272
|
-
% if klass.name == "connection":
|
|
273
|
-
def self.encode(${(", ").join(method.not_ignored_args())})
|
|
274
|
-
% else:
|
|
275
|
-
def self.encode(${(", ").join(["channel"] + method.not_ignored_args())})
|
|
276
|
-
% endif
|
|
277
|
-
% for argument in method.ignored_args():
|
|
278
|
-
${codegen.convert_to_ruby(argument)}
|
|
279
|
-
% endfor
|
|
280
|
-
% if klass.name == "connection":
|
|
281
|
-
channel = 0
|
|
282
|
-
% endif
|
|
283
|
-
buffer = @packed_indexes.dup
|
|
284
|
-
% for line in helpers.genEncodeMethodDefinition(spec, method):
|
|
285
|
-
${line}
|
|
286
|
-
% endfor
|
|
287
|
-
% if "payload" in method.args() or "user_headers" in method.args():
|
|
288
|
-
frames = [MethodFrame.new(buffer, channel)]
|
|
289
|
-
% if "user_headers" in method.args():
|
|
290
|
-
properties, _headers = self.split_headers(user_headers)
|
|
291
|
-
if properties.nil? or properties.empty?
|
|
292
|
-
raise RuntimeError.new("Properties can not be empty!")
|
|
293
|
-
end
|
|
294
|
-
properties_payload = Basic.encode_properties(payload.bytesize, properties)
|
|
295
|
-
frames << HeaderFrame.new(properties_payload, channel)
|
|
296
|
-
% endif
|
|
297
|
-
% if "payload" in method.args():
|
|
298
|
-
frames += self.encode_body(payload, channel, frame_size)
|
|
299
|
-
frames
|
|
300
|
-
% endif
|
|
301
|
-
% else:
|
|
302
|
-
MethodFrame.new(buffer, channel)
|
|
303
|
-
% endif
|
|
304
|
-
end
|
|
305
|
-
% endif
|
|
306
|
-
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
% endfor
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
% endfor
|
|
313
|
-
|
|
314
|
-
METHODS = begin
|
|
315
|
-
Method.methods.inject(Hash.new) do |hash, klass|
|
|
316
|
-
hash.merge!(klass.index => klass)
|
|
317
|
-
end
|
|
318
|
-
end
|
|
319
|
-
end
|
|
320
|
-
end
|
data/generate.rb
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# encoding: utf-8
|
|
3
|
-
|
|
4
|
-
# rabbitmq-codegen is Python 3 compatible and so is
|
|
5
|
-
# the code in this repo but Mako still fails with 3.6 as of May 2017 :( MK.
|
|
6
|
-
python = ENV.fetch("PYTHON", "python2")
|
|
7
|
-
|
|
8
|
-
def sh(*args)
|
|
9
|
-
system(*args)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
extensions = []
|
|
13
|
-
|
|
14
|
-
spec = "codegen/rabbitmq-codegen/amqp-rabbitmq-0.9.1.json"
|
|
15
|
-
unless File.exist?(spec)
|
|
16
|
-
sh "git submodule update --init"
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
path = "lib/amq/protocol/client.rb"
|
|
20
|
-
puts "Running '#{python} ./codegen/codegen.py client #{spec} #{extensions.join(' ')} #{path}'"
|
|
21
|
-
sh "#{python} ./codegen/codegen.py client #{spec} #{extensions.join(' ')} #{path}"
|
|
22
|
-
if File.file?(path)
|
|
23
|
-
sh "ruby -c #{path}"
|
|
24
|
-
end
|
data/profiling/README.md
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# Profiling Scripts
|
|
2
|
-
|
|
3
|
-
This directory contains profiling scripts. Currently they use [stackprof](https://github.com/tmm1/stackprof) which
|
|
4
|
-
requires Ruby 2.1+ (preview2 or later).
|
|
5
|
-
|
|
6
|
-
## Running the Profiler
|
|
7
|
-
|
|
8
|
-
ruby profiling/stackprof/body_framing_with_2k_payload.rb
|
|
9
|
-
stackprof profiling/dumps/body_framing_with_2k_payload.dump --text
|
|
@@ -1,33 +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
|
-
|
|
8
|
-
FRAME_SIZE = 128 * 1024
|
|
9
|
-
|
|
10
|
-
puts
|
|
11
|
-
puts "-" * 80
|
|
12
|
-
puts "Profiling on #{RUBY_DESCRIPTION}"
|
|
13
|
-
|
|
14
|
-
n = 250_000
|
|
15
|
-
|
|
16
|
-
# warm up the JIT, etc
|
|
17
|
-
puts "Doing a warmup run..."
|
|
18
|
-
15_000.times { AMQ::Protocol::Method.encode_body("ab" * 1024, 1, FRAME_SIZE) }
|
|
19
|
-
|
|
20
|
-
require 'stackprof'
|
|
21
|
-
|
|
22
|
-
# preallocate
|
|
23
|
-
ary = Array.new(n) { "ab" * 1024 }
|
|
24
|
-
|
|
25
|
-
puts "Doing main run..."
|
|
26
|
-
result = StackProf.run(mode: :wall) do
|
|
27
|
-
n.times { |i| AMQ::Protocol::Method.encode_body(ary[i], 1, FRAME_SIZE) }
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
File.open('./profiling/dumps/body_framing_with_2k_payload.dump', "w+") do |f|
|
|
31
|
-
f.write Marshal.dump(result)
|
|
32
|
-
end
|
|
33
|
-
|
data/spec/amq/bit_set_spec.rb
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
require "amq/bit_set"
|
|
2
|
-
|
|
3
|
-
# extracted from amqp gem. MK.
|
|
4
|
-
RSpec.describe AMQ::BitSet do
|
|
5
|
-
|
|
6
|
-
#
|
|
7
|
-
# Environment
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
let(:nbits) { (1 << 16) - 1 }
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
# Examples
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
describe "#new" do
|
|
18
|
-
it "has no bits set at the start" do
|
|
19
|
-
bs = AMQ::BitSet.new(128)
|
|
20
|
-
0.upto(127) do |i|
|
|
21
|
-
expect(bs[i]).to be_falsey
|
|
22
|
-
end
|
|
23
|
-
end # it
|
|
24
|
-
end # describe
|
|
25
|
-
|
|
26
|
-
describe "#word_index" do
|
|
27
|
-
subject do
|
|
28
|
-
described_class.new(nbits)
|
|
29
|
-
end
|
|
30
|
-
it "returns 0 when the word is between 0 and 63" do
|
|
31
|
-
expect(subject.word_index(0)).to eq(0)
|
|
32
|
-
expect(subject.word_index(63)).to eq(0)
|
|
33
|
-
end # it
|
|
34
|
-
it "returns 1 when the word is between 64 and 127" do
|
|
35
|
-
expect(subject.word_index(64)).to be(1)
|
|
36
|
-
expect(subject.word_index(127)).to be(1)
|
|
37
|
-
end # it
|
|
38
|
-
it "returns 2 when the word is between 128 and another number" do
|
|
39
|
-
expect(subject.word_index(128)).to be(2)
|
|
40
|
-
end # it
|
|
41
|
-
end # describe
|
|
42
|
-
|
|
43
|
-
describe "#get, #[]" do
|
|
44
|
-
describe "when bit at given position is set" do
|
|
45
|
-
subject do
|
|
46
|
-
o = described_class.new(nbits)
|
|
47
|
-
o.set(3)
|
|
48
|
-
o
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
it "returns true" do
|
|
52
|
-
expect(subject.get(3)).to be_truthy
|
|
53
|
-
end # it
|
|
54
|
-
end # describe
|
|
55
|
-
|
|
56
|
-
describe "when bit at given position is off" do
|
|
57
|
-
subject do
|
|
58
|
-
described_class.new(nbits)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
it "returns false" do
|
|
62
|
-
expect(subject.get(5)).to be_falsey
|
|
63
|
-
end # it
|
|
64
|
-
end # describe
|
|
65
|
-
|
|
66
|
-
describe "when index out of range" do
|
|
67
|
-
subject do
|
|
68
|
-
described_class.new(nbits)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
it "should raise IndexError for negative index" do
|
|
72
|
-
expect { subject.get(-1) }.to raise_error(IndexError)
|
|
73
|
-
end # it
|
|
74
|
-
it "should raise IndexError for index >= number of bits" do
|
|
75
|
-
expect { subject.get(nbits) }.to raise_error(IndexError)
|
|
76
|
-
end # it
|
|
77
|
-
end # describe
|
|
78
|
-
end # describe
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
describe "#set" do
|
|
82
|
-
describe "when bit at given position is set" do
|
|
83
|
-
subject do
|
|
84
|
-
described_class.new(nbits)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
it "has no effect" do
|
|
88
|
-
subject.set(3)
|
|
89
|
-
expect(subject.get(3)).to be_truthy
|
|
90
|
-
subject.set(3)
|
|
91
|
-
expect(subject[3]).to be_truthy
|
|
92
|
-
end # it
|
|
93
|
-
end # describe
|
|
94
|
-
|
|
95
|
-
describe "when bit at given position is off" do
|
|
96
|
-
subject do
|
|
97
|
-
described_class.new(nbits)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
it "sets that bit" do
|
|
101
|
-
subject.set(3)
|
|
102
|
-
expect(subject.get(3)).to be_truthy
|
|
103
|
-
|
|
104
|
-
subject.set(33)
|
|
105
|
-
expect(subject.get(33)).to be_truthy
|
|
106
|
-
|
|
107
|
-
subject.set(3387)
|
|
108
|
-
expect(subject.get(3387)).to be_truthy
|
|
109
|
-
end # it
|
|
110
|
-
end # describe
|
|
111
|
-
|
|
112
|
-
describe "when index out of range" do
|
|
113
|
-
subject do
|
|
114
|
-
described_class.new(nbits)
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
it "should raise IndexError for negative index" do
|
|
118
|
-
expect { subject.set(-1) }.to raise_error(IndexError)
|
|
119
|
-
end # it
|
|
120
|
-
it "should raise IndexError for index >= number of bits" do
|
|
121
|
-
expect { subject.set(nbits) }.to raise_error(IndexError)
|
|
122
|
-
end # it
|
|
123
|
-
end # describe
|
|
124
|
-
end # describe
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
describe "#unset" do
|
|
128
|
-
describe "when bit at a given position is set" do
|
|
129
|
-
subject do
|
|
130
|
-
described_class.new(nbits)
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
it "unsets that bit" do
|
|
134
|
-
subject.set(3)
|
|
135
|
-
expect(subject.get(3)).to be_truthy
|
|
136
|
-
subject.unset(3)
|
|
137
|
-
expect(subject.get(3)).to be_falsey
|
|
138
|
-
end # it
|
|
139
|
-
end # describe
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
describe "when bit at a given position is off" do
|
|
143
|
-
subject do
|
|
144
|
-
described_class.new(nbits)
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
it "has no effect" do
|
|
148
|
-
expect(subject.get(3)).to be_falsey
|
|
149
|
-
subject.unset(3)
|
|
150
|
-
expect(subject.get(3)).to be_falsey
|
|
151
|
-
end # it
|
|
152
|
-
end # describe
|
|
153
|
-
|
|
154
|
-
describe "when index out of range" do
|
|
155
|
-
subject do
|
|
156
|
-
described_class.new(nbits)
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
it "should raise IndexError for negative index" do
|
|
160
|
-
expect { subject.unset(-1) }.to raise_error(IndexError)
|
|
161
|
-
end # it
|
|
162
|
-
it "should raise IndexError for index >= number of bits" do
|
|
163
|
-
expect { subject.unset(nbits) }.to raise_error(IndexError)
|
|
164
|
-
end # it
|
|
165
|
-
end # describe
|
|
166
|
-
end # describe
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
describe "#clear" do
|
|
171
|
-
subject do
|
|
172
|
-
described_class.new(nbits)
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
it "clears all bits" do
|
|
176
|
-
subject.set(3)
|
|
177
|
-
expect(subject.get(3)).to be_truthy
|
|
178
|
-
|
|
179
|
-
subject.set(7668)
|
|
180
|
-
expect(subject.get(7668)).to be_truthy
|
|
181
|
-
|
|
182
|
-
subject.clear
|
|
183
|
-
|
|
184
|
-
expect(subject.get(3)).to be_falsey
|
|
185
|
-
expect(subject.get(7668)).to be_falsey
|
|
186
|
-
end # it
|
|
187
|
-
end # describe
|
|
188
|
-
|
|
189
|
-
describe "#number_of_trailing_ones" do
|
|
190
|
-
it "calculates them" do
|
|
191
|
-
expect(described_class.number_of_trailing_ones(0)).to eq(0)
|
|
192
|
-
expect(described_class.number_of_trailing_ones(1)).to eq(1)
|
|
193
|
-
expect(described_class.number_of_trailing_ones(2)).to eq(0)
|
|
194
|
-
expect(described_class.number_of_trailing_ones(3)).to eq(2)
|
|
195
|
-
expect(described_class.number_of_trailing_ones(4)).to eq(0)
|
|
196
|
-
end # it
|
|
197
|
-
end # describe
|
|
198
|
-
|
|
199
|
-
describe '#next_clear_bit' do
|
|
200
|
-
subject do
|
|
201
|
-
described_class.new(255)
|
|
202
|
-
end
|
|
203
|
-
it "returns sequential values when none have been returned" do
|
|
204
|
-
expect(subject.next_clear_bit).to eq(0)
|
|
205
|
-
subject.set(0)
|
|
206
|
-
expect(subject.next_clear_bit).to eq(1)
|
|
207
|
-
subject.set(1)
|
|
208
|
-
expect(subject.next_clear_bit).to eq(2)
|
|
209
|
-
subject.unset(1)
|
|
210
|
-
expect(subject.next_clear_bit).to eq(1)
|
|
211
|
-
end # it
|
|
212
|
-
|
|
213
|
-
it "returns the same number as long as nothing is set" do
|
|
214
|
-
expect(subject.next_clear_bit).to eq(0)
|
|
215
|
-
expect(subject.next_clear_bit).to eq(0)
|
|
216
|
-
end # it
|
|
217
|
-
|
|
218
|
-
it "handles more than 128 bits" do
|
|
219
|
-
0.upto(254) do |i|
|
|
220
|
-
subject.set(i)
|
|
221
|
-
expect(subject.next_clear_bit).to eq(i + 1)
|
|
222
|
-
end
|
|
223
|
-
subject.unset(254)
|
|
224
|
-
expect(subject.get(254)).to be_falsey
|
|
225
|
-
end # it
|
|
226
|
-
end # describe
|
|
227
|
-
end
|