amq-protocol 1.0.0.pre2 → 1.0.0.pre3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|