arpie 0.0.5 → 0.0.6
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/BINARY_SPEC +31 -7
- data/Rakefile +7 -3
- data/lib/arpie.rb +1 -0
- data/lib/arpie/binary.rb +21 -336
- data/lib/arpie/binary/bytes_type.rb +82 -0
- data/lib/arpie/binary/fixed_type.rb +42 -0
- data/lib/arpie/binary/list_type.rb +117 -0
- data/lib/arpie/binary/pack_type.rb +109 -0
- data/lib/arpie/binary_types.rb +39 -0
- data/lib/arpie/proxy.rb +33 -9
- data/spec/binary_spec.rb +92 -0
- data/spec/bytes_binary_type_spec.rb +49 -0
- data/spec/client_server_spec.rb +1 -1
- data/spec/fixed_binary_type_spec.rb +29 -0
- data/spec/list_binary_type_spec.rb +34 -0
- data/spec/spec_helper.rb +33 -0
- data/tools/benchmark.rb +1 -1
- metadata +25 -15
data/BINARY_SPEC
CHANGED
@@ -79,7 +79,29 @@ Most field types take :sizeof OR :length as an argument.
|
|
79
79
|
==== :length
|
80
80
|
Tell Binary to expect exactly :length items of the given type. Think of it as a fixed-size array.
|
81
81
|
|
82
|
-
|
82
|
+
You can actually specify a field or virtual that was defined before the currently-defining field.
|
83
|
+
Example:
|
84
|
+
|
85
|
+
class Inner < Arpie::Binary
|
86
|
+
field :sz, :uint8
|
87
|
+
field :ls, :list, :of => :uint8, :length => :sz
|
88
|
+
end
|
89
|
+
|
90
|
+
class Outer < Arpie::Binary
|
91
|
+
field :totalsz, :uint8
|
92
|
+
|
93
|
+
field :bytes, :bytes, :length => :totalsz do
|
94
|
+
field :content, :list, :of => Inner,
|
95
|
+
:length => :all
|
96
|
+
end
|
97
|
+
|
98
|
+
field :end, :uint8
|
99
|
+
end
|
100
|
+
|
101
|
+
Note that fields defined this way will NOT update their "length referral" - you will have to
|
102
|
+
do that manually.
|
103
|
+
|
104
|
+
==== :sizeof
|
83
105
|
This includes a prefixed "non-visible" field, which will be used to determine the
|
84
106
|
actual expected length of the data. Example:
|
85
107
|
|
@@ -90,7 +112,7 @@ Will expect a network-order short (16 bits), followed by the amout of bytes the
|
|
90
112
|
If the field type given in :sizeof requires additional parameters, you can pass them with
|
91
113
|
:sizeof_opts (just like with :list - :of).
|
92
114
|
|
93
|
-
|
115
|
+
=== :list
|
94
116
|
|
95
117
|
A :list is an array of arbitary, same-type elements. The element type is given in the :list-specific
|
96
118
|
:of parameter:
|
@@ -105,7 +127,7 @@ If your :of requires additional argument (a list of lists, for example), you can
|
|
105
127
|
field :my_list_2, :list, :sizeof => :uint8, :of => :string,
|
106
128
|
:of_opts => { :sizeof, :nint16 }
|
107
129
|
|
108
|
-
|
130
|
+
=== :bitfield
|
109
131
|
|
110
132
|
The bitfield type unpacks one or more bytes into their bit values, for individual addressing:
|
111
133
|
|
@@ -128,13 +150,15 @@ The bitfield type unpacks one or more bytes into their bit values, for individua
|
|
128
150
|
|
129
151
|
This is pretty much all that you can do with it, for now.
|
130
152
|
|
131
|
-
== :fixed
|
153
|
+
== static values / :fixed
|
132
154
|
|
133
155
|
The fixed type allows defining fixed strings that are always the same, both acting as a filler
|
134
156
|
and a safeguard (it will complain if it does not match):
|
135
157
|
|
136
158
|
field :blah, :fixed, :value => "FIXED"
|
137
159
|
|
160
|
+
The alias Binary.static does this for you.
|
161
|
+
|
138
162
|
== Nested Classes
|
139
163
|
|
140
164
|
Instead of pre-registered primitive data fiels you can pass in class names:
|
@@ -186,7 +210,7 @@ As you get to parse complex data structures, you might encounter the following c
|
|
186
210
|
|
187
211
|
field :middle, :something
|
188
212
|
|
189
|
-
field :matrix, :list, :of => :uint8, :
|
213
|
+
field :matrix, :list, :of => :uint8, :length => (value of :len_a * :len_b)
|
190
214
|
end
|
191
215
|
|
192
216
|
In this case, you will need to use a virtual attribute:
|
@@ -197,8 +221,8 @@ In this case, you will need to use a virtual attribute:
|
|
197
221
|
|
198
222
|
field :middle, :something
|
199
223
|
|
200
|
-
virtual :v_len do |o| o.len_a * o.len_b end
|
201
|
-
field :hah, :list, :of => Nested, :
|
224
|
+
virtual :v_len, :uint16 do |o| o.len_a * o.len_b end
|
225
|
+
field :hah, :list, :of => Nested, :length => :v_len
|
202
226
|
|
203
227
|
pre_to do |o|
|
204
228
|
o.len_a = 4
|
data/Rakefile
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
require "rake"
|
2
2
|
require "rake/clean"
|
3
3
|
require "rake/gempackagetask"
|
4
|
-
|
4
|
+
begin
|
5
|
+
require "hanna/rdoctask"
|
6
|
+
rescue LoadError
|
7
|
+
require "rake/rdoctask"
|
8
|
+
end
|
5
9
|
require "fileutils"
|
6
10
|
include FileUtils
|
7
11
|
|
@@ -9,7 +13,7 @@ include FileUtils
|
|
9
13
|
# Configuration
|
10
14
|
##############################################################################
|
11
15
|
NAME = "arpie"
|
12
|
-
VERS = "0.0.
|
16
|
+
VERS = "0.0.6"
|
13
17
|
CLEAN.include ["**/.*.sw?", "pkg", ".config", "rdoc", "coverage"]
|
14
18
|
RDOC_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', \
|
15
19
|
"#{NAME}: A high-performing layered networking protocol framework. Simple to use, simple to extend.", \
|
@@ -44,7 +48,7 @@ spec = Gem::Specification.new do |s|
|
|
44
48
|
s.files = %w(COPYING README Rakefile) + Dir.glob("{bin,doc,spec,lib,tools,scripts,data}/**/*")
|
45
49
|
s.require_path = "lib"
|
46
50
|
s.bindir = "bin"
|
47
|
-
s.add_dependency('uuidtools', '>=
|
51
|
+
s.add_dependency('uuidtools', '>= 2.0.0')
|
48
52
|
end
|
49
53
|
|
50
54
|
Rake::GemPackageTask.new(spec) do |p|
|
data/lib/arpie.rb
CHANGED
data/lib/arpie/binary.rb
CHANGED
@@ -34,7 +34,7 @@ module Arpie
|
|
34
34
|
# Do not use +Kernel+ methods as field names. It'll confuse method_missing.
|
35
35
|
# Example:
|
36
36
|
# field :test, :uint8
|
37
|
-
#
|
37
|
+
# => in `test': wrong number of arguments (ArgumentError)
|
38
38
|
#
|
39
39
|
# In fact, this is the reason while Binary will not let you define fields with
|
40
40
|
# with names like existing instance methods.
|
@@ -62,6 +62,7 @@ module Arpie
|
|
62
62
|
|
63
63
|
def initialize
|
64
64
|
@fields = {}
|
65
|
+
@@fields[self.class] ||= []
|
65
66
|
|
66
67
|
# set up our own class handlers, create anon classes, set up default values
|
67
68
|
@@fields[self.class].each {|field|
|
@@ -87,7 +88,13 @@ module Arpie
|
|
87
88
|
"Anon#{self.class.__anonymous.inspect}" :
|
88
89
|
self.class.to_s
|
89
90
|
|
90
|
-
|
91
|
+
fields = []
|
92
|
+
@@fields[self.class].each {|field|
|
93
|
+
fields << "%s=>%s" % [field.name.inspect, @fields[field.name].inspect]
|
94
|
+
}
|
95
|
+
fields = '{' + fields.join(", ") + '}'
|
96
|
+
|
97
|
+
"#<#{klass}#{desc} #{fields}>"
|
91
98
|
end
|
92
99
|
|
93
100
|
def method_missing m, *a
|
@@ -269,6 +276,11 @@ module Arpie
|
|
269
276
|
@@fields[self] << Field.new(name.to_sym, type, opts, inline_handler)
|
270
277
|
end
|
271
278
|
|
279
|
+
# Alias for +field+.
|
280
|
+
def self.f *va, &b
|
281
|
+
field *va, &b
|
282
|
+
end
|
283
|
+
|
272
284
|
# Set up a new virtual field
|
273
285
|
def self.virtual name, type, opts = {}, &handler
|
274
286
|
raise ArgumentError, "You need to pass a block with virtuals" unless block_given?
|
@@ -282,12 +294,18 @@ module Arpie
|
|
282
294
|
@@virtuals[self] << Virtual.new(name.to_sym, type, opts, handler)
|
283
295
|
end
|
284
296
|
|
297
|
+
# Alias for +virtual+.
|
298
|
+
def self.v *va, &b
|
299
|
+
virtual *va, &b
|
300
|
+
end
|
285
301
|
|
286
302
|
def self.binary_size opts = {}
|
287
303
|
@@fields[self] ||= []
|
288
304
|
total = @@fields[self].inject(0) {|sum, field|
|
289
305
|
klass = get_type_handler(field.type)
|
290
|
-
|
306
|
+
sz = klass.binary_size(field.opts)
|
307
|
+
sz or raise "cannot binary_size dynamic Binary definitions"
|
308
|
+
sum + sz
|
291
309
|
}
|
292
310
|
|
293
311
|
total
|
@@ -418,337 +436,4 @@ module Arpie
|
|
418
436
|
self.add_hook(:post_from, handler)
|
419
437
|
end
|
420
438
|
end
|
421
|
-
|
422
|
-
class BinaryType
|
423
|
-
include Arpie
|
424
|
-
|
425
|
-
def binary_size opts
|
426
|
-
nil
|
427
|
-
end
|
428
|
-
|
429
|
-
def required_opts
|
430
|
-
{}
|
431
|
-
end
|
432
|
-
|
433
|
-
# Return [object, len]
|
434
|
-
def from binary, opts
|
435
|
-
raise NotImplementedError
|
436
|
-
end
|
437
|
-
|
438
|
-
# Return [binary]
|
439
|
-
def to object, opts
|
440
|
-
raise NotImplementedError
|
441
|
-
end
|
442
|
-
|
443
|
-
def check_limit value, limit
|
444
|
-
case limit
|
445
|
-
when nil
|
446
|
-
true
|
447
|
-
when Range, Array
|
448
|
-
limit.include?(value)
|
449
|
-
else
|
450
|
-
raise ArgumentError, "unknown limit definition: #{limit.inspect}"
|
451
|
-
end or bogon! nil, "not in :limit => #{limit.inspect}"
|
452
|
-
end
|
453
|
-
|
454
|
-
end
|
455
|
-
|
456
|
-
class PackBinaryType < BinaryType
|
457
|
-
attr_reader :pack_string
|
458
|
-
|
459
|
-
def binary_size opts
|
460
|
-
opts = @force_opts.merge(opts || {})
|
461
|
-
PackBinaryType.length_of(@pack_string + (opts[:length] || 1).to_s)
|
462
|
-
end
|
463
|
-
|
464
|
-
def self.length_of format
|
465
|
-
length = 0
|
466
|
-
format.scan(/(\S_?)\s*(\d*)/).each do |directive, count|
|
467
|
-
count = count.to_i
|
468
|
-
count = 1 if count == 0
|
469
|
-
|
470
|
-
length += case directive
|
471
|
-
when 'A', 'a', 'C', 'c', 'Z', 'x' : count
|
472
|
-
when 'B', 'b' : (count / 8.0).ceil
|
473
|
-
when 'D', 'd', 'E', 'G' : count * 8
|
474
|
-
when 'e', 'F', 'f', 'g' : count * 4
|
475
|
-
when 'H', 'h' : (count / 2.0).ceil
|
476
|
-
when 'I', 'i', 'L', 'l', 'N', 'V' : count * 4
|
477
|
-
when 'n', 'S', 's', 'v' : count * 2
|
478
|
-
when 'Q', 'q' : count * 8
|
479
|
-
when 'X' : count * -1
|
480
|
-
else raise ArgumentError, "#{directive} is not supported"
|
481
|
-
end
|
482
|
-
end
|
483
|
-
|
484
|
-
length
|
485
|
-
end
|
486
|
-
|
487
|
-
def initialize pack_string, force_opts = {}
|
488
|
-
@pack_string = pack_string
|
489
|
-
@force_opts = force_opts
|
490
|
-
end
|
491
|
-
|
492
|
-
def from binary, opts
|
493
|
-
opts = @force_opts.merge(opts || {})
|
494
|
-
binary && binary.size >= binary_size(opts) or incomplete!
|
495
|
-
len = opts[:length] || 1
|
496
|
-
pack_string = @pack_string + len.to_s
|
497
|
-
value = binary.unpack(pack_string)[0]
|
498
|
-
value += opts[:mod] if opts[:mod]
|
499
|
-
check_limit value, opts[:limit]
|
500
|
-
|
501
|
-
[value, binary_size(opts)]
|
502
|
-
end
|
503
|
-
|
504
|
-
def to object, opts
|
505
|
-
opts = @force_opts.merge(opts || {})
|
506
|
-
object.nil? and bogon! nil,"nil object given"
|
507
|
-
object -= opts[:mod] if opts[:mod]
|
508
|
-
len = opts[:length] || 1
|
509
|
-
pack_string = @pack_string + len.to_s
|
510
|
-
[object].pack(pack_string)
|
511
|
-
end
|
512
|
-
|
513
|
-
end
|
514
|
-
|
515
|
-
Binary.register_type(PackBinaryType.new('c'), :uint8)
|
516
|
-
Binary.register_type(PackBinaryType.new("c"), :int8)
|
517
|
-
Binary.register_type(PackBinaryType.new("C"), :uint8)
|
518
|
-
Binary.register_type(PackBinaryType.new("s"), :int16)
|
519
|
-
Binary.register_type(PackBinaryType.new("S"), :uint16)
|
520
|
-
Binary.register_type(PackBinaryType.new("i"), :int32)
|
521
|
-
Binary.register_type(PackBinaryType.new("I"), :uint32)
|
522
|
-
Binary.register_type(PackBinaryType.new("q"), :int64)
|
523
|
-
Binary.register_type(PackBinaryType.new("Q"), :uint64)
|
524
|
-
|
525
|
-
Binary.register_type(PackBinaryType.new("l"), :long32)
|
526
|
-
Binary.register_type(PackBinaryType.new("L"), :ulong32)
|
527
|
-
|
528
|
-
Binary.register_type(PackBinaryType.new("n"), :nint16)
|
529
|
-
Binary.register_type(PackBinaryType.new("N"), :nint32)
|
530
|
-
Binary.register_type(PackBinaryType.new("v"), :lint16)
|
531
|
-
Binary.register_type(PackBinaryType.new("V"), :lint32)
|
532
|
-
|
533
|
-
Binary.register_type(PackBinaryType.new("d"), :double)
|
534
|
-
Binary.register_type(PackBinaryType.new("E"), :ldouble)
|
535
|
-
Binary.register_type(PackBinaryType.new("G"), :ndouble)
|
536
|
-
|
537
|
-
Binary.register_type(PackBinaryType.new("f"), :float)
|
538
|
-
Binary.register_type(PackBinaryType.new("e"), :lfloat)
|
539
|
-
Binary.register_type(PackBinaryType.new("g"), :nfloat)
|
540
|
-
|
541
|
-
Binary.register_type(PackBinaryType.new("B"), :msb_bitfield)
|
542
|
-
Binary.register_type(PackBinaryType.new("b"), :lsb_bitfield)
|
543
|
-
|
544
|
-
class BitBinaryType < Arpie::BinaryType
|
545
|
-
def from binary, opts
|
546
|
-
len = opts[:length] || 1
|
547
|
-
binary.size >= len or incomplete!
|
548
|
-
b = binary.split("")[0,len].map {|x|
|
549
|
-
x == "1"
|
550
|
-
}
|
551
|
-
b = b[0] if b.size == 1
|
552
|
-
[b, len]
|
553
|
-
end
|
554
|
-
|
555
|
-
def to object, opts
|
556
|
-
object = [object] if object.is_a?(TrueClass) || object.is_a?(FalseClass)
|
557
|
-
object.map {|x| x == true ? "1" : "0" }.join("")
|
558
|
-
end
|
559
|
-
end
|
560
|
-
Arpie::Binary.register_type(BitBinaryType.new, :bit)
|
561
|
-
|
562
|
-
class BytesBinaryType < BinaryType
|
563
|
-
def all_opts; [:sizeof, :length] end
|
564
|
-
|
565
|
-
def initialize pack_string, force_opts = {}
|
566
|
-
@pack_string = pack_string
|
567
|
-
@force_opts = force_opts
|
568
|
-
end
|
569
|
-
|
570
|
-
def binary_size opts
|
571
|
-
opts = @force_opts.merge(opts || {})
|
572
|
-
if opts[:sizeof]
|
573
|
-
len_handler = Binary.get_type_handler(opts[:sizeof])
|
574
|
-
len_handler.binary_size(opts[:sizeof_opts])
|
575
|
-
elsif opts[:length]
|
576
|
-
opts[:length]
|
577
|
-
else
|
578
|
-
nil
|
579
|
-
end
|
580
|
-
end
|
581
|
-
|
582
|
-
def from binary, opts
|
583
|
-
opts = (opts || {}).merge(@force_opts)
|
584
|
-
if opts[:sizeof]
|
585
|
-
len_handler = Binary.get_type_handler(opts[:sizeof])
|
586
|
-
len, len_size = len_handler.from(binary, opts[:sizeof_opts])
|
587
|
-
binary.size >= len_size + len or incomplete!
|
588
|
-
|
589
|
-
[binary.unpack("x#{len_size} #{@pack_string}#{len}")[0], len_size + len]
|
590
|
-
|
591
|
-
elsif opts[:length]
|
592
|
-
len = case opts[:length]
|
593
|
-
when :all
|
594
|
-
binary.size
|
595
|
-
else
|
596
|
-
opts[:length]
|
597
|
-
end
|
598
|
-
binary.size >= len or incomplete!
|
599
|
-
[binary.unpack("#{@pack_string}#{len}")[0], len]
|
600
|
-
|
601
|
-
else
|
602
|
-
raise ArgumentError, "need one of [:sizeof, :length]"
|
603
|
-
end
|
604
|
-
|
605
|
-
end
|
606
|
-
|
607
|
-
def to object, opts
|
608
|
-
opts = (opts || {}).merge(@force_opts)
|
609
|
-
if opts[:sizeof]
|
610
|
-
len_handler = Binary.get_type_handler(opts[:sizeof])
|
611
|
-
len_handler.respond_to?(:pack_string) or raise ArgumentError,
|
612
|
-
"#{self.class}#to: needs a PackStringType parameter for length"
|
613
|
-
|
614
|
-
[object.size, object].pack("#{len_handler.pack_string} #{@pack_string}*")
|
615
|
-
|
616
|
-
elsif opts[:length]
|
617
|
-
len = case opts[:length]
|
618
|
-
when :all
|
619
|
-
"*"
|
620
|
-
when Symbol
|
621
|
-
opts[:object].send(opts[:length])
|
622
|
-
else
|
623
|
-
opts[:length]
|
624
|
-
end
|
625
|
-
[object].pack("#{@pack_string}#{len}")
|
626
|
-
|
627
|
-
else
|
628
|
-
raise ArgumentError, "need one of [:sizeof, :length]"
|
629
|
-
end
|
630
|
-
|
631
|
-
end
|
632
|
-
end
|
633
|
-
|
634
|
-
Binary.register_type(BytesBinaryType.new("a", :length => 1), :char)
|
635
|
-
Binary.register_type(BytesBinaryType.new("a"), :bytes)
|
636
|
-
Binary.register_type(BytesBinaryType.new("A"), :string)
|
637
|
-
Binary.register_type(BytesBinaryType.new("Z"), :nstring)
|
638
|
-
|
639
|
-
Binary.register_type(BytesBinaryType.new("M"), :quoted_printable)
|
640
|
-
Binary.register_type(BytesBinaryType.new("m"), :base64)
|
641
|
-
Binary.register_type(BytesBinaryType.new("u"), :uuencoded)
|
642
|
-
|
643
|
-
|
644
|
-
class ListBinaryType < BinaryType
|
645
|
-
|
646
|
-
def binary_size opts
|
647
|
-
if opts[:sizeof]
|
648
|
-
len_handler = Binary.get_type_handler(opts[:sizeof])
|
649
|
-
len_handler.binary_size(opts[:sizeof_opts])
|
650
|
-
elsif opts[:length]
|
651
|
-
case opts[:length]
|
652
|
-
when Symbol
|
653
|
-
opts[:object] ? opts[:object].send(opts[:length]) : nil
|
654
|
-
else
|
655
|
-
opts[:length]
|
656
|
-
end
|
657
|
-
else
|
658
|
-
nil
|
659
|
-
end
|
660
|
-
end
|
661
|
-
|
662
|
-
def from binary, opts
|
663
|
-
type_of = Binary.get_type_handler(opts[:of])
|
664
|
-
type_of.respond_to?(:binary_size) &&
|
665
|
-
type_of_binary_size = type_of.binary_size(opts[:of_opts]) or raise ArgumentError,
|
666
|
-
"can only encode known-width fields"
|
667
|
-
|
668
|
-
list = []
|
669
|
-
consumed = 0
|
670
|
-
length = nil
|
671
|
-
|
672
|
-
if opts[:sizeof]
|
673
|
-
len_handler = Binary.get_type_handler(opts[:sizeof])
|
674
|
-
length, ate = len_handler.from(binary, opts[:sizeof_opts])
|
675
|
-
consumed += ate
|
676
|
-
|
677
|
-
elsif opts[:length]
|
678
|
-
length = case opts[:length]
|
679
|
-
when :all
|
680
|
-
binary.size / type_of_binary_size
|
681
|
-
when Symbol
|
682
|
-
opts[:object].send(opts[:length])
|
683
|
-
else
|
684
|
-
opts[:length]
|
685
|
-
end
|
686
|
-
else
|
687
|
-
raise ArgumentError, "need one of [:sizeof, :length]"
|
688
|
-
end
|
689
|
-
|
690
|
-
cc, ate = nil, nil
|
691
|
-
for i in 0...length do
|
692
|
-
cc, ate = type_of.from(binary[consumed .. -1], opts[:of_opts])
|
693
|
-
list << cc
|
694
|
-
consumed += ate
|
695
|
-
end
|
696
|
-
|
697
|
-
[list, consumed]
|
698
|
-
end
|
699
|
-
|
700
|
-
def to object, opts
|
701
|
-
object.is_a?(Array) or bogon! object, "require Array"
|
702
|
-
|
703
|
-
type_of = Binary.get_type_handler(opts[:of])
|
704
|
-
|
705
|
-
if opts[:sizeof]
|
706
|
-
len_handler = Binary.get_type_handler(opts[:sizeof])
|
707
|
-
([len_handler.to(object.size, opts[:sizeof_opts])] + object.map {|o|
|
708
|
-
type_of.to(o, opts[:of_opts])
|
709
|
-
}).join('')
|
710
|
-
|
711
|
-
elsif opts[:length]
|
712
|
-
length = case opts[:length]
|
713
|
-
when Symbol
|
714
|
-
opts[:object].send(opts[:length])
|
715
|
-
else
|
716
|
-
opts[:length]
|
717
|
-
end
|
718
|
-
|
719
|
-
object.size == length or bogon! object,
|
720
|
-
"Array#size does not match required fixed width: " +
|
721
|
-
"have #{object.size}, require #{length.inspect}"
|
722
|
-
|
723
|
-
object.map {|o|
|
724
|
-
type_of.to(o, opts[:of_opts])
|
725
|
-
}.join('')
|
726
|
-
|
727
|
-
else
|
728
|
-
raise ArgumentError, "need one of [:sizeof, :length]"
|
729
|
-
end
|
730
|
-
|
731
|
-
end
|
732
|
-
end
|
733
|
-
Binary.register_type(ListBinaryType.new, :list)
|
734
|
-
|
735
|
-
class FixedBinaryType < BinaryType
|
736
|
-
def required_opts ; {:value => proc {|v| v.is_a?(String)}} end
|
737
|
-
def binary_size opts
|
738
|
-
opts[:value].size
|
739
|
-
end
|
740
|
-
|
741
|
-
def from binary, opts
|
742
|
-
sz = opts[:value].size
|
743
|
-
existing = binary.unpack("a#{sz}")[0]
|
744
|
-
existing == opts[:value] or bogon! nil, ":fixed did not match data in packet"
|
745
|
-
|
746
|
-
[opts[:value], opts[:value].size]
|
747
|
-
end
|
748
|
-
|
749
|
-
def to object, opts
|
750
|
-
opts[:value]
|
751
|
-
end
|
752
|
-
end
|
753
|
-
Binary.register_type(FixedBinaryType.new, :fixed)
|
754
439
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Arpie
|
2
|
+
class BytesBinaryType < BinaryType
|
3
|
+
def initialize pack_string, force_opts = {}
|
4
|
+
@pack_string = pack_string
|
5
|
+
@force_opts = force_opts
|
6
|
+
end
|
7
|
+
|
8
|
+
def binary_size opts
|
9
|
+
opts = @force_opts.merge(opts || {})
|
10
|
+
if opts[:sizeof]
|
11
|
+
len_handler = Binary.get_type_handler(opts[:sizeof])
|
12
|
+
len_handler.binary_size(opts[:sizeof_opts])
|
13
|
+
elsif opts[:length]
|
14
|
+
opts[:length]
|
15
|
+
else
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def from binary, opts
|
21
|
+
opts = (opts || {}).merge(@force_opts)
|
22
|
+
if opts[:sizeof]
|
23
|
+
len_handler = Binary.get_type_handler(opts[:sizeof])
|
24
|
+
len, len_size = len_handler.from(binary, opts[:sizeof_opts])
|
25
|
+
binary.size >= len_size + len or incomplete!
|
26
|
+
|
27
|
+
[binary.unpack("x#{len_size} #{@pack_string}#{len}")[0], len_size + len]
|
28
|
+
|
29
|
+
elsif opts[:length]
|
30
|
+
len = case opts[:length]
|
31
|
+
when :all
|
32
|
+
binary.size
|
33
|
+
when Symbol
|
34
|
+
opts[:object].send(opts[:length])
|
35
|
+
else
|
36
|
+
opts[:length]
|
37
|
+
end
|
38
|
+
binary.size >= len or incomplete!
|
39
|
+
[binary.unpack("#{@pack_string}#{len}")[0], len]
|
40
|
+
|
41
|
+
else
|
42
|
+
raise ArgumentError, "need one of [:sizeof, :length]"
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
def to object, opts
|
48
|
+
opts = (opts || {}).merge(@force_opts)
|
49
|
+
if opts[:sizeof]
|
50
|
+
len_handler = Binary.get_type_handler(opts[:sizeof])
|
51
|
+
len_handler.respond_to?(:pack_string) or raise ArgumentError,
|
52
|
+
"#{self.class}#to: needs a PackStringType parameter for length"
|
53
|
+
|
54
|
+
[object.size, object].pack("#{len_handler.pack_string} #{@pack_string}*")
|
55
|
+
|
56
|
+
elsif opts[:length]
|
57
|
+
len = case opts[:length]
|
58
|
+
when :all
|
59
|
+
"*"
|
60
|
+
when Symbol
|
61
|
+
"*"
|
62
|
+
else
|
63
|
+
opts[:length]
|
64
|
+
end
|
65
|
+
[object].pack("#{@pack_string}#{len}")
|
66
|
+
|
67
|
+
else
|
68
|
+
raise ArgumentError, "need one of [:sizeof, :length]"
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
Binary.register_type(BytesBinaryType.new("a", :length => 1), :char)
|
75
|
+
Binary.register_type(BytesBinaryType.new("a"), :bytes)
|
76
|
+
Binary.register_type(BytesBinaryType.new("A"), :string)
|
77
|
+
Binary.register_type(BytesBinaryType.new("Z"), :nstring)
|
78
|
+
|
79
|
+
Binary.register_type(BytesBinaryType.new("M"), :quoted_printable)
|
80
|
+
Binary.register_type(BytesBinaryType.new("m"), :base64)
|
81
|
+
Binary.register_type(BytesBinaryType.new("u"), :uuencoded)
|
82
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Arpie
|
2
|
+
class FixedBinaryType < BinaryType
|
3
|
+
def binary_size opts
|
4
|
+
opts[:value].size
|
5
|
+
end
|
6
|
+
|
7
|
+
def from binary, opts
|
8
|
+
opts[:value] or raise ArgumentError, "Requires option :value"
|
9
|
+
sz = opts[:value].size
|
10
|
+
existing = binary.unpack("a#{sz}")[0]
|
11
|
+
existing == opts[:value] or bogon! nil, ":fixed did not match data in packet"
|
12
|
+
|
13
|
+
[opts[:value], opts[:value].size]
|
14
|
+
end
|
15
|
+
|
16
|
+
def to object, opts
|
17
|
+
opts[:value] or raise ArgumentError, "Requires option :value"
|
18
|
+
object == opts[:value] or bogon! nil, ":fixed did not match data in structure"
|
19
|
+
opts[:value]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Binary
|
24
|
+
# Create a static field.
|
25
|
+
# This is an alias for:
|
26
|
+
# field :name, :fixed, :value => content
|
27
|
+
#
|
28
|
+
# If no name is specified, it is assumed that the user will
|
29
|
+
# never want to access it and a suitable one is autogenerated.
|
30
|
+
def self.static content, name = nil
|
31
|
+
name ||= "static-%x" % content.hash
|
32
|
+
field name, :fixed, :value => content
|
33
|
+
end
|
34
|
+
|
35
|
+
# An alias for +static+.
|
36
|
+
def self.s *va, &b
|
37
|
+
static *va, &b
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Binary.register_type(FixedBinaryType.new, :fixed)
|
42
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Arpie
|
2
|
+
class ListBinaryType < BinaryType
|
3
|
+
|
4
|
+
def binary_size opts
|
5
|
+
if opts[:sizeof]
|
6
|
+
len_handler = Binary.get_type_handler(opts[:sizeof])
|
7
|
+
len_handler.binary_size(opts[:sizeof_opts])
|
8
|
+
elsif opts[:length]
|
9
|
+
case opts[:length]
|
10
|
+
when Symbol
|
11
|
+
opts[:object] ? opts[:object].send(opts[:length]) : nil
|
12
|
+
else
|
13
|
+
opts[:length]
|
14
|
+
end
|
15
|
+
else
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def from binary, opts
|
21
|
+
type_of = Binary.get_type_handler(opts[:of])
|
22
|
+
type_of_binary_size = nil
|
23
|
+
if type_of.respond_to?(:binary_size)
|
24
|
+
type_of_binary_size = type_of.binary_size(opts[:of_opts]) rescue nil
|
25
|
+
end
|
26
|
+
|
27
|
+
list = []
|
28
|
+
consumed = 0
|
29
|
+
length = nil
|
30
|
+
|
31
|
+
if opts[:sizeof]
|
32
|
+
len_handler = Binary.get_type_handler(opts[:sizeof])
|
33
|
+
length, ate = len_handler.from(binary, opts[:sizeof_opts])
|
34
|
+
consumed += ate
|
35
|
+
|
36
|
+
elsif opts[:length]
|
37
|
+
length = case opts[:length]
|
38
|
+
when :all
|
39
|
+
if type_of_binary_size
|
40
|
+
binary.size / type_of_binary_size
|
41
|
+
else
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
when Symbol
|
46
|
+
opts[:object].send(opts[:length])
|
47
|
+
|
48
|
+
else
|
49
|
+
opts[:length]
|
50
|
+
|
51
|
+
end
|
52
|
+
else
|
53
|
+
raise ArgumentError, "need one of [:sizeof, :length]"
|
54
|
+
end
|
55
|
+
|
56
|
+
cc, ate = nil, nil
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
if length.nil?
|
61
|
+
loop do
|
62
|
+
nextdata = binary[consumed .. -1]
|
63
|
+
break if !nextdata || nextdata == ""
|
64
|
+
cc, ate = type_of.from(binary[consumed .. -1], opts[:of_opts])
|
65
|
+
list << cc
|
66
|
+
consumed += ate
|
67
|
+
end
|
68
|
+
|
69
|
+
else
|
70
|
+
for i in 0...length do
|
71
|
+
cc, ate = type_of.from(binary[consumed .. -1], opts[:of_opts])
|
72
|
+
list << cc
|
73
|
+
consumed += ate
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
[list, consumed]
|
78
|
+
end
|
79
|
+
|
80
|
+
def to object, opts
|
81
|
+
object.is_a?(Array) or bogon! object, "require Array"
|
82
|
+
|
83
|
+
type_of = Binary.get_type_handler(opts[:of])
|
84
|
+
|
85
|
+
if opts[:sizeof]
|
86
|
+
len_handler = Binary.get_type_handler(opts[:sizeof])
|
87
|
+
([len_handler.to(object.size, opts[:sizeof_opts])] + object.map {|o|
|
88
|
+
type_of.to(o, opts[:of_opts])
|
89
|
+
}).join('')
|
90
|
+
|
91
|
+
elsif opts[:length]
|
92
|
+
length = case opts[:length]
|
93
|
+
when :all
|
94
|
+
object.size
|
95
|
+
when Symbol
|
96
|
+
object.size
|
97
|
+
else
|
98
|
+
opts[:length]
|
99
|
+
end
|
100
|
+
|
101
|
+
object.size == length or bogon! object,
|
102
|
+
"Array#size does not match required fixed width: " +
|
103
|
+
"have #{object.size}, require #{length.inspect}"
|
104
|
+
|
105
|
+
object.map {|o|
|
106
|
+
type_of.to(o, opts[:of_opts])
|
107
|
+
}.join('')
|
108
|
+
|
109
|
+
else
|
110
|
+
raise ArgumentError, "need one of [:sizeof, :length]"
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
Binary.register_type(ListBinaryType.new, :list)
|
117
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Arpie
|
2
|
+
class PackBinaryType < BinaryType
|
3
|
+
attr_reader :pack_string
|
4
|
+
|
5
|
+
def binary_size opts
|
6
|
+
opts = @force_opts.merge(opts || {})
|
7
|
+
PackBinaryType.length_of(@pack_string + (opts[:length] || 1).to_s)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.length_of format
|
11
|
+
length = 0
|
12
|
+
format.scan(/(\S_?)\s*(\d*)/).each do |directive, count|
|
13
|
+
count = count.to_i
|
14
|
+
count = 1 if count == 0
|
15
|
+
|
16
|
+
length += case directive
|
17
|
+
when 'A', 'a', 'C', 'c', 'Z', 'x' : count
|
18
|
+
when 'B', 'b' : (count / 8.0).ceil
|
19
|
+
when 'D', 'd', 'E', 'G' : count * 8
|
20
|
+
when 'e', 'F', 'f', 'g' : count * 4
|
21
|
+
when 'H', 'h' : (count / 2.0).ceil
|
22
|
+
when 'I', 'i', 'L', 'l', 'N', 'V' : count * 4
|
23
|
+
when 'n', 'S', 's', 'v' : count * 2
|
24
|
+
when 'Q', 'q' : count * 8
|
25
|
+
when 'X' : count * -1
|
26
|
+
else raise ArgumentError, "#{directive} is not supported"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
length
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize pack_string, force_opts = {}
|
34
|
+
@pack_string = pack_string
|
35
|
+
@force_opts = force_opts
|
36
|
+
end
|
37
|
+
|
38
|
+
def from binary, opts
|
39
|
+
opts = @force_opts.merge(opts || {})
|
40
|
+
binary && binary.size >= binary_size(opts) or incomplete!
|
41
|
+
len = opts[:length] || 1
|
42
|
+
pack_string = @pack_string + len.to_s
|
43
|
+
value = binary.unpack(pack_string)[0]
|
44
|
+
value += opts[:mod] if opts[:mod]
|
45
|
+
check_limit value, opts[:limit]
|
46
|
+
|
47
|
+
[value, binary_size(opts)]
|
48
|
+
end
|
49
|
+
|
50
|
+
def to object, opts
|
51
|
+
opts = @force_opts.merge(opts || {})
|
52
|
+
object.nil? and bogon! nil,"nil object given"
|
53
|
+
object -= opts[:mod] if opts[:mod]
|
54
|
+
len = opts[:length] || 1
|
55
|
+
pack_string = @pack_string + len.to_s
|
56
|
+
[object].pack(pack_string)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
Binary.register_type(PackBinaryType.new('c'), :uint8)
|
62
|
+
Binary.register_type(PackBinaryType.new("c"), :int8)
|
63
|
+
Binary.register_type(PackBinaryType.new("C"), :uint8)
|
64
|
+
Binary.register_type(PackBinaryType.new("s"), :int16)
|
65
|
+
Binary.register_type(PackBinaryType.new("S"), :uint16)
|
66
|
+
Binary.register_type(PackBinaryType.new("i"), :int32)
|
67
|
+
Binary.register_type(PackBinaryType.new("I"), :uint32)
|
68
|
+
Binary.register_type(PackBinaryType.new("q"), :int64)
|
69
|
+
Binary.register_type(PackBinaryType.new("Q"), :uint64)
|
70
|
+
|
71
|
+
Binary.register_type(PackBinaryType.new("l"), :long32)
|
72
|
+
Binary.register_type(PackBinaryType.new("L"), :ulong32)
|
73
|
+
|
74
|
+
Binary.register_type(PackBinaryType.new("n"), :nint16)
|
75
|
+
Binary.register_type(PackBinaryType.new("N"), :nint32)
|
76
|
+
Binary.register_type(PackBinaryType.new("v"), :lint16)
|
77
|
+
Binary.register_type(PackBinaryType.new("V"), :lint32)
|
78
|
+
|
79
|
+
Binary.register_type(PackBinaryType.new("d"), :double)
|
80
|
+
Binary.register_type(PackBinaryType.new("E"), :ldouble)
|
81
|
+
Binary.register_type(PackBinaryType.new("G"), :ndouble)
|
82
|
+
|
83
|
+
Binary.register_type(PackBinaryType.new("f"), :float)
|
84
|
+
Binary.register_type(PackBinaryType.new("e"), :lfloat)
|
85
|
+
Binary.register_type(PackBinaryType.new("g"), :nfloat)
|
86
|
+
|
87
|
+
Binary.register_type(PackBinaryType.new("B"), :msb_bitfield)
|
88
|
+
Binary.register_type(PackBinaryType.new("b"), :lsb_bitfield)
|
89
|
+
|
90
|
+
class BitBinaryType < Arpie::BinaryType
|
91
|
+
def from binary, opts
|
92
|
+
len = opts[:length] || 1
|
93
|
+
len = binary.size if len == :all
|
94
|
+
binary.size >= len or incomplete!
|
95
|
+
b = binary.split("")[0,len].map {|x|
|
96
|
+
x == "1"
|
97
|
+
}
|
98
|
+
b = b[0] if b.size == 1
|
99
|
+
[b, len]
|
100
|
+
end
|
101
|
+
|
102
|
+
def to object, opts
|
103
|
+
object = [object] if object.is_a?(TrueClass) || object.is_a?(FalseClass)
|
104
|
+
object.map {|x| x == true ? "1" : "0" }.join("")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
Arpie::Binary.register_type(BitBinaryType.new, :bit)
|
109
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Arpie
|
2
|
+
class BinaryType
|
3
|
+
include Arpie
|
4
|
+
|
5
|
+
def binary_size opts
|
6
|
+
nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def required_opts
|
10
|
+
{}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Return [object, len]
|
14
|
+
def from binary, opts
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return [binary]
|
19
|
+
def to object, opts
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
def check_limit value, limit
|
24
|
+
case limit
|
25
|
+
when nil
|
26
|
+
true
|
27
|
+
when Range, Array
|
28
|
+
limit.include?(value)
|
29
|
+
else
|
30
|
+
raise ArgumentError, "unknown limit definition: #{limit.inspect}"
|
31
|
+
end or bogon! nil, "not in :limit => #{limit.inspect}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
require 'arpie/binary/pack_type'
|
37
|
+
require 'arpie/binary/bytes_type'
|
38
|
+
require 'arpie/binary/fixed_type'
|
39
|
+
require 'arpie/binary/list_type'
|
data/lib/arpie/proxy.rb
CHANGED
@@ -51,8 +51,16 @@ module Arpie
|
|
51
51
|
|
52
52
|
begin
|
53
53
|
# Prune old serials. This can probably be optimized, but works well enough for now.
|
54
|
-
if @uuid_tracking && message.uuid
|
55
|
-
message.uuid
|
54
|
+
if @uuid_tracking && message.uuid
|
55
|
+
uuid, serial = * message.uuid
|
56
|
+
|
57
|
+
raise ArgumentError,
|
58
|
+
"Invalid UUID given, expect [uuid/64bit, serial/numeric]." unless
|
59
|
+
uuid.is_a?(Integer) && serial.is_a?(Integer)
|
60
|
+
|
61
|
+
# Limit to sane values.
|
62
|
+
uuid &= 0xffffffffffffffff
|
63
|
+
serial &= 0xffffffffffffffff
|
56
64
|
|
57
65
|
timestamps = @uuids.values.map {|v| v[0] }.sort
|
58
66
|
latest_timestamp = timestamps[-@max_uuids]
|
@@ -82,31 +90,47 @@ module Arpie
|
|
82
90
|
class ProxyClient < RPCClient
|
83
91
|
attr_accessor :namespace
|
84
92
|
|
93
|
+
# Set to false to disable replay protection.
|
94
|
+
# Default is true.
|
95
|
+
attr_accessor :replay_protection
|
96
|
+
|
97
|
+
# The current serial for this transport.
|
98
|
+
attr_accessor :serial
|
99
|
+
|
100
|
+
# The generated uuid for this Client.
|
101
|
+
# nil if no call has been made yet.
|
102
|
+
attr_accessor :uuid
|
103
|
+
|
85
104
|
def initialize *protocols
|
86
105
|
super
|
87
106
|
@protocol, @namespace = protocol, ""
|
107
|
+
@serial = 0
|
88
108
|
@uuid_generator = lambda {|client, method, argv|
|
89
|
-
UUID.random_create.to_i
|
109
|
+
UUIDTools::UUID.random_create.to_i
|
90
110
|
}
|
111
|
+
@replay_protection = true
|
91
112
|
end
|
92
113
|
|
93
114
|
# Set up a new UUID generator for this proxy client.
|
94
115
|
# Make sure that this yields really random numbers.
|
95
116
|
# The default uses the uuidtools gem and is usually okay.
|
96
|
-
# You can simply set this to nil (by calling it without
|
97
|
-
# a block).
|
98
117
|
#
|
99
|
-
#
|
118
|
+
# This gets called exactly once for each created ProxyClient.
|
100
119
|
def uuid_generator &handler #:yields: client, method, argv
|
101
120
|
@uuid_generator = handler
|
102
121
|
self
|
103
122
|
end
|
104
123
|
|
105
124
|
def method_missing meth, *argv # :nodoc:
|
106
|
-
|
107
|
-
|
125
|
+
serial = nil
|
126
|
+
if @replay_protection
|
127
|
+
serial = [
|
128
|
+
@uuid ||= @uuid_generator.call(self, meth, argv),
|
129
|
+
@serial += 1
|
130
|
+
]
|
131
|
+
end
|
108
132
|
|
109
|
-
call = Arpie::RPCall.new(@namespace, meth, argv,
|
133
|
+
call = Arpie::RPCall.new(@namespace, meth, argv, serial)
|
110
134
|
ret = self.request(call)
|
111
135
|
case ret
|
112
136
|
when Exception
|
data/spec/binary_spec.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe "Binary" do
|
4
|
+
|
5
|
+
describe "empty:" do subject {
|
6
|
+
[
|
7
|
+
Class.new(Binary) do
|
8
|
+
end,
|
9
|
+
""
|
10
|
+
]
|
11
|
+
}
|
12
|
+
it_should_behave_like "Binary Tests"
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "basic sanity:" do subject {
|
16
|
+
[
|
17
|
+
Class.new(Binary) do
|
18
|
+
field :a, :uint32
|
19
|
+
field :b, :uint32
|
20
|
+
field :c, :uint64
|
21
|
+
field :s, :string, :length => 4
|
22
|
+
end,
|
23
|
+
[1, 2, 3, "abcd"].pack("I I Q a4")
|
24
|
+
]
|
25
|
+
}
|
26
|
+
|
27
|
+
it_should_behave_like "Binary Tests with data"
|
28
|
+
end
|
29
|
+
|
30
|
+
describe ".virtual:" do subject {
|
31
|
+
[
|
32
|
+
Class.new(Binary) do
|
33
|
+
field :a, :uint8
|
34
|
+
field :b, :uint8
|
35
|
+
virtual :c, :uint8 do |o| o.a * o.b end
|
36
|
+
end,
|
37
|
+
[1, 2].pack("CC")
|
38
|
+
]
|
39
|
+
}
|
40
|
+
|
41
|
+
it_should_behave_like "Binary Tests with data"
|
42
|
+
|
43
|
+
it "evaluates the block given" do
|
44
|
+
b, co = @c.from(@d)
|
45
|
+
b.c.should == b.a * b.b
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe ".aliases:" do subject {
|
50
|
+
[
|
51
|
+
Class.new(Binary) do
|
52
|
+
f :a, :uint8
|
53
|
+
f :b, :uint8
|
54
|
+
s "hio!"
|
55
|
+
v :c, :uint8 do |o| o.a * o.b end
|
56
|
+
end,
|
57
|
+
[1, 2, "hio!"].pack("CCa4")
|
58
|
+
]
|
59
|
+
}
|
60
|
+
|
61
|
+
it_should_behave_like "Binary Tests with data"
|
62
|
+
end
|
63
|
+
|
64
|
+
describe ":default:" do subject {
|
65
|
+
[
|
66
|
+
Class.new(Binary) do
|
67
|
+
field :a, :uint8
|
68
|
+
field :b, :uint8, :default => 5, :optional => true
|
69
|
+
v :c, :uint8 do |o| o.a * o.b end
|
70
|
+
end,
|
71
|
+
[1, 2].pack("CC")
|
72
|
+
]
|
73
|
+
}
|
74
|
+
|
75
|
+
it_should_behave_like "Binary Tests with data"
|
76
|
+
|
77
|
+
it "does not use the default when a value was read" do
|
78
|
+
b, con = @c.from([1, 2].pack("CC"))
|
79
|
+
b.a.should == 1
|
80
|
+
b.b.should == 2
|
81
|
+
end
|
82
|
+
|
83
|
+
it "uses the default when no value is present" do
|
84
|
+
b, con = @c.from([1].pack("C"))
|
85
|
+
b.a.should == 1
|
86
|
+
b.b.should == 5
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe "BytesBinaryType" do
|
4
|
+
describe "sizeof and length" do subject {
|
5
|
+
[
|
6
|
+
Class.new(Arpie::Binary) do
|
7
|
+
field :s, :string, :sizeof => :uint8
|
8
|
+
field :d, :string, :length => 5
|
9
|
+
end,
|
10
|
+
[4, "abcd", "12345"].pack("C a4 a5")
|
11
|
+
]
|
12
|
+
}
|
13
|
+
|
14
|
+
it_should_behave_like "Binary Tests with data"
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "virtual length" do subject {
|
18
|
+
[
|
19
|
+
Class.new(Arpie::Binary) do
|
20
|
+
field :v, :uint8
|
21
|
+
field :d, :string, :length => :v
|
22
|
+
end,
|
23
|
+
[4, "abcd"].pack("C a4")
|
24
|
+
]
|
25
|
+
}
|
26
|
+
|
27
|
+
it_should_behave_like "Binary Tests with data"
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "length as virtual should not update the virtual" do subject {
|
31
|
+
[
|
32
|
+
Class.new(Arpie::Binary) do
|
33
|
+
field :data_length, :uint8
|
34
|
+
field :data, :bytes, :length => :data_length
|
35
|
+
end,
|
36
|
+
[4, "abcd"].pack("C a4")
|
37
|
+
]
|
38
|
+
}
|
39
|
+
|
40
|
+
it_should_behave_like "Binary Tests with data"
|
41
|
+
|
42
|
+
it "does not change virtuals on data change" do
|
43
|
+
dd, b = @c.from(@d + "xxyzz")
|
44
|
+
dd.data += "a"
|
45
|
+
expect = [4, "abcda"].pack("Ca*")
|
46
|
+
dd.to.should == expect
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/spec/client_server_spec.rb
CHANGED
@@ -94,7 +94,7 @@ describe "ProxyServer" do
|
|
94
94
|
it "should not re-evaluate for already-seen uuids" do
|
95
95
|
@client.evaluate_calls
|
96
96
|
@client.evaluate_calls
|
97
|
-
@client.
|
97
|
+
@client.serial -= 1
|
98
98
|
@client.evaluate_calls
|
99
99
|
@client.evaluate_calls
|
100
100
|
$evaluate_calls.should == 3
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe FixedBinaryType do
|
4
|
+
|
5
|
+
it "always returns the fixed size" do
|
6
|
+
subject.from("aaaa", :value => "aaaa").should == ["aaaa", 4]
|
7
|
+
end
|
8
|
+
|
9
|
+
it "always encodes to fixed size" do
|
10
|
+
subject.to("aaaa", :value => "aaaa").should == "aaaa"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "returns the proper binary size" do
|
14
|
+
subject.binary_size(:value => "aaaa").should == 4
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should raise error on mismatch in #from" do
|
18
|
+
proc { subject.from("aaa", :value => "aaaa") }.should raise_error StreamError
|
19
|
+
end
|
20
|
+
|
21
|
+
it "raises error on mismatch in #to" do
|
22
|
+
proc { subject.to("aaa", :value => "aaaa") }.should raise_error StreamError
|
23
|
+
end
|
24
|
+
|
25
|
+
it "raiseserror on missing option" do
|
26
|
+
proc { subject.from("aaa", {}) }.should raise_error ArgumentError
|
27
|
+
proc { subject.to("aaa", {}) }.should raise_error ArgumentError
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe "ListBinaryType" do
|
4
|
+
describe "length as virtual" do
|
5
|
+
class Inner < Arpie::Binary
|
6
|
+
field :sz, :uint8
|
7
|
+
field :ls, :list, :of => :uint8, :length => :sz
|
8
|
+
end
|
9
|
+
|
10
|
+
class Outer < Arpie::Binary
|
11
|
+
field :totalsz, :uint8
|
12
|
+
|
13
|
+
field :bytes, :bytes, :length => :totalsz do
|
14
|
+
field :content, :list, :of => Inner,
|
15
|
+
:length => :all
|
16
|
+
end
|
17
|
+
|
18
|
+
field :end, :uint8
|
19
|
+
end
|
20
|
+
|
21
|
+
it "reads correctly when all bytes are eaten" do
|
22
|
+
c, consumed = Outer.from [8, 2, 0, 1, 4, 0, 1, 2, 3, 0xff].pack("C*")
|
23
|
+
consumed.should == 10
|
24
|
+
c.bytes.content.size.should == 2
|
25
|
+
c.bytes.content[0].ls.should == [0, 1]
|
26
|
+
c.bytes.content[1].ls.should == [0, 1, 2, 3]
|
27
|
+
c.end.should == 0xff
|
28
|
+
end
|
29
|
+
|
30
|
+
it "raises EIncomplete when partial data remains" do
|
31
|
+
proc { Outer.from [8, 0, 1, 2, 3, 4, 5, 6, 7, 4, 1, 2 ].pack("C*") }.should raise_error Arpie::EIncomplete
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,6 +6,7 @@ unless Object.const_defined?('Arpie')
|
|
6
6
|
$:.unshift(File.join(File.dirname(__FILE__), "../lib/"))
|
7
7
|
require 'arpie'
|
8
8
|
end
|
9
|
+
include Arpie
|
9
10
|
|
10
11
|
describe "IO Mockup", :shared => true do
|
11
12
|
before do
|
@@ -56,3 +57,35 @@ describe "RPCProtocolChainSetup", :shared => true do
|
|
56
57
|
end
|
57
58
|
end
|
58
59
|
|
60
|
+
|
61
|
+
|
62
|
+
describe "Binary Setup", :shared => true do
|
63
|
+
before do
|
64
|
+
@c = subject[0]
|
65
|
+
@d = subject[1]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "Binary Tests", :shared => true do
|
70
|
+
it_should_behave_like "Binary Setup"
|
71
|
+
|
72
|
+
it "reads and packs the given example binary" do
|
73
|
+
dd, b = @c.from(@d)
|
74
|
+
dd.to.should == @d
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "Binary Tests with data", :shared => true do
|
79
|
+
it_should_behave_like "Binary Tests"
|
80
|
+
|
81
|
+
it "should raise EIncomplete when not enough data is available" do
|
82
|
+
proc { @c.from("")}.should raise_error Arpie::EIncomplete
|
83
|
+
end
|
84
|
+
|
85
|
+
it "returns the proper cutoff position" do
|
86
|
+
dd, b = @c.from(@d + "xxyzz")
|
87
|
+
b.should == @d.size
|
88
|
+
end
|
89
|
+
|
90
|
+
it "returns a valid binary size"
|
91
|
+
end
|
data/tools/benchmark.rb
CHANGED
@@ -46,7 +46,7 @@ Benchmark.bm {|b|
|
|
46
46
|
|
47
47
|
puts ""
|
48
48
|
puts "Arpie: proxied MarshalProtocol without replay protection"
|
49
|
-
$proxy.
|
49
|
+
$proxy.replay_protection = false
|
50
50
|
b.report(" 1") { 1.times { $proxy.reverse "benchmark" } }
|
51
51
|
b.report("1000") { 1000.times { $proxy.reverse "benchmark" } }
|
52
52
|
}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arpie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bernhard Stoeckner
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-08-25 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version:
|
23
|
+
version: 2.0.0
|
24
24
|
version:
|
25
25
|
description: A high-performing layered networking protocol framework. Simple to use, simple to extend.
|
26
26
|
email: elven@swordcoast.net
|
@@ -36,26 +36,36 @@ files:
|
|
36
36
|
- COPYING
|
37
37
|
- README
|
38
38
|
- Rakefile
|
39
|
+
- spec/protocol_merge_and_split_spec.rb
|
39
40
|
- spec/protocol_spec.rb
|
41
|
+
- spec/bytes_binary_type_spec.rb
|
42
|
+
- spec/fixed_binary_type_spec.rb
|
43
|
+
- spec/list_binary_type_spec.rb
|
40
44
|
- spec/spec.opts
|
41
|
-
- spec/spec_helper.rb
|
42
|
-
- spec/protocol_merge_and_split_spec.rb
|
43
45
|
- spec/rcov.opts
|
46
|
+
- spec/spec_helper.rb
|
47
|
+
- spec/binary_spec.rb
|
44
48
|
- spec/client_server_spec.rb
|
45
|
-
- lib/arpie.rb
|
46
|
-
- lib/arpie
|
47
|
-
- lib/arpie/client.rb
|
48
|
-
- lib/arpie/error.rb
|
49
|
-
- lib/arpie/protocol.rb
|
50
|
-
- lib/arpie/binary.rb
|
51
|
-
- lib/arpie/proxy.rb
|
52
49
|
- lib/arpie/xmlrpc.rb
|
50
|
+
- lib/arpie/proxy.rb
|
53
51
|
- lib/arpie/server.rb
|
54
|
-
-
|
52
|
+
- lib/arpie/binary.rb
|
53
|
+
- lib/arpie/protocol.rb
|
54
|
+
- lib/arpie/error.rb
|
55
|
+
- lib/arpie/client.rb
|
56
|
+
- lib/arpie/binary_types.rb
|
57
|
+
- lib/arpie/binary/bytes_type.rb
|
58
|
+
- lib/arpie/binary/fixed_type.rb
|
59
|
+
- lib/arpie/binary/list_type.rb
|
60
|
+
- lib/arpie/binary/pack_type.rb
|
61
|
+
- lib/arpie.rb
|
55
62
|
- tools/protocol_benchmark.rb
|
63
|
+
- tools/benchmark.rb
|
56
64
|
- BINARY_SPEC
|
57
65
|
has_rdoc: true
|
58
66
|
homepage: http://arpie.elv.es
|
67
|
+
licenses: []
|
68
|
+
|
59
69
|
post_install_message:
|
60
70
|
rdoc_options:
|
61
71
|
- --quiet
|
@@ -84,9 +94,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
94
|
requirements: []
|
85
95
|
|
86
96
|
rubyforge_project: arpie
|
87
|
-
rubygems_version: 1.3.
|
97
|
+
rubygems_version: 1.3.4
|
88
98
|
signing_key:
|
89
|
-
specification_version:
|
99
|
+
specification_version: 3
|
90
100
|
summary: A high-performing layered networking protocol framework. Simple to use, simple to extend.
|
91
101
|
test_files: []
|
92
102
|
|