arpie 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- === :sizeof
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
- == :list
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
- == :bitfield
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, :sizeof => (value of :len_a * :len_b)
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, :sizeof => :v_len
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
- require "rake/rdoctask"
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.5"
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', '>= 1.0.7')
51
+ s.add_dependency('uuidtools', '>= 2.0.0')
48
52
  end
49
53
 
50
54
  Rake::GemPackageTask.new(spec) do |p|
@@ -1,6 +1,7 @@
1
1
  require 'arpie/error'
2
2
  require 'arpie/protocol'
3
3
  require 'arpie/binary'
4
+ require 'arpie/binary_types'
4
5
  require 'arpie/xmlrpc'
5
6
  require 'arpie/server'
6
7
  require 'arpie/client'
@@ -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
- # => in `test': wrong number of arguments (ArgumentError)
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
- "#<#{klass}#{desc} #{@fields.inspect}>"
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
- sum + klass.binary_size(field.opts)
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'
@@ -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 && message.uuid.is_a?(Numeric)
55
- message.uuid &= 0xffffffffffffffff
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
- # Note that disabling this disables replay protection.
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
- uuid = @uuid_generator ?
107
- @uuid_generator.call(self, meth, argv) : nil
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, uuid)
133
+ call = Arpie::RPCall.new(@namespace, meth, argv, serial)
110
134
  ret = self.request(call)
111
135
  case ret
112
136
  when Exception
@@ -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
@@ -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.uuid_generator do 100 end
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
@@ -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
@@ -46,7 +46,7 @@ Benchmark.bm {|b|
46
46
 
47
47
  puts ""
48
48
  puts "Arpie: proxied MarshalProtocol without replay protection"
49
- $proxy.uuid_generator
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.5
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-02-24 00:00:00 +01:00
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: 1.0.7
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
- - tools/benchmark.rb
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.0
97
+ rubygems_version: 1.3.4
88
98
  signing_key:
89
- specification_version: 2
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