arpie 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/BINARY_SPEC +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
|