arpie 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|