depix 1.1.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,15 +0,0 @@
1
- require File.dirname(__FILE__) + '/../depix'
2
-
3
- require 'benchmark'
4
-
5
- iter = 1000
6
-
7
- puts "Reading DPX header #{iter} times, all data"
8
- puts Benchmark.measure {
9
- iter.times { Depix.from_file(File.dirname(__FILE__)+"/../../test/samples/026_FROM_HERO_TAPE_5-3-1_MOV.0029.dpx", false) }
10
- }
11
-
12
- puts "Reading DPX header #{iter} times, compact data"
13
- puts Benchmark.measure {
14
- iter.times { Depix.from_file(File.dirname(__FILE__)+"/../../test/samples/026_FROM_HERO_TAPE_5-3-1_MOV.0029.dpx", true) }
15
- }
@@ -1,531 +0,0 @@
1
- module Depix
2
-
3
- #:stopdoc:
4
-
5
- =begin
6
- A basic C structs library (only works by value).
7
- Here's the basic mode of operation:
8
- 1) You define a struct, with a number of fields in it
9
- 3) Each field knows how big it is and how to produce a pattern to get it's value from the byte stream
10
- by using Ruby's "pack/unpack". Each field thus provides an unpack pattern, and patterns are ordered
11
- into a stack, starting with the first unpack pattern
12
- 4) When you parse some bytes using the struct, heres what will happen:
13
- - An unpack pattern will be compiled from all of the fields composing the struct,
14
- and it will be a single string. The string gets applied to the bytes passed to parse()
15
- - An array of unpacked values returned by unpack is then passed to the struct's consumption engine,
16
- which lets each field take as many items off the stack as it needs. A field might happily produce
17
- 4 items for unpacking and then take the same 4 items off the stack of parsed values. Or not.
18
- - A new structure gets created and for every named field it defines an attr_accessor. When consuming,
19
- the values returned by Field objects get set using the accessors (so accessors can be overridden too!)
20
- 5) When you save out the struct roughly the same happens but in reverse (readers are called per field,
21
- then it's checked whether the data can be packed and fits into the alloted number of bytes, and then
22
- one big array of values is composed and passed on to Array#pack)
23
- =end
24
-
25
- class Field
26
- attr_accessor :name, # Field name
27
- :length, # Field length in bytes, including any possible padding
28
- :pattern, # The unpack pattern that defines the field
29
- :req, # Is the field required?
30
- :desc, # Field description
31
- :rtype # To which Ruby type this has to be cast (and which type is accepted as value)
32
- alias_method :req?, :req
33
-
34
- # Hash init
35
- def initialize(opts = {})
36
- opts.each_pair {|k, v| send(k.to_s + '=', v) }
37
- end
38
-
39
- # Emit an unsigned int field
40
- def self.emit_u32(o = {})
41
- U32Field.new(o)
42
- end
43
-
44
- # Emit a short int field
45
- def self.emit_u8(o = {})
46
- U8Field.new(o)
47
- end
48
-
49
- # Emit a double int field
50
- def self.emit_u16(o = {})
51
- U16Field.new(o)
52
- end
53
-
54
- # Emit a char field
55
- def self.emit_char(o = {})
56
- opts = {:length => 1}.merge(o)
57
- CharField.new(opts)
58
- end
59
-
60
- # Emit a float field
61
- def self.emit_r32(o = {})
62
- R32Field.new(o)
63
- end
64
-
65
- # Return a cleaned value (like a null-terminated string truncated up to null)
66
- def clean(v)
67
- v
68
- end
69
-
70
- # Show a nice textual explanation of the field
71
- def explain
72
- [rtype ? ("(%s)" % rtype) : nil, desc, (req? ? "- required" : nil)].compact.join(' ')
73
- end
74
-
75
- # Return the actual values from the stack. The stack will begin on the element we need,
76
- # so the default consumption is shift. Normally all fields shift the stack
77
- # as they go, and if they contain nested substructs they will pop the stack as well
78
- def consume!(stack)
79
- clean(stack.shift)
80
- end
81
-
82
- # Check that the passed value:
83
- # a) Matches the Ruby type expected
84
- # b) Fits into the slot
85
- # c) Does not overflow
86
- # When the validation fails should raise
87
- def validate!(value)
88
- raise "#{name} value required, but got nil in #{name}".strip if value.nil? && req?
89
- raise "Value expected to be #{rtype} but was #{value.class}" if !value.nil? && rtype && !value.is_a?(rtype)
90
- end
91
-
92
- # Pack a value passed into a string
93
- def pack(value)
94
- raise "No pattern defined for #{self}" unless pattern
95
- if value.nil?
96
- [self.class.const_get(:BLANK)].pack(pattern)
97
- else
98
- [value].pack(pattern)
99
- end
100
- end
101
- end
102
-
103
- class U32Field < Field
104
- BLANK = 0xFFFFFFFF
105
- undef :length=, :pattern=
106
-
107
- def pattern
108
- "N"
109
- end
110
-
111
- def length
112
- 4
113
- end
114
-
115
- def clean(value)
116
- value == BLANK ? nil : value
117
- end
118
-
119
- # Override - might be Bignum although cast to Integer sometimes
120
- def validate!(value)
121
- raise "#{name} value required, but got nil".strip if value.nil? && req?
122
- raise "#{name} value expected to be #{rtype} but was #{value.class}" if !value.nil? && (!value.is_a?(Integer) && !value.is_a?(Bignum))
123
- raise "#{name} value #{value} overflows" if !value.nil? && (value < 0 || value >= BLANK)
124
- end
125
-
126
- end
127
-
128
- class U8Field < Field
129
- undef :length=, :pattern=
130
-
131
- BLANK = 0xFF
132
-
133
- def pattern
134
- "c"
135
- end
136
-
137
- def length
138
- 1
139
- end
140
-
141
- def rtype
142
- Integer
143
- end
144
-
145
- def clean(v)
146
- (v == BLANK || v == -1) ? nil : v
147
- end
148
-
149
- def validate!(value)
150
- super(value)
151
- raise "#{name} value #{value} out of bounds for 8 bit unsigned int".lstrip if (!value.nil? && (value < 0 || value >= BLANK))
152
- end
153
- end
154
-
155
- class Filler < Field
156
- undef :pattern=
157
- def pattern
158
- "x#{length ? length.to_i : 1}"
159
- end
160
-
161
- # Leave the stack alone since we skipped
162
- def consume(stack)
163
- nil
164
- end
165
-
166
- def pack(data)
167
- raise "This is a filler, it cannot be reconstructed from a value"
168
- end
169
- end
170
-
171
- class U16Field < Field
172
- BLANK = 0xFFFF
173
- undef :length=, :pattern=
174
-
175
- def pattern
176
- "n"
177
- end
178
-
179
- def length
180
- 2
181
- end
182
-
183
- def rtype
184
- Integer
185
- end
186
-
187
- def clean(v)
188
- v == BLANK ? nil : v
189
- end
190
-
191
- def validate!(value)
192
- super(value)
193
- raise "#{name} value #{value} out of bounds for 16bit unsigned int" if (value < 0 || value >= BLANK)
194
- end
195
- end
196
-
197
- class R32Field < Field
198
- undef :length=, :pattern=
199
- BLANK = 0xFFFFFFFF
200
-
201
- def pattern
202
- "g"
203
- end
204
-
205
- def clean(v)
206
- v.nan? ? nil : v
207
- end
208
-
209
- def length
210
- 4
211
- end
212
-
213
- def rtype
214
- Float
215
- end
216
- end
217
-
218
- class CharField < Field
219
- BLANK = "\0"
220
- undef :pattern=
221
-
222
- BLANKING_VALUES = [0x00.chr, 0xFF.chr]
223
- BLANKING_PATTERNS = BLANKING_VALUES.inject([]) do | p, char |
224
- p << /^([#{char}]+)/ << /([#{char}]+)$/mu
225
- end
226
-
227
- def pattern
228
- "A#{(length || 1).to_i}"
229
- end
230
-
231
- def clean(v)
232
- if v == BLANK
233
- nil
234
- else
235
- # Truncate everything from the null byte up
236
- upto_nulb = v.split(0x00.chr).shift
237
- (upto_nulb.nil? || upto_nulb.empty?) ? nil : upto_nulb
238
- end
239
- end
240
-
241
- def rtype
242
- String
243
- end
244
-
245
- def validate!(value)
246
- super(value)
247
- raise "#{value} overflows the #{length} bytes allocated" if !value.nil? && value.length > length
248
- end
249
-
250
- def pack(value)
251
- value.ljust(length, "\000") rescue ("\000" * length)
252
- end
253
- end
254
-
255
- # Wrapper for an array structure
256
- class ArrayField < Field
257
- attr_accessor :members
258
- undef :length=, :pattern=
259
-
260
- def length
261
- members.inject(0){|_, s| _ + s.length }
262
- end
263
-
264
- def pattern
265
- members.inject(''){|_, s| _ + s.pattern }
266
- end
267
-
268
- def consume!(stack)
269
- members.map{|m| m.consume!(stack)}
270
- end
271
-
272
- def rtype
273
- Array
274
- end
275
-
276
- def explain
277
- return 'Empty array' if (!members || members.empty?)
278
- tpl = "(Array of %d %s fields)" % [ members.length, members[0].rtype]
279
- r = (req? ? "- required" : nil)
280
- [tpl, desc, r].compact.join(' ')
281
- end
282
-
283
- def validate!(array)
284
- raise "This value would overflow, #{array.length} elements passed but only #{members.length} fit" unless array.length <= members.length
285
- raise "This value is required, but the array is empty" if req? && array.empty?
286
- array.zip(members).map do | v, m |
287
- m.validate!(v) unless (v.nil? && !m.req?)
288
- end
289
- end
290
-
291
- def pack(values)
292
- # For members that are present, get values. For members that are missing, fill with null bytes upto length.
293
- # For values that are nil, skip packing
294
- members.zip(values).map do |m, v|
295
- if !m.req? && v.nil?
296
- raise "#{m} needs to provide length" unless m.length
297
- "\377" * m.length
298
- else
299
- v.respond_to?(:pack) ? v.pack : m.pack(v)
300
- end
301
- end.join
302
- end
303
- end
304
-
305
- # Wrapper for a contained structure
306
- class InnerField < Field
307
- attr_accessor :cast
308
- undef :length=, :pattern=
309
-
310
- def length
311
- cast.length
312
- end
313
-
314
- def pattern
315
- cast.pattern
316
- end
317
-
318
- def consume!(stack)
319
- cast.consume!(stack)
320
- end
321
-
322
- def rtype
323
- cast
324
- end
325
-
326
- def validate!(value)
327
- super(value)
328
- cast.validate!(value) if cast.respond_to?(:validate!) && (!value.nil? || req?)
329
- end
330
-
331
- def pack(value)
332
- cast.pack(value)
333
- end
334
- end
335
-
336
- # Base class for a struct. Could also be implemented as a module actually
337
- class Dict
338
- DEF_OPTS = { :req => false, :desc => nil }
339
-
340
- class << self
341
-
342
- # Get the array of fields defined in this struct
343
- def fields
344
- @fields ||= []
345
- end
346
-
347
- # Validate a passed instance
348
- def validate!(instance)
349
- fields.each do | f |
350
- f.validate!(instance.send(f.name)) if f.name
351
- end
352
- end
353
-
354
- # Define a 4-byte unsigned integer
355
- def u32(name, *extras)
356
- count, opts = count_and_opts_from(extras)
357
- attr_accessor name
358
- fields << Field.emit_u32( {:name => name }.merge(opts) )
359
- end
360
-
361
- # Define a double-width unsigned integer
362
- def u16(name, *extras)
363
- count, opts = count_and_opts_from(extras)
364
- attr_accessor name
365
- fields << Field.emit_u16( {:name => name }.merge(opts) )
366
- end
367
-
368
-
369
- # Define a small unsigned integer
370
- def u8(name, *extras)
371
- count, opts = count_and_opts_from(extras)
372
- attr_accessor name
373
- fields << Field.emit_u8( {:name => name }.merge(opts) )
374
- end
375
-
376
- # Define a real number
377
- def r32(name, *extras)
378
- count, opts = count_and_opts_from(extras)
379
- attr_accessor name
380
- fields << Field.emit_r32( {:name => name}.merge(opts) )
381
- end
382
-
383
- # Define an array of values
384
- def array(name, mapped_to, *extras)
385
- count, opts = count_and_opts_from(extras)
386
- attr_accessor name
387
-
388
- a = ArrayField.new({:name => name}.merge(opts))
389
- a.members = if mapped_to.is_a?(Class) # Array of structs
390
- [InnerField.new(:cast => mapped_to)] * count
391
- else
392
- [Field.send("emit_#{mapped_to}")] * count
393
- end
394
- yield a.members if block_given?
395
- fields << a
396
- end
397
-
398
- # Define a nested struct
399
- def inner(name, mapped_to, *extras)
400
- count, opts = count_and_opts_from(extras)
401
- attr_accessor name
402
- fields << InnerField.new({:name => name, :cast => mapped_to}.merge(opts))
403
- end
404
-
405
- # Define a char field
406
- def char(name, *extras)
407
- count, opts = count_and_opts_from(extras)
408
- attr_accessor name
409
- fields << Field.emit_char( {:name => name, :length => count}.merge(opts) )
410
- end
411
-
412
- # Get the pattern that will be used to unpack this structure and all of it's descendants
413
- def pattern
414
- fields.map{|f| f.pattern }.join
415
- end
416
-
417
- # Get the pattern that will be used to unpack this structure and all of it's descendants
418
- # from a buffer with pieces in little-endian byte order
419
- def pattern_le
420
- pattern.tr("gN", "eV")
421
- end
422
-
423
- # How many bytes are needed to complete this structure
424
- def length
425
- fields.inject(0){|_, s| _ + s.length }
426
- end
427
-
428
- # Consume a stack of unpacked values, letting each field decide how many to consume
429
- def consume!(stack_of_unpacked_values)
430
- new_item = new
431
- @fields.each do | field |
432
- new_item.send("#{field.name}=", field.consume!(stack_of_unpacked_values)) unless field.name.nil?
433
- end
434
- new_item
435
- end
436
-
437
- # Apply this structure to data in the string, returning an instance of this structure with fields completed
438
- def apply!(string)
439
- consume!(string.unpack(pattern))
440
- end
441
-
442
- # Apply this structure to data in the string, returning an instance of this structure with fields completed
443
- # assume little-endian fields
444
- def apply_le!(string)
445
- consume!(string.unpack(pattern_le))
446
- end
447
-
448
- # Get a class that would parse just the same, preserving only the fields passed in the array. This speeds
449
- # up parsing because we only extract and conform the fields that we need
450
- def only(*field_names)
451
- distillate = fields.inject([]) do | m, f |
452
- if field_names.include?(f.name) # preserve
453
- m.push(f)
454
- else # create filler
455
- unless m[-1].is_a?(Filler)
456
- m.push(Filler.new(:length => f.length))
457
- else
458
- m[-1].length += f.length
459
- end
460
- m
461
- end
462
- end
463
-
464
- anon = Class.new(self)
465
- anon.fields.replace(distillate)
466
- only_items = distillate.map{|n| n.name }
467
-
468
- anon
469
- end
470
-
471
- # Get an opaque struct based on this one, that will consume exactly as many bytes as this
472
- # structure would occupy, but discard them instead
473
- def filler
474
- only([])
475
- end
476
-
477
- # Pack the instance of this struct
478
- def pack(instance, buffer = nil)
479
-
480
- # Preallocate a buffer just as big as me since we want everything to remain at fixed offsets
481
- buffer ||= ("\000" * length)
482
-
483
- # If the instance is nil return pure padding
484
- return buffer if instance.nil?
485
-
486
- # Now for the important stuff. For each field that we have, replace a piece at offsets in the buffer
487
- # with the packed results, skipping fillers
488
- fields.each_with_index do | f, i |
489
-
490
- # Skip blanking, we just dont touch it. TODO - test!
491
- next if f.is_a?(Filler)
492
-
493
- # Where should we put that value?
494
- offset = fields[0...i].inject(0){|_, s| _ + s.length }
495
-
496
- val = instance.send(f.name)
497
-
498
- # Validate the passed value using the format the field supports
499
- f.validate!(val)
500
-
501
- packed = f.pack(val)
502
-
503
- # Signal offset violation
504
- raise "Improper length for #{f.name} - packed #{packed.length} bytes but #{f.length} is required to fill the slot" if packed.length != f.length
505
-
506
- buffer[offset...(offset+f.length)] = packed
507
- end
508
- raise "Resulting buffer not the same length, expected #{length} bytes but compued #{buffer.length}" if buffer.length != length
509
- buffer
510
- end
511
-
512
- private
513
-
514
- # extract_options! on a diet
515
- def count_and_opts_from(args)
516
- options, count = (args[-1].is_a?(Hash) ? DEF_OPTS.merge(args.pop) : DEF_OPTS), (args.shift || 1)
517
- [count, options]
518
- end
519
- end # End class methods
520
-
521
- def []=(field, value)
522
- send("#{field}=", value)
523
- end
524
-
525
- def [](field)
526
- send(field)
527
- end
528
- end
529
-
530
- #:startdoc:
531
- end