amq-protocol 1.8.0 → 1.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4ed555f9528be0395d8ba9a11a39e193aa347f89
4
- data.tar.gz: 345f66cb02153603fb16465bc9edddaf1b2bc63a
3
+ metadata.gz: 4ac3c4778d62f3a7f84cd1132c77de339a2e97c3
4
+ data.tar.gz: 205cf9a34ea9addcfe2458e1dfcc9b6fe8a6c81c
5
5
  SHA512:
6
- metadata.gz: b5b11714e11f26704f9768d750bb61c4034edac91141d6a1743ad7fbbc6999fc88b2c70acbbbc42f14cdc6bd3eb3a0a3441519bd2922413a03d13a208d101e08
7
- data.tar.gz: f50280bc01e330a2b2952585b444ceb5b195855836d572791c727c73b6d295b3037b76fc4ccf042837656a684f23889658114a981a55a33f50a05651d85b8478
6
+ metadata.gz: ed02a18089fc8e1f633716981a1d995cf5ad6eb8663bdfb794effc32351dea6d97b56cbcea01a413f65d1ace0a56f1853bbe0bcce10072587c9439eb60d748d2
7
+ data.tar.gz: 0c2344d1e5919666c6b38934fe3f38817f117f117ed8606b6090c96e7e43c6e8afa556bbf4bf9e2e082e7bea75518ac5090f9d48d869525edb532fc67a95856e
@@ -6,14 +6,12 @@ rvm:
6
6
  - 1.9.3
7
7
  - jruby-19mode
8
8
  - 1.9.2
9
- - rbx-19mode
9
+ - rbx-2.2.0
10
10
  - ruby-head
11
- - jruby-head
12
11
  - 1.8.7
13
- - rbx-18mode
14
12
  notifications:
15
13
  recipients:
16
- - michael@novemberain.com
14
+ - michael@rabbitmq.com
17
15
  matrix:
18
16
  allow_failures:
19
17
  - rvm: ruby-head
