depix 1.1.6 → 2.0.0

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.
@@ -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