arpie 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/BINARY_SPEC +218 -0
- data/README +46 -0
- data/Rakefile +2 -2
- data/lib/arpie/binary.rb +754 -0
- data/lib/arpie/error.rb +39 -0
- data/lib/arpie/protocol.rb +103 -106
- data/lib/arpie/xmlrpc.rb +4 -4
- data/lib/arpie.rb +2 -0
- data/spec/protocol_merge_and_split_spec.rb +28 -8
- data/spec/protocol_spec.rb +4 -0
- metadata +12 -8
data/lib/arpie/binary.rb
ADDED
@@ -0,0 +1,754 @@
|
|
1
|
+
module Arpie
|
2
|
+
|
3
|
+
# A Binary is a helper to convert arbitary bitlevel binary data
|
4
|
+
# to and from ruby Struct-lookalikes.
|
5
|
+
#
|
6
|
+
# Here's an example:
|
7
|
+
#
|
8
|
+
# class Test < Arpie::Binary
|
9
|
+
# describe "I am a test"
|
10
|
+
#
|
11
|
+
# field :a, :uint8
|
12
|
+
# field :b, :bytes, :sizeof => :nint16
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# This will allow you to parse binary blocks consisting of:
|
16
|
+
# - a byte
|
17
|
+
# - a network-order int (16 bit)
|
18
|
+
# - a string which length' is the given int
|
19
|
+
# Rinse and repeat.
|
20
|
+
#
|
21
|
+
# For all available data types, please look into the source
|
22
|
+
# file of this class at the bottom.
|
23
|
+
#
|
24
|
+
# Writing new types is easy enough, (see BinaryType).
|
25
|
+
#
|
26
|
+
# On confusing names:
|
27
|
+
#
|
28
|
+
# Arpie uses the term +binary+ to refer to on-the-wire data bit/byte binary format.
|
29
|
+
# A +Binary+ (uppercase) is a object describing the format. If you're reading +binary+,
|
30
|
+
# think "raw data"; if you're reading +Binary+ or +object+, think Arpie::Binary.
|
31
|
+
#
|
32
|
+
# Another warning:
|
33
|
+
#
|
34
|
+
# Do not use +Kernel+ methods as field names. It'll confuse method_missing.
|
35
|
+
# Example:
|
36
|
+
# field :test, :uint8
|
37
|
+
# => in `test': wrong number of arguments (ArgumentError)
|
38
|
+
#
|
39
|
+
# In fact, this is the reason while Binary will not let you define fields with
|
40
|
+
# with names like existing instance methods.
|
41
|
+
class Binary
|
42
|
+
extend Arpie
|
43
|
+
class Field < Struct.new(:name, :type, :opts, :inline_handler) ; end
|
44
|
+
class Virtual < Struct.new(:name, :type, :opts, :handler) ; end
|
45
|
+
|
46
|
+
|
47
|
+
@@types ||= {}
|
48
|
+
@@fields ||= {}
|
49
|
+
@@virtuals ||= {}
|
50
|
+
@@description ||= {}
|
51
|
+
@@hooks ||= {}
|
52
|
+
|
53
|
+
#:stopdoc:
|
54
|
+
@@anonymous ||= {}
|
55
|
+
def self.__anonymous
|
56
|
+
@@anonymous[self]
|
57
|
+
end
|
58
|
+
def self.__anonymous= x
|
59
|
+
@@anonymous[self] = x
|
60
|
+
end
|
61
|
+
#:startdoc:
|
62
|
+
|
63
|
+
def initialize
|
64
|
+
@fields = {}
|
65
|
+
|
66
|
+
# set up our own class handlers, create anon classes, set up default values
|
67
|
+
@@fields[self.class].each {|field|
|
68
|
+
if field.inline_handler
|
69
|
+
@fields[field.name] = field.inline_handler.new
|
70
|
+
|
71
|
+
elsif field.type.is_a?(Class)
|
72
|
+
@fields[field.name] = field.type.new
|
73
|
+
|
74
|
+
elsif field.opts[:default]
|
75
|
+
@fields[field.name] = field.opts[:default]
|
76
|
+
end
|
77
|
+
}
|
78
|
+
if block_given?
|
79
|
+
yield self
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def inspect #:nodoc:
|
84
|
+
desc = " " + @@description[self.class].inspect if @@description[self.class]
|
85
|
+
# Anonymous is special
|
86
|
+
klass = self.class.respond_to?(:__anonymous) && self.class.__anonymous ?
|
87
|
+
"Anon#{self.class.__anonymous.inspect}" :
|
88
|
+
self.class.to_s
|
89
|
+
|
90
|
+
"#<#{klass}#{desc} #{@fields.inspect}>"
|
91
|
+
end
|
92
|
+
|
93
|
+
def method_missing m, *a
|
94
|
+
m.to_s =~ /^(.+?)(=?)$/ or super
|
95
|
+
at = $1.to_sym
|
96
|
+
if self.class.field?(at)
|
97
|
+
if $2 == "="
|
98
|
+
a.size == 1 or raise ArgumentError
|
99
|
+
if !a[0].is_a?(Class) && inline = self.class.get_field(at)[3]
|
100
|
+
a[0], __nil = inline.from(a[0], {})
|
101
|
+
end
|
102
|
+
@fields[at] = a[0]
|
103
|
+
else
|
104
|
+
@fields[at]
|
105
|
+
end
|
106
|
+
|
107
|
+
elsif self.class.virtual?(at)
|
108
|
+
if $2 == "="
|
109
|
+
raise ArgumentError
|
110
|
+
else
|
111
|
+
Binary.call_virtual(self, at)
|
112
|
+
end
|
113
|
+
|
114
|
+
else
|
115
|
+
super
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.call_virtual(on_object, name)
|
120
|
+
@@virtuals[on_object.class].select {|virtual|
|
121
|
+
virtual.name == name
|
122
|
+
}[0].handler.call(on_object)
|
123
|
+
end
|
124
|
+
|
125
|
+
# This registers a new type with this binary.
|
126
|
+
def self.register_type handler, *type_aliases
|
127
|
+
type_aliases.each do |type|
|
128
|
+
@@types[type] = handler
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns true if this Binary has the named +field+.
|
133
|
+
def self.field? field
|
134
|
+
@@fields[self] or return false
|
135
|
+
@@fields[self].select {|_field|
|
136
|
+
_field.name == field
|
137
|
+
}.size > 0
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.get_field field
|
141
|
+
@@fields[self] or raise ArgumentError, "No such field: #{field.inspect}"
|
142
|
+
@@fields[self].each {|_field|
|
143
|
+
_field.name == field and return _field
|
144
|
+
}
|
145
|
+
raise ArgumentError, "No such field: #{field.inspect}"
|
146
|
+
end
|
147
|
+
|
148
|
+
# Returns true if this Binary has the named +virtual+.
|
149
|
+
def self.virtual? virtual
|
150
|
+
@@virtuals[self] or return false
|
151
|
+
@@virtuals[self].select {|_virtual|
|
152
|
+
_virtual.name == virtual
|
153
|
+
}.size > 0
|
154
|
+
end
|
155
|
+
|
156
|
+
# Returns the BinaryType handler class for +type+, which can be a
|
157
|
+
# symbol (:uint8), or a Arpie::Binary.
|
158
|
+
def self.get_type_handler type
|
159
|
+
if type.class === Arpie::Binary
|
160
|
+
type
|
161
|
+
else
|
162
|
+
@@types[type] or raise ArgumentError,
|
163
|
+
"#{self}: No such field type: #{type.inspect}"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.describe_all_types
|
168
|
+
ret = []
|
169
|
+
strf = "%-15s %-8s %s"
|
170
|
+
ret << strf % %w{TYPE WIDTH HANDLER}
|
171
|
+
@@types.sort{|a,b| a[0].to_s <=> b[0].to_s}.each {|type, handler|
|
172
|
+
ret << strf % [type.inspect, (handler.binary_size({}) rescue nil), handler.inspect]
|
173
|
+
}
|
174
|
+
ret.join("\n")
|
175
|
+
end
|
176
|
+
|
177
|
+
# You can use this to provide a short description of this Binary.
|
178
|
+
# It will be shown when calling Binary.inspect.
|
179
|
+
# When called without a parameter, (non-recursively) print out a
|
180
|
+
# pretty description of this data type as it would appear on the wire.
|
181
|
+
def self.describe text = nil
|
182
|
+
unless text.nil?
|
183
|
+
@@description[self] = text
|
184
|
+
else
|
185
|
+
ret = []
|
186
|
+
ret << "%-10s %s" % ["Binary:", @@description[self]]
|
187
|
+
ret << ""
|
188
|
+
|
189
|
+
sprf = "%-10s %-25s %-15s %-15s %-15s %s"
|
190
|
+
sprf_of = "%68s %s"
|
191
|
+
if @@virtuals[self] && @@virtuals[self].size > 0
|
192
|
+
ret << sprf % ["Virtuals:", "NAME", "TYPE", "WIDTH", "", "DESCRIPTION"]
|
193
|
+
@@virtuals[self].each {|virtual|
|
194
|
+
width = self.get_type_handler(virtual.type).binary_size(virtual.opts)
|
195
|
+
ret << sprf % [ "",
|
196
|
+
virtual.name,
|
197
|
+
virtual.type,
|
198
|
+
width,
|
199
|
+
"",
|
200
|
+
virtual.opts[:description]
|
201
|
+
]
|
202
|
+
}
|
203
|
+
ret << ""
|
204
|
+
end
|
205
|
+
if @@fields[self] && @@fields[self].size > 0
|
206
|
+
ret << sprf % %w{Fields: NAME TYPE WIDTH OF DESCRIPTION}
|
207
|
+
@@fields[self].each {|field|
|
208
|
+
width = self.get_type_handler(field.type).binary_size(field.opts)
|
209
|
+
ret << sprf % [ "",
|
210
|
+
field.name,
|
211
|
+
field.type,
|
212
|
+
(field.opts[:length] || field.opts[:sizeof] || width),
|
213
|
+
field.opts[:of] ? field.opts[:of].inspect : "",
|
214
|
+
field.opts[:description]
|
215
|
+
]
|
216
|
+
ret << sprf_of % [ "",
|
217
|
+
field.opts[:of_opts].inspect
|
218
|
+
] if field.opts[:of_opts]
|
219
|
+
}
|
220
|
+
end
|
221
|
+
ret.join("\n")
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Specify that this Binary has a field of type +type+.
|
226
|
+
# See the class documentation for usage.
|
227
|
+
def self.field name, type = nil, opts = {}, &block
|
228
|
+
@@fields[self] ||= []
|
229
|
+
handler = get_type_handler(type)
|
230
|
+
|
231
|
+
raise ArgumentError, "#{name.inspect} already exists as a virtual" if virtual?(name)
|
232
|
+
raise ArgumentError, "#{name.inspect} already exists as a field" if field?(name)
|
233
|
+
raise ArgumentError, "#{name.inspect} already exists as a instance method" if instance_methods.index(name.to_s)
|
234
|
+
raise ArgumentError, "#{name.inspect}: cannot inline classes" if block_given? and type.class === Arpie::Binary
|
235
|
+
|
236
|
+
@@fields[self].each {|field|
|
237
|
+
raise ArgumentError, "#{name.inspect}: :optional fields cannot be followed by required fields" if
|
238
|
+
field[:opts].include?(:optional)
|
239
|
+
} unless opts[:optional]
|
240
|
+
|
241
|
+
|
242
|
+
type.nil? && !block_given? and raise ArgumentError,
|
243
|
+
"You need to specify an inline handler if no type is given."
|
244
|
+
inline_handler = nil
|
245
|
+
|
246
|
+
if block_given?
|
247
|
+
inline_handler = Class.new(Arpie::Binary)
|
248
|
+
inline_handler.__anonymous = [name, type, opts]
|
249
|
+
inline_handler.instance_eval(&block)
|
250
|
+
end
|
251
|
+
|
252
|
+
if type.nil?
|
253
|
+
type, inline_handler = inline_handler, nil
|
254
|
+
end
|
255
|
+
|
256
|
+
if handler.respond_to?(:required_opts)
|
257
|
+
missing_required = handler.required_opts.keys - opts.keys
|
258
|
+
raise ArgumentError, "#{self}: #{name.inspect} as type #{type.inspect} " +
|
259
|
+
"requires options: #{missing_required.inspect}" if missing_required.size > 0
|
260
|
+
handler.required_opts.each {|k,v|
|
261
|
+
v.nil? and next
|
262
|
+
v.call(opts[k]) or raise ArgumentError, "#{self}: Invalid value given for opt key #{k.inspect}."
|
263
|
+
}
|
264
|
+
end
|
265
|
+
|
266
|
+
opts[:description] ||= opts[:desc] if opts[:desc]
|
267
|
+
opts.delete(:desc)
|
268
|
+
|
269
|
+
@@fields[self] << Field.new(name.to_sym, type, opts, inline_handler)
|
270
|
+
end
|
271
|
+
|
272
|
+
# Set up a new virtual field
|
273
|
+
def self.virtual name, type, opts = {}, &handler
|
274
|
+
raise ArgumentError, "You need to pass a block with virtuals" unless block_given?
|
275
|
+
raise ArgumentError, "#{name.inspect} already exists as a virtual" if virtual?(name)
|
276
|
+
raise ArgumentError, "#{name.inspect} already exists as a field" if field?(name)
|
277
|
+
raise ArgumentError, "#{name.inspect} already exists as a instance method" if instance_methods.index(name.to_s)
|
278
|
+
|
279
|
+
@@virtuals[self] ||= []
|
280
|
+
opts[:description] ||= opts[:desc]
|
281
|
+
opts.delete(:desc)
|
282
|
+
@@virtuals[self] << Virtual.new(name.to_sym, type, opts, handler)
|
283
|
+
end
|
284
|
+
|
285
|
+
|
286
|
+
def self.binary_size opts = {}
|
287
|
+
@@fields[self] ||= []
|
288
|
+
total = @@fields[self].inject(0) {|sum, field|
|
289
|
+
klass = get_type_handler(field.type)
|
290
|
+
sum + klass.binary_size(field.opts)
|
291
|
+
}
|
292
|
+
|
293
|
+
total
|
294
|
+
end
|
295
|
+
|
296
|
+
def self.add_hook(hook, handler)
|
297
|
+
@@hooks[self] ||= {}
|
298
|
+
@@hooks[self][hook] ||= []
|
299
|
+
@@hooks[self][hook] << handler
|
300
|
+
end
|
301
|
+
|
302
|
+
def self.call_hooks(hook, *va)
|
303
|
+
@@hooks[self] ||= {}
|
304
|
+
@@hooks[self][hook] ||= []
|
305
|
+
@@hooks[self][hook].each {|handler|
|
306
|
+
va = *handler.call(*va)
|
307
|
+
}
|
308
|
+
va
|
309
|
+
end
|
310
|
+
|
311
|
+
# Parse the given +binary+, which is a string, and return an instance of this class.
|
312
|
+
# Will raise Arpie::EIncomplete when not enough data is available in +binary+ to construct
|
313
|
+
# a complete Binary.
|
314
|
+
def self.from binary, opts = {}
|
315
|
+
@@fields[self] ||= []
|
316
|
+
binary = * self.call_hooks(:pre_from, binary)
|
317
|
+
|
318
|
+
consumed_bytes = 0
|
319
|
+
obj = new
|
320
|
+
@@fields[self].each {|field| # name, klass, kopts, inline_handler|
|
321
|
+
field.opts[:binary] = binary
|
322
|
+
field.opts[:object] = obj
|
323
|
+
handler = get_type_handler(field.type)
|
324
|
+
|
325
|
+
attrib, consumed = binary, nil
|
326
|
+
|
327
|
+
attrib, consumed =
|
328
|
+
handler.from(binary[consumed_bytes .. -1], field.opts) rescue case $!
|
329
|
+
when EIncomplete
|
330
|
+
if field.opts[:optional]
|
331
|
+
attrib, consumed = field.opts[:default], handler.binary_size(field.opts)
|
332
|
+
else
|
333
|
+
raise $!,
|
334
|
+
"#{$!.to_s}, #{self}#from needs more data for " +
|
335
|
+
"#{field.name.inspect}. (data: #{binary[consumed_bytes .. -1].inspect})"
|
336
|
+
end
|
337
|
+
when StreamError
|
338
|
+
bogon! binary[consumed_bytes .. -1], "#{self}#from: #{field.name.inspect}: #{$!.to_s}"
|
339
|
+
else
|
340
|
+
raise
|
341
|
+
end
|
342
|
+
consumed_bytes += consumed
|
343
|
+
|
344
|
+
obj.send((field.name.to_s + "=").to_sym, attrib)
|
345
|
+
field.opts.delete(:binary)
|
346
|
+
field.opts.delete(:object)
|
347
|
+
}
|
348
|
+
|
349
|
+
binary, obj, consumed_bytes = self.call_hooks(:post_from, binary, obj, consumed_bytes)
|
350
|
+
[obj, consumed_bytes]
|
351
|
+
end
|
352
|
+
|
353
|
+
# Recursively convert the given Binary object to wire format.
|
354
|
+
def self.to object, opts = {}
|
355
|
+
object.nil? and raise ArgumentError, "cannot #to nil"
|
356
|
+
@@fields[self] ||= []
|
357
|
+
r = []
|
358
|
+
object = * self.call_hooks(:pre_to, object)
|
359
|
+
|
360
|
+
@@fields[self].each {|field| # name, klass, kopts, inline_handler|
|
361
|
+
field.opts[:object] = object
|
362
|
+
handler = get_type_handler(field.type)
|
363
|
+
val = object.send(field.name)
|
364
|
+
|
365
|
+
if field.inline_handler
|
366
|
+
val = val.to
|
367
|
+
end
|
368
|
+
|
369
|
+
# r << (val.respond_to?(:to) ? val.to(opts) : handler.to(val, kopts)) rescue case $!
|
370
|
+
r << handler.to(val, field.opts) rescue case $!
|
371
|
+
when StreamError
|
372
|
+
raise $!, "#{self}#from: #{field.name.inspect}: #{$!.to_s}"
|
373
|
+
else
|
374
|
+
raise
|
375
|
+
end
|
376
|
+
field.opts.delete(:object)
|
377
|
+
}
|
378
|
+
|
379
|
+
r = r.join('')
|
380
|
+
_obj, r = self.call_hooks(:post_to, object, r)
|
381
|
+
r
|
382
|
+
end
|
383
|
+
|
384
|
+
def to opts = {}
|
385
|
+
self.class.to(self, opts)
|
386
|
+
end
|
387
|
+
|
388
|
+
# Add a hook that gets called before converting a binary to
|
389
|
+
# Binary representation.
|
390
|
+
# Arguments to the handler: +binary+
|
391
|
+
# Note that all handlers need to return their arguemts as they were
|
392
|
+
# passed, as they will replace the original values.
|
393
|
+
def self.pre_to &handler
|
394
|
+
self.add_hook(:pre_to, handler)
|
395
|
+
end
|
396
|
+
# Add a hook that gets called after converting a binary to
|
397
|
+
# Binary representation.
|
398
|
+
# Arguments to the handler: +object+, +binary+, +consumed_bytes+.
|
399
|
+
# Note that all handlers need to return their arguemts as they were
|
400
|
+
# passed, as they will replace the original values.
|
401
|
+
def self.post_to &handler
|
402
|
+
self.add_hook(:post_to, handler)
|
403
|
+
end
|
404
|
+
# Add a hook that gets called before converting a Binary to
|
405
|
+
# wire format.
|
406
|
+
# Arguments to the handler: +object+
|
407
|
+
# Note that all handlers need to return their arguemts as they were
|
408
|
+
# passed, as they will replace the original values.
|
409
|
+
def self.pre_from &handler
|
410
|
+
self.add_hook(:pre_from, handler)
|
411
|
+
end
|
412
|
+
# Add a hook that gets called after converting a Binary to
|
413
|
+
# wire format.
|
414
|
+
# Arguments to the handler: +binary+, +object+
|
415
|
+
# Note that all handlers need to return their arguemts as they were
|
416
|
+
# passed, as they will replace the original values.
|
417
|
+
def self.post_from &handler
|
418
|
+
self.add_hook(:post_from, handler)
|
419
|
+
end
|
420
|
+
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
|
+
end
|