@@ -1,3 +1,23 @@
1
+ ## Changes between 1.8.0 and 1.9.0
2
+
3
+ ### Performance Improvements in AMQ::BitSet
4
+
5
+ `AMQ::BitSet#next_clear_bit` is now drastically more efficient
6
+ (down from 6 minutes for 10,000 iterations to 4 seconds for 65,536 iterations).
7
+
8
+ Contributed by Doug Rohrer, Dave Anderson, and Jason Voegele from
9
+ [Neo](http://www.neo.com).
10
+
11
+
12
+ ## Changes between 1.7.0 and 1.8.0
13
+
14
+ ### Body Framing Fix
15
+
16
+ Messages exactly 128 Kb in size are now framed correctly.
17
+
18
+ Contributed by Nicolas Viennot.
19
+
20
+
1
21
  ## Changes between 1.6.0 and 1.7.0
2
22
 
3
23
  ### connection.blocked Support
data/README.md CHANGED
@@ -43,7 +43,7 @@ amq-protocol is maintained by [Michael Klishin](https://github.com/michaelklishi
43
43
  [![Build Status](https://secure.travis-ci.org/ruby-amqp/amq-protocol.png)](https://travis-ci.org/ruby-amqp/amq-protocol)
44
44
 
45
45
 
46
- ## Development
46
+ ## Issues
47
47
 
48
48
  Please report any issues you may find to our [Issue tracker](http://github.com/ruby-amqp/amq-protocol/issues) on GitHub.
49
49
 
@@ -0,0 +1,34 @@
1
+ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
2
+
3
+ require 'amq/int_allocator'
4
+ require "benchmark"
5
+
6
+ allocator = AMQ::IntAllocator.new(1,65535)
7
+ mutex = Mutex.new
8
+
9
+ Benchmark.bm do |x|
10
+
11
+
12
+ x.report("allocate") do
13
+ allocator = AMQ::IntAllocator.new(1,65535)
14
+ 1.upto(65534) do |i|
15
+ mutex.synchronize do
16
+ n = allocator.allocate
17
+ raise 'it be broke' unless n == i
18
+ end
19
+ end
20
+ end
21
+
22
+ x.report("allocate_with_release") do
23
+ allocator = AMQ::IntAllocator.new(1,65535)
24
+ 1.upto(65534) do |i|
25
+ mutex.synchronize do
26
+ n = allocator.allocate
27
+ if i % 5 == 0
28
+ allocator.release(n)
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ end
@@ -7,6 +7,7 @@ module AMQ
7
7
  # Originally part of amqp gem. Extracted to make it possible for Bunny to use it.
8
8
  class BitSet
9
9
 
10
+ attr_reader :words_in_use
10
11
  #
11
12
  # API
12
13
  #
@@ -28,8 +29,10 @@ module AMQ
28
29
  # @param [Integer] A bit to set
29
30
  # @api public
30
31
  def set(i)
32
+ check_range(i)
31
33
  w = self.word_index(i)
32
- @words[w] |= (1 << i)
34
+ result = @words[w] |= (1 << (i % BITS_PER_WORD))
35
+ result
33
36
  end # set(i)
34
37
 
35
38
  # Fetches flag value for given bit.
@@ -38,9 +41,10 @@ module AMQ
38
41
  # @return [Boolean] true if given bit is set, false otherwise
39
42
  # @api public
40
43
  def get(i)
44
+ check_range(i)
41
45
  w = self.word_index(i)
42
46
 
43
- (@words[w] & (1 << i)) != 0
47
+ (@words[w] & (1 << i % BITS_PER_WORD)) != 0
44
48
  end # get(i)
45
49
  alias [] get
46
50
 
@@ -49,10 +53,12 @@ module AMQ
49
53
  # @param [Integer] A bit to unset
50
54
  # @api public
51
55
  def unset(i)
56
+ check_range(i)
52
57
  w = self.word_index(i)
53
58
  return if w.nil?
54
59
 
55
- @words[w] &= ~(1 << i)
60
+ result = @words[w] &= ~(1 << i % BITS_PER_WORD)
61
+ result
56
62
  end # unset(i)
57
63
 
58
64
  # Clears all bits in the set
@@ -61,22 +67,53 @@ module AMQ
61
67
  self.init_words(@nbits)
62
68
  end # clear
63
69
 
70
+ def next_clear_bit()
71
+ @words.each_with_index do |word, i|
72
+ if word == WORD_MASK
73
+ next
74
+ end
75
+ return i * BITS_PER_WORD + BitSet.number_of_trailing_ones(word)
76
+ end
77
+ -1
78
+ end # next_clear_bit
79
+
80
+ def to_s
81
+ result = ""
82
+ @words.each do |w|
83
+ result += w.to_s(2).rjust(BITS_PER_WORD,'0') + ":"
84
+ end
85
+ result
86
+ end # to_s
64
87
 
65
88
  #
66
89
  # Implementation
67
90
  #
68
91
 
92
+ # @private
93
+ def self.number_of_trailing_ones(num)
94
+ 0.upto(BITS_PER_WORD) do |bit|
95
+ return bit if num[bit] == 0
96
+ end
97
+ BITS_PER_WORD
98
+ end # number_of_trailing_ones
99
+
100
+ # @private
101
+ def word_index(i)
102
+ i >> ADDRESS_BITS_PER_WORD
103
+ end # word_index
104
+
69
105
  protected
70
106
 
71
107
  # @private
72
108
  def init_words(nbits)
73
109
  n = word_index(nbits-1) + 1
74
- @words = Array.new(n) { 1 }
110
+ @words = Array.new(n) { 0 }
75
111
  end # init_words
76
112
 
77
- # @private
78
- def word_index(i)
79
- i >> ADDRESS_BITS_PER_WORD
80
- end # word_index(i)
113
+ def check_range(i)
114
+ if i < 0 || i >= @nbits
115
+ raise IndexError.new("Cannot access bit #{i} from a BitSet with #{@nbits} bits")
116
+ end
117
+ end # check_range
81
118
  end # BitSet
82
119
  end # AMQ
@@ -43,10 +43,16 @@ module AMQ
43
43
  #
44
44
  # @return [Integer] Allocated integer if allocation succeeded. nil otherwise.
45
45
  def allocate
46
- if n = find_unallocated_position
47
- @free_set.set(n)
48
46
 
49
- n
47
+ if n = @free_set.next_clear_bit
48
+
49
+ if n < @hi - 1 then
50
+ @free_set.set(n)
51
+ n + 1
52
+ else
53
+ -1
54
+ end
55
+
50
56
  else
51
57
  -1
52
58
  end
@@ -57,13 +63,13 @@ module AMQ
57
63
  #
58
64
  # @return [NilClass] nil
59
65
  def free(reservation)
60
- @free_set.unset(reservation)
66
+ @free_set.unset(reservation-1)
61
67
  end # free(reservation)
62
68
  alias release free
63
69
 
64
70
  # @return [Boolean] true if provided argument was previously allocated, false otherwise
65
71
  def allocated?(reservation)
66
- @free_set.get(reservation)
72
+ @free_set.get(reservation-1)
67
73
  end # allocated?(reservation)
68
74
 
69
75
  # Releases the whole allocation range
@@ -75,22 +81,5 @@ module AMQ
75
81
 
76
82
  protected
77
83
 
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
84
  end # IntAllocator
96
85
  end # AMQ
@@ -1,5 +1,5 @@
1
1
  module AMQ
2
2
  module Protocol
3
- VERSION = "1.8.0"
3
+ VERSION = "1.9.0"
4
4
  end # Protocol
5
5
  end # AMQ
@@ -19,6 +19,32 @@ describe AMQ::BitSet do
19
19
  # Examples
20
20
  #
21
21
 
22
+ describe "#new" do
23
+ it "has no bits set at the start" do
24
+ bs = AMQ::BitSet.new(128)
25
+ 0.upto(127) do |i|
26
+ bs[i].should == false
27
+ end
28
+ end # it
29
+ end # describe
30
+
31
+ describe "#word_index" do
32
+ subject do
33
+ described_class.new(nbits)
34
+ end
35
+ it "returns 0 when the word is between 0 and 63" do
36
+ subject.word_index(0).should == 0
37
+ subject.word_index(63).should == 0
38
+ end # it
39
+ it "returns 1 when the word is between 64 and 127" do
40
+ subject.word_index(64).should == 1
41
+ subject.word_index(127).should == 1
42
+ end # it
43
+ it "returns 2 when the word is between 128 and another number" do
44
+ subject.word_index(128).should == 2
45
+ end # it
46
+ end # describe
47
+
22
48
  describe "#get, #[]" do
23
49
  describe "when bit at given position is set" do
24
50
  subject do
@@ -41,6 +67,19 @@ describe AMQ::BitSet do
41
67
  subject.get(5).should be_false
42
68
  end # it
43
69
  end # describe
70
+
71
+ describe "when index out of range" do
72
+ subject do
73
+ described_class.new(nbits)
74
+ end
75
+
76
+ it "should raise IndexError for negative index" do
77
+ lambda { subject.get(-1) }.should raise_error(IndexError)
78
+ end # it
79
+ it "should raise IndexError for index >= number of bits" do
80
+ lambda { subject.get(nbits) }.should raise_error(IndexError)
81
+ end # it
82
+ end # describe
44
83
  end # describe
45
84
 
46
85
 
@@ -56,7 +95,7 @@ describe AMQ::BitSet do
56
95
  subject.set(3)
57
96
  subject[3].should be_true
58
97
  end # it
59
- end
98
+ end # describe
60
99
 
61
100
  describe "when bit at given position is off" do
62
101
  subject do
@@ -72,7 +111,20 @@ describe AMQ::BitSet do
72
111
 
73
112
  subject.set(3387)
74
113
  subject.get(3387).should be_true
114
+ end # it
115
+ end # describe
116
+
117
+ describe "when index out of range" do
118
+ subject do
119
+ described_class.new(nbits)
75
120
  end
121
+
122
+ it "should raise IndexError for negative index" do
123
+ lambda { subject.set(-1) }.should raise_error(IndexError)
124
+ end # it
125
+ it "should raise IndexError for index >= number of bits" do
126
+ lambda { subject.set(nbits) }.should raise_error(IndexError)
127
+ end # it
76
128
  end # describe
77
129
  end # describe
78
130
 
@@ -103,6 +155,19 @@ describe AMQ::BitSet do
103
155
  subject.get(3).should be_false
104
156
  end # it
105
157
  end # describe
158
+
159
+ describe "when index out of range" do
160
+ subject do
161
+ described_class.new(nbits)
162
+ end
163
+
164
+ it "should raise IndexError for negative index" do
165
+ lambda { subject.unset(-1) }.should raise_error(IndexError)
166
+ end # it
167
+ it "should raise IndexError for index >= number of bits" do
168
+ lambda { subject.unset(nbits) }.should raise_error(IndexError)
169
+ end # it
170
+ end # describe
106
171
  end # describe
107
172
 
108
173
 
@@ -123,6 +188,45 @@ describe AMQ::BitSet do
123
188
 
124
189
  subject.get(3).should be_false
125
190
  subject.get(7668).should be_false
191
+ end # it
192
+ end # describe
193
+
194
+ describe "#number_of_trailing_ones" do
195
+ it "calculates them" do
196
+ described_class.number_of_trailing_ones(0).should == 0
197
+ described_class.number_of_trailing_ones(1).should == 1
198
+ described_class.number_of_trailing_ones(2).should == 0
199
+ described_class.number_of_trailing_ones(3).should == 2
200
+ described_class.number_of_trailing_ones(4).should == 0
201
+ end # it
202
+ end # describe
203
+
204
+ describe '#next_clear_bit' do
205
+ subject do
206
+ described_class.new(255)
126
207
  end
127
- end
208
+ it "returns sequential values when none have been returned" do
209
+ subject.next_clear_bit.should == 0
210
+ subject.set(0)
211
+ subject.next_clear_bit.should == 1
212
+ subject.set(1)
213
+ subject.next_clear_bit.should == 2
214
+ subject.unset(1)
215
+ subject.next_clear_bit.should == 1
216
+ end # it
217
+
218
+ it "returns the same number as long as nothing is set" do
219
+ subject.next_clear_bit.should == 0
220
+ subject.next_clear_bit.should == 0
221
+ end # it
222
+
223
+ it "handles more than 128 bits" do
224
+ 0.upto(254) do |i|
225
+ subject.set(i)
226
+ subject.next_clear_bit.should == i + 1
227
+ end
228
+ subject.unset(254)
229
+ subject.get(254).should be_false
230
+ end # it
231
+ end # describe
128
232
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amq-protocol
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Stastny
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-09-17 00:00:00.000000000 Z
14
+ date: 2013-11-21 00:00:00.000000000 Z
15
15
  dependencies: []
16
16
  description: |2
17
17
  amq-protocol is an AMQP 0.9.1 serialization library for Ruby. It is not an
@@ -35,6 +35,7 @@ files:
35
35
  - README.md
36
36
  - Rakefile
37
37
  - amq-protocol.gemspec
38
+ - benchmarks/int_allocator.rb
38
39
  - benchmarks/pure/body_framing_with_256k_payload.rb
39
40
  - benchmarks/pure/body_framing_with_2k_payload.rb
40
41
  - codegen/__init__.py
@@ -102,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
103
  version: '0'
103
104
  requirements: []
104
105
  rubyforge_project: amq-protocol
105
- rubygems_version: 2.0.5
106
+ rubygems_version: 2.1.6
106
107
  signing_key:
107
108
  specification_version: 4
108
109
  summary: AMQP 0.9.1 encoder & decoder.