totty-amqp 0.6.7.1.totty
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/README +143 -0
- data/Rakefile +20 -0
- data/amqp.todo +32 -0
- data/doc/EXAMPLE_01_PINGPONG +2 -0
- data/doc/EXAMPLE_02_CLOCK +2 -0
- data/doc/EXAMPLE_03_STOCKS +2 -0
- data/doc/EXAMPLE_04_MULTICLOCK +2 -0
- data/doc/EXAMPLE_05_ACK +2 -0
- data/doc/EXAMPLE_05_POP +2 -0
- data/doc/EXAMPLE_06_HASHTABLE +2 -0
- data/examples/amqp/simple.rb +79 -0
- data/examples/mq/ack.rb +45 -0
- data/examples/mq/clock.rb +56 -0
- data/examples/mq/hashtable.rb +52 -0
- data/examples/mq/internal.rb +49 -0
- data/examples/mq/logger.rb +88 -0
- data/examples/mq/multiclock.rb +49 -0
- data/examples/mq/pingpong.rb +45 -0
- data/examples/mq/pop.rb +43 -0
- data/examples/mq/primes-simple.rb +19 -0
- data/examples/mq/primes.rb +99 -0
- data/examples/mq/stocks.rb +58 -0
- data/lib/amqp/buffer.rb +395 -0
- data/lib/amqp/client.rb +210 -0
- data/lib/amqp/frame.rb +124 -0
- data/lib/amqp/protocol.rb +212 -0
- data/lib/amqp/server.rb +99 -0
- data/lib/amqp/spec.rb +832 -0
- data/lib/amqp/version.rb +3 -0
- data/lib/amqp.rb +152 -0
- data/lib/ext/blankslate.rb +7 -0
- data/lib/ext/em.rb +8 -0
- data/lib/ext/emfork.rb +69 -0
- data/lib/mq/exchange.rb +314 -0
- data/lib/mq/header.rb +33 -0
- data/lib/mq/logger.rb +89 -0
- data/lib/mq/queue.rb +455 -0
- data/lib/mq/rpc.rb +100 -0
- data/lib/mq.rb +877 -0
- data/old/README +30 -0
- data/old/Rakefile +12 -0
- data/old/amqp-0.8.json +606 -0
- data/old/amqp_spec.rb +796 -0
- data/old/amqpc.rb +695 -0
- data/old/codegen.rb +148 -0
- data/protocol/amqp-0.8.json +617 -0
- data/protocol/amqp-0.8.xml +3908 -0
- data/protocol/codegen.rb +173 -0
- data/protocol/doc.txt +281 -0
- data/research/api.rb +88 -0
- data/research/primes-forked.rb +63 -0
- data/research/primes-processes.rb +135 -0
- data/research/primes-threaded.rb +49 -0
- data/totty-amqp.gemspec +87 -0
- metadata +142 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + '/../../lib'
|
2
|
+
require 'mq'
|
3
|
+
|
4
|
+
AMQP.start(:host => 'localhost') do
|
5
|
+
|
6
|
+
def log *args
|
7
|
+
p [ Time.now, *args ]
|
8
|
+
end
|
9
|
+
|
10
|
+
# AMQP.logging = true
|
11
|
+
|
12
|
+
amq = MQ.new
|
13
|
+
EM.add_periodic_timer(1){
|
14
|
+
puts
|
15
|
+
|
16
|
+
log :sending, 'ping'
|
17
|
+
amq.queue('one').publish('ping')
|
18
|
+
}
|
19
|
+
|
20
|
+
amq = MQ.new
|
21
|
+
amq.queue('one').subscribe{ |msg|
|
22
|
+
log 'one', :received, msg, :sending, 'pong'
|
23
|
+
amq.queue('two').publish('pong')
|
24
|
+
}
|
25
|
+
|
26
|
+
amq = MQ.new
|
27
|
+
amq.queue('two').subscribe{ |msg|
|
28
|
+
log 'two', :received, msg
|
29
|
+
}
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
__END__
|
34
|
+
|
35
|
+
[Sun Jul 20 03:52:24 -0700 2008, :sending, "ping"]
|
36
|
+
[Sun Jul 20 03:52:24 -0700 2008, "one", :received, "ping", :sending, "pong"]
|
37
|
+
[Sun Jul 20 03:52:24 -0700 2008, "two", :received, "pong"]
|
38
|
+
|
39
|
+
[Sun Jul 20 03:52:25 -0700 2008, :sending, "ping"]
|
40
|
+
[Sun Jul 20 03:52:25 -0700 2008, "one", :received, "ping", :sending, "pong"]
|
41
|
+
[Sun Jul 20 03:52:25 -0700 2008, "two", :received, "pong"]
|
42
|
+
|
43
|
+
[Sun Jul 20 03:52:26 -0700 2008, :sending, "ping"]
|
44
|
+
[Sun Jul 20 03:52:26 -0700 2008, "one", :received, "ping", :sending, "pong"]
|
45
|
+
[Sun Jul 20 03:52:26 -0700 2008, "two", :received, "pong"]
|
data/examples/mq/pop.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + '/../../lib'
|
2
|
+
require 'mq'
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
Signal.trap('INT') { AMQP.stop{ EM.stop } }
|
6
|
+
Signal.trap('TERM'){ AMQP.stop{ EM.stop } }
|
7
|
+
|
8
|
+
AMQP.start do
|
9
|
+
queue = MQ.queue('awesome')
|
10
|
+
|
11
|
+
queue.publish('Totally rad 1')
|
12
|
+
queue.publish('Totally rad 2')
|
13
|
+
EM.add_timer(5){ queue.publish('Totally rad 3') }
|
14
|
+
|
15
|
+
queue.pop{ |msg|
|
16
|
+
unless msg
|
17
|
+
# queue was empty
|
18
|
+
p [Time.now, :queue_empty!]
|
19
|
+
|
20
|
+
# try again in 1 second
|
21
|
+
EM.add_timer(1){ queue.pop }
|
22
|
+
else
|
23
|
+
# process this message
|
24
|
+
p [Time.now, msg]
|
25
|
+
|
26
|
+
# get the next message in the queue
|
27
|
+
queue.pop
|
28
|
+
end
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
__END__
|
33
|
+
|
34
|
+
[Wed Oct 15 15:24:30 -0700 2008, "Totally rad 1"]
|
35
|
+
[Wed Oct 15 15:24:30 -0700 2008, "Totally rad 2"]
|
36
|
+
[Wed Oct 15 15:24:30 -0700 2008, :queue_empty!]
|
37
|
+
[Wed Oct 15 15:24:31 -0700 2008, :queue_empty!]
|
38
|
+
[Wed Oct 15 15:24:32 -0700 2008, :queue_empty!]
|
39
|
+
[Wed Oct 15 15:24:33 -0700 2008, :queue_empty!]
|
40
|
+
[Wed Oct 15 15:24:34 -0700 2008, :queue_empty!]
|
41
|
+
[Wed Oct 15 15:24:35 -0700 2008, "Totally rad 3"]
|
42
|
+
[Wed Oct 15 15:24:35 -0700 2008, :queue_empty!]
|
43
|
+
[Wed Oct 15 15:24:36 -0700 2008, :queue_empty!]
|
@@ -0,0 +1,19 @@
|
|
1
|
+
MAX = 1000
|
2
|
+
|
3
|
+
class Fixnum
|
4
|
+
def prime?
|
5
|
+
('1' * self) !~ /^1?$|^(11+?)\1+$/
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class PrimeChecker
|
10
|
+
def is_prime? number
|
11
|
+
number.prime?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
prime_checker = PrimeChecker.new
|
16
|
+
|
17
|
+
(10_000...(10_000+MAX)).each do |n|
|
18
|
+
prime_checker.is_prime? n
|
19
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + '/../../lib'
|
2
|
+
require 'mq'
|
3
|
+
|
4
|
+
# check MAX numbers for prime-ness
|
5
|
+
MAX = 1000
|
6
|
+
|
7
|
+
# logging
|
8
|
+
def log *args
|
9
|
+
p args
|
10
|
+
end
|
11
|
+
|
12
|
+
# spawn workers
|
13
|
+
workers = ARGV[0] ? (Integer(ARGV[0]) rescue 1) : 1
|
14
|
+
AMQP.fork(workers) do
|
15
|
+
|
16
|
+
log MQ.id, :started
|
17
|
+
|
18
|
+
class Fixnum
|
19
|
+
def prime?
|
20
|
+
('1' * self) !~ /^1?$|^(11+?)\1+$/
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class PrimeChecker
|
25
|
+
def is_prime? number
|
26
|
+
log "prime checker #{MQ.id}", :prime?, number
|
27
|
+
number.prime?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
MQ.rpc('prime checker', PrimeChecker.new)
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
# use workers to check which numbers are prime
|
36
|
+
AMQP.start(:host => 'localhost') do
|
37
|
+
|
38
|
+
prime_checker = MQ.rpc('prime checker')
|
39
|
+
|
40
|
+
(10_000...(10_000+MAX)).each do |num|
|
41
|
+
log :checking, num
|
42
|
+
|
43
|
+
prime_checker.is_prime?(num) { |is_prime|
|
44
|
+
log :prime?, num, is_prime
|
45
|
+
(@primes||=[]) << num if is_prime
|
46
|
+
|
47
|
+
if (@responses = (@responses || 0) + 1) == MAX
|
48
|
+
log :primes=, @primes
|
49
|
+
EM.stop_event_loop
|
50
|
+
end
|
51
|
+
}
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
__END__
|
58
|
+
|
59
|
+
$ uname -a
|
60
|
+
Linux gc 2.6.24-ARCH #1 SMP PREEMPT Sun Mar 30 10:50:22 CEST 2008 x86_64 Intel(R) Xeon(R) CPU X3220 @ 2.40GHz GenuineIntel GNU/Linux
|
61
|
+
|
62
|
+
$ cat /proc/cpuinfo | grep processor | wc -l
|
63
|
+
4
|
64
|
+
|
65
|
+
$ time ruby primes-simple.rb
|
66
|
+
|
67
|
+
real 0m16.055s
|
68
|
+
user 0m16.052s
|
69
|
+
sys 0m0.000s
|
70
|
+
|
71
|
+
$ time ruby primes.rb 1 >/dev/null
|
72
|
+
|
73
|
+
real 0m18.278s
|
74
|
+
user 0m0.993s
|
75
|
+
sys 0m0.027s
|
76
|
+
|
77
|
+
$ time ruby primes.rb 2 >/dev/null
|
78
|
+
|
79
|
+
real 0m17.316s
|
80
|
+
user 0m0.967s
|
81
|
+
sys 0m0.053s
|
82
|
+
|
83
|
+
$ time ruby primes.rb 4 >/dev/null
|
84
|
+
|
85
|
+
real 0m8.229s
|
86
|
+
user 0m1.010s
|
87
|
+
sys 0m0.030s
|
88
|
+
|
89
|
+
$ time ruby primes.rb 8 >/dev/null
|
90
|
+
|
91
|
+
real 0m5.893s
|
92
|
+
user 0m1.023s
|
93
|
+
sys 0m0.050s
|
94
|
+
|
95
|
+
$ time ruby primes.rb 16 >/dev/null
|
96
|
+
|
97
|
+
real 0m5.601s
|
98
|
+
user 0m0.990s
|
99
|
+
sys 0m0.043s
|
@@ -0,0 +1,58 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + '/../../lib'
|
2
|
+
require 'mq'
|
3
|
+
|
4
|
+
AMQP.start(:host => 'localhost') do
|
5
|
+
|
6
|
+
def log *args
|
7
|
+
p [ Time.now, *args ]
|
8
|
+
end
|
9
|
+
|
10
|
+
def publish_stock_prices
|
11
|
+
mq = MQ.new
|
12
|
+
EM.add_periodic_timer(1){
|
13
|
+
puts
|
14
|
+
|
15
|
+
{ :appl => 170+rand(1000)/100.0,
|
16
|
+
:msft => 22+rand(500)/100.0
|
17
|
+
}.each do |stock, price|
|
18
|
+
stock = "usd.#{stock}"
|
19
|
+
|
20
|
+
log :publishing, stock, price
|
21
|
+
mq.topic('stocks').publish(price, :key => stock)
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def watch_appl_stock
|
27
|
+
mq = MQ.new
|
28
|
+
mq.queue('apple stock').bind(mq.topic('stocks'), :key => 'usd.appl').subscribe{ |price|
|
29
|
+
log 'apple stock', price
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def watch_us_stocks
|
34
|
+
mq = MQ.new
|
35
|
+
mq.queue('us stocks').bind(mq.topic('stocks'), :key => 'usd.*').subscribe{ |info, price|
|
36
|
+
log 'us stock', info.routing_key, price
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
publish_stock_prices
|
41
|
+
watch_appl_stock
|
42
|
+
watch_us_stocks
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
__END__
|
47
|
+
|
48
|
+
[Fri Aug 15 01:39:00 -0700 2008, :publishing, "usd.appl", 173.45]
|
49
|
+
[Fri Aug 15 01:39:00 -0700 2008, :publishing, "usd.msft", 26.98]
|
50
|
+
[Fri Aug 15 01:39:00 -0700 2008, "apple stock", "173.45"]
|
51
|
+
[Fri Aug 15 01:39:00 -0700 2008, "us stock", "usd.appl", "173.45"]
|
52
|
+
[Fri Aug 15 01:39:00 -0700 2008, "us stock", "usd.msft", "26.98"]
|
53
|
+
|
54
|
+
[Fri Aug 15 01:39:01 -0700 2008, :publishing, "usd.appl", 179.72]
|
55
|
+
[Fri Aug 15 01:39:01 -0700 2008, :publishing, "usd.msft", 26.56]
|
56
|
+
[Fri Aug 15 01:39:01 -0700 2008, "apple stock", "179.72"]
|
57
|
+
[Fri Aug 15 01:39:01 -0700 2008, "us stock", "usd.appl", "179.72"]
|
58
|
+
[Fri Aug 15 01:39:01 -0700 2008, "us stock", "usd.msft", "26.56"]
|
data/lib/amqp/buffer.rb
ADDED
@@ -0,0 +1,395 @@
|
|
1
|
+
if [].map.respond_to? :with_index
|
2
|
+
class Array #:nodoc:
|
3
|
+
def enum_with_index
|
4
|
+
each.with_index
|
5
|
+
end
|
6
|
+
end
|
7
|
+
else
|
8
|
+
require 'enumerator'
|
9
|
+
end
|
10
|
+
|
11
|
+
module AMQP
|
12
|
+
class Buffer #:nodoc: all
|
13
|
+
class Overflow < StandardError; end
|
14
|
+
class InvalidType < StandardError; end
|
15
|
+
|
16
|
+
def initialize data = ''
|
17
|
+
@data = data
|
18
|
+
@pos = 0
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :pos
|
22
|
+
|
23
|
+
def data
|
24
|
+
@data.clone
|
25
|
+
end
|
26
|
+
alias :contents :data
|
27
|
+
alias :to_s :data
|
28
|
+
|
29
|
+
def << data
|
30
|
+
@data << data.to_s
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def length
|
35
|
+
@data.length
|
36
|
+
end
|
37
|
+
|
38
|
+
def empty?
|
39
|
+
pos == length
|
40
|
+
end
|
41
|
+
|
42
|
+
def rewind
|
43
|
+
@pos = 0
|
44
|
+
end
|
45
|
+
|
46
|
+
def read_properties *types
|
47
|
+
types.shift if types.first == :properties
|
48
|
+
|
49
|
+
i = 0
|
50
|
+
values = []
|
51
|
+
|
52
|
+
while props = read(:short)
|
53
|
+
(0..14).each do |n|
|
54
|
+
# no more property types
|
55
|
+
break unless types[i]
|
56
|
+
|
57
|
+
# if flag is set
|
58
|
+
if props & (1<<(15-n)) != 0
|
59
|
+
if types[i] == :bit
|
60
|
+
# bit values exist in flags only
|
61
|
+
values << true
|
62
|
+
else
|
63
|
+
# save type name for later reading
|
64
|
+
values << types[i]
|
65
|
+
end
|
66
|
+
else
|
67
|
+
# property not set or is false bit
|
68
|
+
values << (types[i] == :bit ? false : nil)
|
69
|
+
end
|
70
|
+
|
71
|
+
i+=1
|
72
|
+
end
|
73
|
+
|
74
|
+
# bit(0) == 0 means no more property flags
|
75
|
+
break unless props & 1 == 1
|
76
|
+
end
|
77
|
+
|
78
|
+
values.map do |value|
|
79
|
+
value.is_a?(Symbol) ? read(value) : value
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def read *types
|
84
|
+
if types.first == :properties
|
85
|
+
return read_properties(*types)
|
86
|
+
end
|
87
|
+
|
88
|
+
values = types.map do |type|
|
89
|
+
case type
|
90
|
+
when :octet
|
91
|
+
_read(1, 'C')
|
92
|
+
when :short
|
93
|
+
_read(2, 'n')
|
94
|
+
when :long
|
95
|
+
_read(4, 'N')
|
96
|
+
when :longlong
|
97
|
+
upper, lower = _read(8, 'NN')
|
98
|
+
upper << 32 | lower
|
99
|
+
when :shortstr
|
100
|
+
_read read(:octet)
|
101
|
+
when :longstr
|
102
|
+
_read read(:long)
|
103
|
+
when :timestamp
|
104
|
+
Time.at read(:longlong)
|
105
|
+
when :table
|
106
|
+
t = Hash.new
|
107
|
+
|
108
|
+
table = Buffer.new(read(:longstr))
|
109
|
+
until table.empty?
|
110
|
+
key, type = table.read(:shortstr, :octet)
|
111
|
+
key = key.intern
|
112
|
+
t[key] ||= case type
|
113
|
+
when 83 # 'S'
|
114
|
+
table.read(:longstr)
|
115
|
+
when 73 # 'I'
|
116
|
+
table.read(:long)
|
117
|
+
when 68 # 'D'
|
118
|
+
exp = table.read(:octet)
|
119
|
+
num = table.read(:long)
|
120
|
+
num / 10.0**exp
|
121
|
+
when 84 # 'T'
|
122
|
+
table.read(:timestamp)
|
123
|
+
when 70 # 'F'
|
124
|
+
table.read(:table)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
t
|
129
|
+
when :bit
|
130
|
+
if (@bits ||= []).empty?
|
131
|
+
val = read(:octet)
|
132
|
+
@bits = (0..7).map{|i| (val & 1<<i) != 0 }
|
133
|
+
end
|
134
|
+
|
135
|
+
@bits.shift
|
136
|
+
else
|
137
|
+
raise InvalidType, "Cannot read data of type #{type}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
types.size == 1 ? values.first : values
|
142
|
+
end
|
143
|
+
|
144
|
+
def write type, data
|
145
|
+
case type
|
146
|
+
when :octet
|
147
|
+
_write(data, 'C')
|
148
|
+
when :short
|
149
|
+
_write(data, 'n')
|
150
|
+
when :long
|
151
|
+
_write(data, 'N')
|
152
|
+
when :longlong
|
153
|
+
lower = data & 0xffffffff
|
154
|
+
upper = (data & ~0xffffffff) >> 32
|
155
|
+
_write([upper, lower], 'NN')
|
156
|
+
when :shortstr
|
157
|
+
data = (data || '').to_s
|
158
|
+
_write([data.length, data], 'Ca*')
|
159
|
+
when :longstr
|
160
|
+
if data.is_a? Hash
|
161
|
+
write(:table, data)
|
162
|
+
else
|
163
|
+
data = (data || '').to_s
|
164
|
+
_write([data.length, data], 'Na*')
|
165
|
+
end
|
166
|
+
when :timestamp
|
167
|
+
write(:longlong, data.to_i)
|
168
|
+
when :table
|
169
|
+
data ||= {}
|
170
|
+
write :longstr, (data.inject(Buffer.new) do |table, (key, value)|
|
171
|
+
table.write(:shortstr, key.to_s)
|
172
|
+
|
173
|
+
case value
|
174
|
+
when String
|
175
|
+
table.write(:octet, 83) # 'S'
|
176
|
+
table.write(:longstr, value.to_s)
|
177
|
+
when Fixnum
|
178
|
+
table.write(:octet, 73) # 'I'
|
179
|
+
table.write(:long, value)
|
180
|
+
when Float
|
181
|
+
table.write(:octet, 68) # 'D'
|
182
|
+
# XXX there's gotta be a better way to do this..
|
183
|
+
exp = value.to_s.split('.').last.length
|
184
|
+
num = value * 10**exp
|
185
|
+
table.write(:octet, exp)
|
186
|
+
table.write(:long, num)
|
187
|
+
when Time
|
188
|
+
table.write(:octet, 84) # 'T'
|
189
|
+
table.write(:timestamp, value)
|
190
|
+
when Hash
|
191
|
+
table.write(:octet, 70) # 'F'
|
192
|
+
table.write(:table, value)
|
193
|
+
end
|
194
|
+
|
195
|
+
table
|
196
|
+
end)
|
197
|
+
when :bit
|
198
|
+
[*data].to_enum(:each_slice, 8).each{|bits|
|
199
|
+
write(:octet, bits.enum_with_index.inject(0){ |byte, (bit, i)|
|
200
|
+
byte |= 1<<i if bit
|
201
|
+
byte
|
202
|
+
})
|
203
|
+
}
|
204
|
+
when :properties
|
205
|
+
values = []
|
206
|
+
data.enum_with_index.inject(0) do |short, ((type, value), i)|
|
207
|
+
n = i % 15
|
208
|
+
last = i+1 == data.size
|
209
|
+
|
210
|
+
if (n == 0 and i != 0) or last
|
211
|
+
if data.size > i+1
|
212
|
+
short |= 1<<0
|
213
|
+
elsif last and value
|
214
|
+
values << [type,value]
|
215
|
+
short |= 1<<(15-n)
|
216
|
+
end
|
217
|
+
|
218
|
+
write(:short, short)
|
219
|
+
short = 0
|
220
|
+
end
|
221
|
+
|
222
|
+
if value and !last
|
223
|
+
values << [type,value]
|
224
|
+
short |= 1<<(15-n)
|
225
|
+
end
|
226
|
+
|
227
|
+
short
|
228
|
+
end
|
229
|
+
|
230
|
+
values.each do |type, value|
|
231
|
+
write(type, value) unless type == :bit
|
232
|
+
end
|
233
|
+
else
|
234
|
+
raise InvalidType, "Cannot write data of type #{type}"
|
235
|
+
end
|
236
|
+
|
237
|
+
self
|
238
|
+
end
|
239
|
+
|
240
|
+
def extract
|
241
|
+
begin
|
242
|
+
cur_data, cur_pos = @data.clone, @pos
|
243
|
+
yield self
|
244
|
+
rescue Overflow
|
245
|
+
@data, @pos = cur_data, cur_pos
|
246
|
+
nil
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def _read size, pack = nil
|
251
|
+
if @pos + size > length
|
252
|
+
raise Overflow
|
253
|
+
else
|
254
|
+
data = @data[@pos,size]
|
255
|
+
@data[@pos,size] = ''
|
256
|
+
if pack
|
257
|
+
data = data.unpack(pack)
|
258
|
+
data = data.pop if data.size == 1
|
259
|
+
end
|
260
|
+
data
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def _write data, pack = nil
|
265
|
+
data = [*data].pack(pack) if pack
|
266
|
+
@data[@pos,0] = data
|
267
|
+
@pos += data.length
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
if $0 =~ /bacon/ or $0 == __FILE__
|
273
|
+
require 'bacon'
|
274
|
+
include AMQP
|
275
|
+
|
276
|
+
describe Buffer do
|
277
|
+
before do
|
278
|
+
@buf = Buffer.new
|
279
|
+
end
|
280
|
+
|
281
|
+
should 'have contents' do
|
282
|
+
@buf.contents.should == ''
|
283
|
+
end
|
284
|
+
|
285
|
+
should 'initialize with data' do
|
286
|
+
@buf = Buffer.new('abc')
|
287
|
+
@buf.contents.should == 'abc'
|
288
|
+
end
|
289
|
+
|
290
|
+
should 'append raw data' do
|
291
|
+
@buf << 'abc'
|
292
|
+
@buf << 'def'
|
293
|
+
@buf.contents.should == 'abcdef'
|
294
|
+
end
|
295
|
+
|
296
|
+
should 'append other buffers' do
|
297
|
+
@buf << Buffer.new('abc')
|
298
|
+
@buf.data.should == 'abc'
|
299
|
+
end
|
300
|
+
|
301
|
+
should 'have a position' do
|
302
|
+
@buf.pos.should == 0
|
303
|
+
end
|
304
|
+
|
305
|
+
should 'have a length' do
|
306
|
+
@buf.length.should == 0
|
307
|
+
@buf << 'abc'
|
308
|
+
@buf.length.should == 3
|
309
|
+
end
|
310
|
+
|
311
|
+
should 'know the end' do
|
312
|
+
@buf.empty?.should == true
|
313
|
+
end
|
314
|
+
|
315
|
+
should 'read and write data' do
|
316
|
+
@buf._write('abc')
|
317
|
+
@buf.rewind
|
318
|
+
@buf._read(2).should == 'ab'
|
319
|
+
@buf._read(1).should == 'c'
|
320
|
+
end
|
321
|
+
|
322
|
+
should 'raise on overflow' do
|
323
|
+
lambda{ @buf._read(1) }.should.raise Buffer::Overflow
|
324
|
+
end
|
325
|
+
|
326
|
+
should 'raise on invalid types' do
|
327
|
+
lambda{ @buf.read(:junk) }.should.raise Buffer::InvalidType
|
328
|
+
lambda{ @buf.write(:junk, 1) }.should.raise Buffer::InvalidType
|
329
|
+
end
|
330
|
+
|
331
|
+
{ :octet => 0b10101010,
|
332
|
+
:short => 100,
|
333
|
+
:long => 100_000_000,
|
334
|
+
:longlong => 666_555_444_333_222_111,
|
335
|
+
:shortstr => 'hello',
|
336
|
+
:longstr => 'bye'*500,
|
337
|
+
:timestamp => time = Time.at(Time.now.to_i),
|
338
|
+
:table => { :this => 'is', :a => 'hash', :with => {:nested => 123, :and => time, :also => 123.456} },
|
339
|
+
:bit => true
|
340
|
+
}.each do |type, value|
|
341
|
+
|
342
|
+
should "read and write a #{type}" do
|
343
|
+
@buf.write(type, value)
|
344
|
+
@buf.rewind
|
345
|
+
@buf.read(type).should == value
|
346
|
+
@buf.should.be.empty
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
|
351
|
+
should 'read and write multiple bits' do
|
352
|
+
bits = [true, false, false, true, true, false, false, true, true, false]
|
353
|
+
@buf.write(:bit, bits)
|
354
|
+
@buf.write(:octet, 100)
|
355
|
+
|
356
|
+
@buf.rewind
|
357
|
+
|
358
|
+
bits.map do
|
359
|
+
@buf.read(:bit)
|
360
|
+
end.should == bits
|
361
|
+
@buf.read(:octet).should == 100
|
362
|
+
end
|
363
|
+
|
364
|
+
should 'read and write properties' do
|
365
|
+
properties = ([
|
366
|
+
[:octet, 1],
|
367
|
+
[:shortstr, 'abc'],
|
368
|
+
[:bit, true],
|
369
|
+
[:bit, false],
|
370
|
+
[:shortstr, nil],
|
371
|
+
[:timestamp, nil],
|
372
|
+
[:table, { :a => 'hash' }],
|
373
|
+
]*5).sort_by{rand}
|
374
|
+
|
375
|
+
@buf.write(:properties, properties)
|
376
|
+
@buf.rewind
|
377
|
+
@buf.read(:properties, *properties.map{|type,_| type }).should == properties.map{|_,value| value }
|
378
|
+
@buf.should.be.empty
|
379
|
+
end
|
380
|
+
|
381
|
+
should 'do transactional reads with #extract' do
|
382
|
+
@buf.write :octet, 8
|
383
|
+
orig = @buf.to_s
|
384
|
+
|
385
|
+
@buf.rewind
|
386
|
+
@buf.extract do |b|
|
387
|
+
b.read :octet
|
388
|
+
b.read :short
|
389
|
+
end
|
390
|
+
|
391
|
+
@buf.pos.should == 0
|
392
|
+
@buf.data.should == orig
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|