cql-rb 1.0.0.pre5 → 1.0.0.pre6
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.md +61 -46
- data/lib/cql.rb +1 -0
- data/lib/cql/byte_buffer.rb +138 -0
- data/lib/cql/future.rb +3 -0
- data/lib/cql/io/node_connection.rb +29 -26
- data/lib/cql/protocol.rb +1 -1
- data/lib/cql/protocol/decoding.rb +41 -33
- data/lib/cql/protocol/encoding.rb +4 -15
- data/lib/cql/protocol/request_frame.rb +3 -3
- data/lib/cql/protocol/response_frame.rb +174 -81
- data/lib/cql/version.rb +1 -1
- data/spec/cql/byte_buffer_spec.rb +299 -0
- data/spec/cql/io/io_reactor_spec.rb +13 -16
- data/spec/cql/protocol/decoding_spec.rb +128 -91
- data/spec/cql/protocol/encoding_spec.rb +8 -57
- data/spec/cql/protocol/request_frame_spec.rb +10 -5
- data/spec/cql/protocol/response_frame_spec.rb +31 -15
- data/spec/integration/client_spec.rb +4 -0
- data/spec/integration/protocol_spec.rb +1 -1
- data/spec/integration/regression_spec.rb +41 -14
- data/spec/spec_helper.rb +13 -6
- data/spec/support/bytes_helper.rb +1 -1
- data/spec/support/fake_server.rb +10 -2
- metadata +5 -2
@@ -16,15 +16,13 @@ module Cql
|
|
16
16
|
def write_string(buffer, str)
|
17
17
|
str = str.to_s
|
18
18
|
buffer << [str.bytesize].pack(Formats::SHORT_FORMAT)
|
19
|
-
buffer <<
|
20
|
-
buffer.force_encoding(::Encoding::BINARY)
|
19
|
+
buffer << str
|
21
20
|
buffer
|
22
21
|
end
|
23
22
|
|
24
23
|
def write_long_string(buffer, str)
|
25
24
|
buffer << [str.bytesize].pack(Formats::INT_FORMAT)
|
26
|
-
buffer <<
|
27
|
-
buffer.force_encoding(::Encoding::BINARY)
|
25
|
+
buffer << str
|
28
26
|
buffer
|
29
27
|
end
|
30
28
|
|
@@ -43,22 +41,20 @@ module Cql
|
|
43
41
|
def write_bytes(buffer, bytes)
|
44
42
|
if bytes
|
45
43
|
write_int(buffer, bytes.bytesize)
|
46
|
-
buffer <<
|
44
|
+
buffer << bytes
|
47
45
|
else
|
48
46
|
write_int(buffer, -1)
|
49
47
|
end
|
50
|
-
buffer.force_encoding(::Encoding::BINARY)
|
51
48
|
buffer
|
52
49
|
end
|
53
50
|
|
54
51
|
def write_short_bytes(buffer, bytes)
|
55
52
|
if bytes
|
56
53
|
write_short(buffer, bytes.bytesize)
|
57
|
-
buffer <<
|
54
|
+
buffer << bytes
|
58
55
|
else
|
59
56
|
write_short(buffer, -1)
|
60
57
|
end
|
61
|
-
buffer.force_encoding(::Encoding::BINARY)
|
62
58
|
buffer
|
63
59
|
end
|
64
60
|
|
@@ -109,13 +105,6 @@ module Cql
|
|
109
105
|
def write_float(buffer, n)
|
110
106
|
buffer << [n].pack(Formats::FLOAT_FORMAT)
|
111
107
|
end
|
112
|
-
|
113
|
-
private
|
114
|
-
|
115
|
-
def binary_cast(str)
|
116
|
-
return str if str.ascii_only?
|
117
|
-
str.dup.force_encoding(::Encoding::BINARY)
|
118
|
-
end
|
119
108
|
end
|
120
109
|
end
|
121
110
|
end
|
@@ -10,9 +10,9 @@ module Cql
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def write(io)
|
13
|
-
buffer =
|
14
|
-
|
15
|
-
|
13
|
+
buffer = @body.write(ByteBuffer.new)
|
14
|
+
io << [1, 0, @stream_id, @body.opcode].pack(Formats::HEADER_FORMAT)
|
15
|
+
io << [buffer.length].pack(Formats::INT_FORMAT)
|
16
16
|
io << buffer
|
17
17
|
end
|
18
18
|
end
|
@@ -8,7 +8,7 @@ require 'set'
|
|
8
8
|
module Cql
|
9
9
|
module Protocol
|
10
10
|
class ResponseFrame
|
11
|
-
def initialize(buffer=
|
11
|
+
def initialize(buffer=ByteBuffer.new)
|
12
12
|
@headers = FrameHeaders.new(buffer)
|
13
13
|
check_complete!
|
14
14
|
end
|
@@ -87,7 +87,11 @@ module Cql
|
|
87
87
|
|
88
88
|
def check_complete!
|
89
89
|
if @buffer.length >= 8
|
90
|
-
@protocol_version
|
90
|
+
@protocol_version = @buffer.read_byte(true)
|
91
|
+
@flags = @buffer.read_byte(true)
|
92
|
+
@stream_id = @buffer.read_byte(true)
|
93
|
+
@opcode = @buffer.read_byte(true)
|
94
|
+
@length = @buffer.read_int
|
91
95
|
raise UnsupportedFrameTypeError, 'Request frames are not supported' if @protocol_version > 0
|
92
96
|
@protocol_version &= 0x7f
|
93
97
|
end
|
@@ -120,7 +124,7 @@ module Cql
|
|
120
124
|
extra_length = @buffer.length - @length
|
121
125
|
@response = @type.decode!(@buffer)
|
122
126
|
if @buffer.length > extra_length
|
123
|
-
@buffer.
|
127
|
+
@buffer.discard(@buffer.length - extra_length)
|
124
128
|
end
|
125
129
|
end
|
126
130
|
end
|
@@ -303,33 +307,38 @@ module Cql
|
|
303
307
|
|
304
308
|
private
|
305
309
|
|
310
|
+
COLUMN_TYPES = [
|
311
|
+
nil,
|
312
|
+
:ascii,
|
313
|
+
:bigint,
|
314
|
+
:blob,
|
315
|
+
:boolean,
|
316
|
+
:counter,
|
317
|
+
:decimal,
|
318
|
+
:double,
|
319
|
+
:float,
|
320
|
+
:int,
|
321
|
+
:text,
|
322
|
+
:timestamp,
|
323
|
+
:uuid,
|
324
|
+
:varchar,
|
325
|
+
:varint,
|
326
|
+
:timeuuid,
|
327
|
+
:inet,
|
328
|
+
].freeze
|
329
|
+
|
306
330
|
def self.read_column_type!(buffer)
|
307
331
|
id, type = read_option!(buffer) do |id, b|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
when 0x03 then :blob
|
312
|
-
when 0x04 then :boolean
|
313
|
-
when 0x05 then :counter
|
314
|
-
when 0x06 then :decimal
|
315
|
-
when 0x07 then :double
|
316
|
-
when 0x08 then :float
|
317
|
-
when 0x09 then :int
|
318
|
-
# when 0x0a then :text
|
319
|
-
when 0x0b then :timestamp
|
320
|
-
when 0x0c then :uuid
|
321
|
-
when 0x0d then :varchar
|
322
|
-
when 0x0e then :varint
|
323
|
-
when 0x0f then :timeuuid
|
324
|
-
when 0x10 then :inet
|
325
|
-
when 0x20
|
332
|
+
if id > 0 && id <= 0x10
|
333
|
+
COLUMN_TYPES[id]
|
334
|
+
elsif id == 0x20
|
326
335
|
sub_type = read_column_type!(buffer)
|
327
336
|
[:list, sub_type]
|
328
|
-
|
337
|
+
elsif id == 0x21
|
329
338
|
key_type = read_column_type!(buffer)
|
330
339
|
value_type = read_column_type!(buffer)
|
331
340
|
[:map, key_type, value_type]
|
332
|
-
|
341
|
+
elsif id == 0x22
|
333
342
|
sub_type = read_column_type!(buffer)
|
334
343
|
[:set, sub_type]
|
335
344
|
else
|
@@ -360,75 +369,159 @@ module Cql
|
|
360
369
|
end
|
361
370
|
end
|
362
371
|
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
when :int
|
383
|
-
read_int!(bytes)
|
384
|
-
when :timestamp
|
385
|
-
timestamp = read_long!(bytes)
|
386
|
-
Time.at(timestamp/1000.0)
|
387
|
-
when :varchar, :text
|
388
|
-
bytes.force_encoding(::Encoding::UTF_8)
|
389
|
-
when :varint
|
390
|
-
read_varint!(bytes)
|
391
|
-
when :timeuuid, :uuid
|
392
|
-
read_uuid!(bytes)
|
393
|
-
when :inet
|
394
|
-
IPAddr.new_ntoh(bytes)
|
395
|
-
when Array
|
396
|
-
case type.first
|
397
|
-
when :list
|
398
|
-
list = []
|
399
|
-
size = read_short!(bytes)
|
400
|
-
size.times do
|
401
|
-
list << convert_type(read_short_bytes!(bytes), type.last)
|
402
|
-
end
|
403
|
-
list
|
404
|
-
when :map
|
405
|
-
map = {}
|
406
|
-
size = read_short!(bytes)
|
407
|
-
size.times do
|
408
|
-
key = convert_type(read_short_bytes!(bytes), type[1])
|
409
|
-
value = convert_type(read_short_bytes!(bytes), type[2])
|
410
|
-
map[key] = value
|
411
|
-
end
|
412
|
-
map
|
413
|
-
when :set
|
414
|
-
set = Set.new
|
415
|
-
size = read_short!(bytes)
|
416
|
-
size.times do
|
417
|
-
set << convert_type(read_short_bytes!(bytes), type.last)
|
372
|
+
class TypeConverter
|
373
|
+
include Decoding
|
374
|
+
|
375
|
+
def initialize
|
376
|
+
@conversions = conversions
|
377
|
+
end
|
378
|
+
|
379
|
+
def convert_type(buffer, type, size_bytes=4)
|
380
|
+
return nil if buffer.empty?
|
381
|
+
case type
|
382
|
+
when Array
|
383
|
+
buffer.discard(size_bytes)
|
384
|
+
case type.first
|
385
|
+
when :list
|
386
|
+
convert_list(buffer, @conversions[type[1]])
|
387
|
+
when :map
|
388
|
+
convert_map(buffer, @conversions[type[1]], @conversions[type[2]])
|
389
|
+
when :set
|
390
|
+
convert_set(buffer, @conversions[type[1]])
|
418
391
|
end
|
419
|
-
|
392
|
+
else
|
393
|
+
@conversions[type].call(buffer, size_bytes)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
def conversions
|
398
|
+
{
|
399
|
+
:ascii => method(:convert_ascii),
|
400
|
+
:bigint => method(:convert_bigint),
|
401
|
+
:blob => method(:convert_blob),
|
402
|
+
:boolean => method(:convert_boolean),
|
403
|
+
:counter => method(:convert_bigint),
|
404
|
+
:decimal => method(:convert_decimal),
|
405
|
+
:double => method(:convert_double),
|
406
|
+
:float => method(:convert_float),
|
407
|
+
:int => method(:convert_int),
|
408
|
+
:timestamp => method(:convert_timestamp),
|
409
|
+
:varchar => method(:convert_varchar),
|
410
|
+
:text => method(:convert_varchar),
|
411
|
+
:varint => method(:convert_varint),
|
412
|
+
:timeuuid => method(:convert_uuid),
|
413
|
+
:uuid => method(:convert_uuid),
|
414
|
+
:inet => method(:convert_inet),
|
415
|
+
}
|
416
|
+
end
|
417
|
+
|
418
|
+
def convert_ascii(buffer, size_bytes)
|
419
|
+
bytes = size_bytes == 4 ? read_bytes!(buffer) : read_short_bytes!(buffer)
|
420
|
+
bytes ? bytes.force_encoding(::Encoding::ASCII) : nil
|
421
|
+
end
|
422
|
+
|
423
|
+
def convert_bigint(buffer, size_bytes)
|
424
|
+
buffer.discard(size_bytes)
|
425
|
+
read_long!(buffer)
|
426
|
+
end
|
427
|
+
|
428
|
+
def convert_blob(buffer, size_bytes)
|
429
|
+
bytes = size_bytes == 4 ? read_bytes!(buffer) : read_short_bytes!(buffer)
|
430
|
+
bytes ? bytes : nil
|
431
|
+
end
|
432
|
+
|
433
|
+
def convert_boolean(buffer, size_bytes)
|
434
|
+
buffer.discard(size_bytes)
|
435
|
+
buffer.read(1) == Constants::TRUE_BYTE
|
436
|
+
end
|
437
|
+
|
438
|
+
def convert_counter(buffer, size_bytes)
|
439
|
+
buffer.discard(size_bytes)
|
440
|
+
read_long!(buffer)
|
441
|
+
end
|
442
|
+
|
443
|
+
def convert_decimal(buffer, size_bytes)
|
444
|
+
read_decimal!(buffer, buffer.read_int)
|
445
|
+
end
|
446
|
+
|
447
|
+
def convert_double(buffer, size_bytes)
|
448
|
+
buffer.discard(size_bytes)
|
449
|
+
read_double!(buffer)
|
450
|
+
end
|
451
|
+
|
452
|
+
def convert_float(buffer, size_bytes)
|
453
|
+
buffer.discard(size_bytes)
|
454
|
+
read_float!(buffer)
|
455
|
+
end
|
456
|
+
|
457
|
+
def convert_int(buffer, size_bytes)
|
458
|
+
buffer.discard(size_bytes)
|
459
|
+
read_int!(buffer)
|
460
|
+
end
|
461
|
+
|
462
|
+
def convert_timestamp(buffer, size_bytes)
|
463
|
+
buffer.discard(size_bytes)
|
464
|
+
timestamp = read_long!(buffer)
|
465
|
+
Time.at(timestamp/1000.0)
|
466
|
+
end
|
467
|
+
|
468
|
+
def convert_varchar(buffer, size_bytes)
|
469
|
+
bytes = size_bytes == 4 ? read_bytes!(buffer) : read_short_bytes!(buffer)
|
470
|
+
bytes ? bytes.force_encoding(::Encoding::UTF_8) : nil
|
471
|
+
end
|
472
|
+
|
473
|
+
def convert_varint(buffer, size_bytes)
|
474
|
+
read_varint!(buffer, buffer.read_int)
|
475
|
+
end
|
476
|
+
|
477
|
+
def convert_uuid(buffer, size_bytes)
|
478
|
+
buffer.discard(size_bytes)
|
479
|
+
read_uuid!(buffer)
|
480
|
+
end
|
481
|
+
|
482
|
+
def convert_inet(buffer, size_bytes)
|
483
|
+
size = size_bytes == 4 ? buffer.read_int : buffer.read_short
|
484
|
+
IPAddr.new_ntoh(buffer.read(size))
|
485
|
+
end
|
486
|
+
|
487
|
+
def convert_list(buffer, value_converter)
|
488
|
+
list = []
|
489
|
+
size = buffer.read_short
|
490
|
+
size.times do
|
491
|
+
list << value_converter.call(buffer, 2)
|
492
|
+
end
|
493
|
+
list
|
494
|
+
end
|
495
|
+
|
496
|
+
def convert_map(buffer, key_converter, value_converter)
|
497
|
+
map = {}
|
498
|
+
size = buffer.read_short
|
499
|
+
size.times do
|
500
|
+
key = key_converter.call(buffer, 2)
|
501
|
+
value = value_converter.call(buffer, 2)
|
502
|
+
map[key] = value
|
503
|
+
end
|
504
|
+
map
|
505
|
+
end
|
506
|
+
|
507
|
+
def convert_set(buffer, value_converter)
|
508
|
+
set = Set.new
|
509
|
+
size = buffer.read_short
|
510
|
+
size.times do
|
511
|
+
set << value_converter.call(buffer, 2)
|
420
512
|
end
|
513
|
+
set
|
421
514
|
end
|
422
515
|
end
|
423
516
|
|
424
517
|
def self.read_rows!(buffer, column_specs)
|
518
|
+
type_converter = TypeConverter.new
|
425
519
|
rows_count = read_int!(buffer)
|
426
520
|
rows = []
|
427
521
|
rows_count.times do |row_index|
|
428
522
|
row = {}
|
429
523
|
column_specs.each do |column_spec|
|
430
|
-
|
431
|
-
row[column_spec[2]] = convert_type(column_value, column_spec[3])
|
524
|
+
row[column_spec[2]] = type_converter.convert_type(buffer, column_spec[3])
|
432
525
|
end
|
433
526
|
rows << row
|
434
527
|
end
|
data/lib/cql/version.rb
CHANGED
@@ -0,0 +1,299 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module Cql
|
7
|
+
describe ByteBuffer do
|
8
|
+
let :buffer do
|
9
|
+
described_class.new
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#initialize' do
|
13
|
+
it 'can be inititialized empty' do
|
14
|
+
described_class.new.should be_empty
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'can be initialized with bytes' do
|
18
|
+
described_class.new('hello').length.should == 5
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#length/#size/#bytesize' do
|
23
|
+
it 'returns the number of bytes in the buffer' do
|
24
|
+
buffer << 'foo'
|
25
|
+
buffer.length.should == 3
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'is zero initially' do
|
29
|
+
buffer.length.should == 0
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'is aliased as #size' do
|
33
|
+
buffer << 'foo'
|
34
|
+
buffer.size.should == 3
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'is aliased as #bytesize' do
|
38
|
+
buffer << 'foo'
|
39
|
+
buffer.bytesize.should == 3
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#empty?' do
|
44
|
+
it 'is true initially' do
|
45
|
+
buffer.should be_empty
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'is false when there are bytes in the buffer' do
|
49
|
+
buffer << 'foo'
|
50
|
+
buffer.should_not be_empty
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#append/#<<' do
|
55
|
+
it 'adds bytes to the buffer' do
|
56
|
+
buffer.append('foo')
|
57
|
+
buffer.should_not be_empty
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'can be used as <<' do
|
61
|
+
buffer << 'foo'
|
62
|
+
buffer.should_not be_empty
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns itself' do
|
66
|
+
buffer.append('foo').should eql(buffer)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'stores its bytes as binary' do
|
70
|
+
buffer.append('hällö').length.should == 7
|
71
|
+
buffer.to_s.encoding.should == ::Encoding::BINARY
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'handles appending with multibyte strings' do
|
75
|
+
buffer.append('hello')
|
76
|
+
buffer.append('würld')
|
77
|
+
buffer.to_s.should == 'hellowürld'.force_encoding(::Encoding::BINARY)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'handles appending with another byte buffer' do
|
81
|
+
buffer.append('hello ').append(ByteBuffer.new('world'))
|
82
|
+
buffer.to_s.should == 'hello world'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#eql?' do
|
87
|
+
it 'is equal to another buffer with the same contents' do
|
88
|
+
b1 = described_class.new
|
89
|
+
b2 = described_class.new
|
90
|
+
b1.append('foo')
|
91
|
+
b2.append('foo')
|
92
|
+
b1.should eql(b2)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'is not equal to another buffer with other contents' do
|
96
|
+
b1 = described_class.new
|
97
|
+
b2 = described_class.new
|
98
|
+
b1.append('foo')
|
99
|
+
b2.append('bar')
|
100
|
+
b1.should_not eql(b2)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'is aliased as #==' do
|
104
|
+
b1 = described_class.new
|
105
|
+
b2 = described_class.new
|
106
|
+
b1.append('foo')
|
107
|
+
b2.append('foo')
|
108
|
+
b1.should == b2
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'is equal to another buffer when both are empty' do
|
112
|
+
b1 = described_class.new
|
113
|
+
b2 = described_class.new
|
114
|
+
b1.should eql(b2)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '#hash' do
|
119
|
+
it 'has the same hash code as another buffer with the same contents' do
|
120
|
+
b1 = described_class.new
|
121
|
+
b2 = described_class.new
|
122
|
+
b1.append('foo')
|
123
|
+
b2.append('foo')
|
124
|
+
b1.hash.should == b2.hash
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'is not equal to the hash code of another buffer with other contents' do
|
128
|
+
b1 = described_class.new
|
129
|
+
b2 = described_class.new
|
130
|
+
b1.append('foo')
|
131
|
+
b2.append('bar')
|
132
|
+
b1.hash.should_not == b2.hash
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'is equal to the hash code of another buffer when both are empty' do
|
136
|
+
b1 = described_class.new
|
137
|
+
b2 = described_class.new
|
138
|
+
b1.hash.should == b2.hash
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe '#to_s' do
|
143
|
+
it 'returns the bytes' do
|
144
|
+
buffer.append('hello world').to_s.should == 'hello world'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe '#to_str' do
|
149
|
+
it 'returns the bytes' do
|
150
|
+
buffer.append('hello world').to_str.should == 'hello world'
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe '#inspect' do
|
155
|
+
it 'returns the bytes wrapped in ByteBuffer(...)' do
|
156
|
+
buffer.append("\xca\xfe")
|
157
|
+
buffer.inspect.should == '#<Cql::ByteBuffer: "\xCA\xFE">'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe '#discard' do
|
162
|
+
it 'discards the specified number of bytes from the front of the buffer' do
|
163
|
+
buffer.append('hello world')
|
164
|
+
buffer.discard(4)
|
165
|
+
buffer.should == ByteBuffer.new('o world')
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'returns the byte buffer' do
|
169
|
+
buffer.append('hello world')
|
170
|
+
buffer.discard(4).should == ByteBuffer.new('o world')
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'raises an error if the number of bytes in the buffer is fewer than the number to discard' do
|
174
|
+
expect { buffer.discard(1) }.to raise_error(RangeError)
|
175
|
+
buffer.append('hello')
|
176
|
+
expect { buffer.discard(7) }.to raise_error(RangeError)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe '#read' do
|
181
|
+
it 'returns the specified number of bytes, as a string' do
|
182
|
+
buffer.append('hello')
|
183
|
+
buffer.read(4).should == 'hell'
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'removes the bytes from the buffer' do
|
187
|
+
buffer.append('hello')
|
188
|
+
buffer.read(3)
|
189
|
+
buffer.should == ByteBuffer.new('lo')
|
190
|
+
buffer.read(2).should == 'lo'
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'raises an error if there are not enough bytes' do
|
194
|
+
buffer.append('hello')
|
195
|
+
expect { buffer.read(23423543) }.to raise_error(RangeError)
|
196
|
+
expect { buffer.discard(5).read(1) }.to raise_error(RangeError)
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'returns a string with binary encoding' do
|
200
|
+
buffer.append('hello')
|
201
|
+
buffer.read(4).encoding.should == ::Encoding::BINARY
|
202
|
+
buffer.append('∆')
|
203
|
+
buffer.read(2).encoding.should == ::Encoding::BINARY
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe '#read_int' do
|
208
|
+
it 'returns the first four bytes interpreted as an int' do
|
209
|
+
buffer.append("\xca\xfe\xba\xbe\x01")
|
210
|
+
buffer.read_int.should == 0xcafebabe
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'removes the bytes from the buffer' do
|
214
|
+
buffer.append("\xca\xfe\xba\xbe\x01")
|
215
|
+
buffer.read_int
|
216
|
+
buffer.should == ByteBuffer.new("\x01")
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'raises an error if there are not enough bytes' do
|
220
|
+
buffer.append("\xca\xfe\xba")
|
221
|
+
expect { buffer.read_int }.to raise_error(RangeError)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe '#read_short' do
|
226
|
+
it 'returns the first two bytes interpreted as a short' do
|
227
|
+
buffer.append("\xca\xfe\x01")
|
228
|
+
buffer.read_short.should == 0xcafe
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'removes the bytes from the buffer' do
|
232
|
+
buffer.append("\xca\xfe\x01")
|
233
|
+
buffer.read_short
|
234
|
+
buffer.should == ByteBuffer.new("\x01")
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'raises an error if there are not enough bytes' do
|
238
|
+
buffer.append("\xca")
|
239
|
+
expect { buffer.read_short }.to raise_error(RangeError)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe '#read_byte' do
|
244
|
+
it 'returns the first bytes interpreted as an int' do
|
245
|
+
buffer.append("\x10\x01")
|
246
|
+
buffer.read_byte.should == 0x10
|
247
|
+
buffer.read_byte.should == 0x01
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'removes the byte from the buffer' do
|
251
|
+
buffer.append("\x10\x01")
|
252
|
+
buffer.read_byte
|
253
|
+
buffer.should == ByteBuffer.new("\x01")
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'raises an error if there are no bytes' do
|
257
|
+
expect { buffer.read_byte }.to raise_error(RangeError)
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'can interpret the byte as signed' do
|
261
|
+
buffer.append("\x81\x02")
|
262
|
+
buffer.read_byte(true).should == -127
|
263
|
+
buffer.read_byte(true).should == 2
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
describe '#dup' do
|
268
|
+
it 'returns a copy' do
|
269
|
+
buffer.append('hello world')
|
270
|
+
copy = buffer.dup
|
271
|
+
copy.should eql(buffer)
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'returns a copy which can be modified without modifying the original' do
|
275
|
+
buffer.append('hello world')
|
276
|
+
copy = buffer.dup
|
277
|
+
copy.append('goodbye')
|
278
|
+
copy.should_not eql(buffer)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
context 'when reading and appending' do
|
283
|
+
it 'handles heavy churn' do
|
284
|
+
1000.times do
|
285
|
+
buffer.append('x' * 6)
|
286
|
+
buffer.read_byte
|
287
|
+
buffer.append('y')
|
288
|
+
buffer.read_int
|
289
|
+
buffer.read_short
|
290
|
+
buffer.append('z' * 4)
|
291
|
+
buffer.read_byte
|
292
|
+
buffer.append('z')
|
293
|
+
buffer.read_int
|
294
|
+
buffer.should be_empty
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|