amq-protocol 1.0.0.pre2 → 1.0.0.pre3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/amq/int_allocator.rb +96 -0
- data/lib/amq/protocol/version.rb +1 -1
- data/spec/amq/int_allocator_spec.rb +116 -0
- metadata +5 -3
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "amq/bit_set"
|
4
|
+
|
5
|
+
module AMQ
|
6
|
+
# Simple bitset-based integer allocator, heavily inspired by com.rabbitmq.utility.IntAllocator class
|
7
|
+
# in the RabbitMQ Java client.
|
8
|
+
#
|
9
|
+
# Unlike monotonically incrementing identifier, this allocator is suitable for very long running programs
|
10
|
+
# that aggressively allocate and release channels.
|
11
|
+
class IntAllocator
|
12
|
+
|
13
|
+
#
|
14
|
+
# API
|
15
|
+
#
|
16
|
+
|
17
|
+
# @return [Integer] Number of integers in the allocation range
|
18
|
+
attr_reader :number_of_bits
|
19
|
+
# @return [Integer] Upper boundary of the integer range available for allocation
|
20
|
+
attr_reader :hi
|
21
|
+
# @return [Integer] Lower boundary of the integer range available for allocation
|
22
|
+
attr_reader :lo
|
23
|
+
|
24
|
+
# @param [Integer] lo Lower boundary of the integer range available for allocation
|
25
|
+
# @param [Integer] hi Upper boundary of the integer range available for allocation
|
26
|
+
# @raise [ArgumentError] if upper boundary is not greater than the lower one
|
27
|
+
def initialize(lo, hi)
|
28
|
+
raise ArgumentError.new "upper boundary must be greater than the lower one (given: hi = #{hi}, lo = #{lo})" unless hi > lo
|
29
|
+
|
30
|
+
@hi = hi
|
31
|
+
@lo = lo
|
32
|
+
|
33
|
+
@number_of_bits = hi - lo
|
34
|
+
@range = Range.new(1, @number_of_bits)
|
35
|
+
@free_set = BitSet.new(@number_of_bits)
|
36
|
+
end # initialize(hi, lo)
|
37
|
+
|
38
|
+
# Attempts to allocate next available integer. If allocation succeeds, allocated value is returned.
|
39
|
+
# Otherwise, nil is returned.
|
40
|
+
#
|
41
|
+
# Current implementation of this method is O(n), where n is number of bits in the range available for
|
42
|
+
# allocation.
|
43
|
+
#
|
44
|
+
# @return [Integer] Allocated integer if allocation succeeded. nil otherwise.
|
45
|
+
def allocate
|
46
|
+
if n = find_unallocated_position
|
47
|
+
@free_set.set(n)
|
48
|
+
|
49
|
+
n
|
50
|
+
else
|
51
|
+
-1
|
52
|
+
end
|
53
|
+
end # allocate
|
54
|
+
|
55
|
+
# Releases previously allocated integer. If integer provided as argument was not previously allocated,
|
56
|
+
# this method has no effect.
|
57
|
+
#
|
58
|
+
# @return [NilClass] nil
|
59
|
+
def free(reservation)
|
60
|
+
@free_set.unset(reservation)
|
61
|
+
end # free(reservation)
|
62
|
+
alias release free
|
63
|
+
|
64
|
+
# @return [Boolean] true if provided argument was previously allocated, false otherwise
|
65
|
+
def allocated?(reservation)
|
66
|
+
@free_set.get(reservation)
|
67
|
+
end # allocated?(reservation)
|
68
|
+
|
69
|
+
# Releases the whole allocation range
|
70
|
+
def reset
|
71
|
+
@free_set.clear
|
72
|
+
end # reset
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
# This implementation is significantly less efficient
|
79
|
+
# that what the RabbitMQ Java client has (based on java.lang.Long#nextSetBit and
|
80
|
+
# java.lang.Long.numberOfTrailingZeros, and thus binary search over bits).
|
81
|
+
# But for channel id generation, this is a good enough implementation.
|
82
|
+
#
|
83
|
+
# @private
|
84
|
+
def find_unallocated_position
|
85
|
+
r = nil
|
86
|
+
@range.each do |i|
|
87
|
+
if !@free_set.get(i)
|
88
|
+
r = i
|
89
|
+
break;
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
r
|
94
|
+
end # find_unallocated_position
|
95
|
+
end # IntAllocator
|
96
|
+
end # AMQ
|
data/lib/amq/protocol/version.rb
CHANGED
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require "amq/int_allocator"
|
5
|
+
|
6
|
+
describe AMQ::IntAllocator do
|
7
|
+
|
8
|
+
#
|
9
|
+
# Environment
|
10
|
+
#
|
11
|
+
|
12
|
+
subject do
|
13
|
+
described_class.new(1, 5)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
# ...
|
18
|
+
|
19
|
+
|
20
|
+
#
|
21
|
+
# Examples
|
22
|
+
#
|
23
|
+
|
24
|
+
describe "#number_of_bits" do
|
25
|
+
it "returns number of bits available for allocation" do
|
26
|
+
subject.number_of_bits.should == 4
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
describe "#hi" do
|
32
|
+
it "returns upper bound of the allocation range" do
|
33
|
+
subject.hi.should == 5
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#lo" do
|
38
|
+
it "returns lower bound of the allocation range" do
|
39
|
+
subject.lo.should == 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
describe "#allocate" do
|
45
|
+
context "when integer in the range is available" do
|
46
|
+
it "returns allocated integer" do
|
47
|
+
subject.allocate.should == 1
|
48
|
+
subject.allocate.should == 2
|
49
|
+
subject.allocate.should == 3
|
50
|
+
subject.allocate.should == 4
|
51
|
+
|
52
|
+
subject.allocate.should == -1
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "when integer in the range IS NOT available" do
|
57
|
+
it "returns -1" do
|
58
|
+
4.times { subject.allocate }
|
59
|
+
|
60
|
+
subject.allocate.should == -1
|
61
|
+
subject.allocate.should == -1
|
62
|
+
subject.allocate.should == -1
|
63
|
+
subject.allocate.should == -1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
describe "#free" do
|
70
|
+
context "when the integer WAS allocated" do
|
71
|
+
it "returns frees that integer" do
|
72
|
+
4.times { subject.allocate }
|
73
|
+
subject.allocate.should == -1
|
74
|
+
|
75
|
+
subject.free(1)
|
76
|
+
subject.allocate.should == 1
|
77
|
+
subject.allocate.should == -1
|
78
|
+
subject.free(2)
|
79
|
+
subject.allocate.should == 2
|
80
|
+
subject.allocate.should == -1
|
81
|
+
subject.free(3)
|
82
|
+
subject.allocate.should == 3
|
83
|
+
subject.allocate.should == -1
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "when the integer WAS NOT allocated" do
|
88
|
+
it "has no effect" do
|
89
|
+
32.times { subject.free(1) }
|
90
|
+
subject.allocate.should == 1
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
describe "#allocated?" do
|
97
|
+
context "when given position WAS allocated" do
|
98
|
+
it "returns true" do
|
99
|
+
3.times { subject.allocate }
|
100
|
+
|
101
|
+
subject.allocated?(1).should be_true
|
102
|
+
subject.allocated?(2).should be_true
|
103
|
+
subject.allocated?(3).should be_true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when given position WAS NOT allocated" do
|
108
|
+
it "returns false" do
|
109
|
+
2.times { subject.allocate }
|
110
|
+
|
111
|
+
subject.allocated?(3).should be_false
|
112
|
+
subject.allocated?(4).should be_false
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amq-protocol
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 4271866905
|
5
5
|
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
9
|
- 0
|
10
10
|
- pre
|
11
|
-
-
|
12
|
-
version: 1.0.0.
|
11
|
+
- 3
|
12
|
+
version: 1.0.0.pre3
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Jakub Stastny
|
@@ -51,6 +51,7 @@ files:
|
|
51
51
|
- irb.rb
|
52
52
|
- lib/amq/bit_set.rb
|
53
53
|
- lib/amq/hacks.rb
|
54
|
+
- lib/amq/int_allocator.rb
|
54
55
|
- lib/amq/protocol.rb
|
55
56
|
- lib/amq/protocol/client.rb
|
56
57
|
- lib/amq/protocol/frame.rb
|
@@ -63,6 +64,7 @@ files:
|
|
63
64
|
- protocol.rb.pytemplate
|
64
65
|
- spec/amq/bit_set_spec.rb
|
65
66
|
- spec/amq/hacks_spec.rb
|
67
|
+
- spec/amq/int_allocator_spec.rb
|
66
68
|
- spec/amq/protocol/basic_spec.rb
|
67
69
|
- spec/amq/protocol/blank_body_encoding_spec.rb
|
68
70
|
- spec/amq/protocol/channel_spec.rb
|