File Coverage

blib/lib/Parse/Binary.pm
Criterion Covered Total %
statement 87 639 13.6
branch 0 222 0.0
condition 0 84 0.0
subroutine 29 126 23.0
pod 0 63 0.0
total 116 1134 10.2


line stmt bran cond sub pod time code
1             package Parse::Binary;
2             $Parse::Binary::VERSION = '0.11';
3              
4 1     1   24466 use 5.005;
  1         4  
  1         44  
5 1     1   1779 use bytes;
  1         14  
  1         7  
6 1     1   34 use strict;
  1         8  
  1         38  
7 1     1   1474 use integer;
  1         10  
  1         5  
8 1     1   910 use Parse::Binary::FixedFormat;
  1         3  
  1         55  
9              
10             =head1 NAME
11              
12             Parse::Binary - Unpack binary data structures into object hierarchies
13              
14             =head1 VERSION
15              
16             This document describes version 0.11 of Parse::Binary, released
17             January 25, 2009.
18              
19             =head1 SYNOPSIS
20              
21             # This class represents a Win32 F<.ico> file:
22              
23             package IconFile;
24             use base 'Parse::Binary';
25             use constant FORMAT => (
26             Magic => 'a2',
27             Type => 'v',
28             Count => 'v',
29             'Icon' => [ 'a16', '{$Count}', 1 ],
30             Data => 'a*',
31             );
32              
33             # An individual icon resource:
34              
35             package Icon;
36             use base 'Parse::Binary';
37             use constant FORMAT => (
38             Width => 'C',
39             Height => 'C',
40             ColorCount => 'C',
41             Reserved => 'C',
42             Planes => 'v',
43             BitCount => 'v',
44             ImageSize => 'V',
45             ImageOffset => 'v',
46             );
47             sub Data {
48             my ($self) = @_;
49             return $self->parent->substr($self->ImageOffset, $self->ImageSize);
50             }
51              
52             # Simple F<.ico> file dumper that uses them:
53              
54             use IconFile;
55             my $icon_file = IconFile->new('input.ico');
56             foreach my $icon ($icon_file->members) {
57             print "Dimension: ", $icon->Width, "x", $icon->Height, $/;
58             print "Colors: ", 2 ** $icon->BitCount, $/;
59             print "Image Size: ", $icon->ImageSize, " bytes", $/;
60             print "Actual Size: ", length($icon->Data), " bytes", $/, $/;
61             }
62             $icon_file->write('output.ico'); # save as another .ico file
63              
64             =head1 DESCRIPTION
65              
66             This module makes parsing binary data structures much easier, by serving
67             as a base class for classes that represents the binary data, which may
68             contain objects of other classes to represent parts of itself.
69              
70             Documentation is unfortunately a bit lacking at this moment. Please read
71             the tests and source code of L and L for examples
72             of using this module.
73              
74             =cut
75              
76 1         112 use constant PROPERTIES => qw(
77             %struct $filename $size $parent @siblings %children
78             $output $lazy $iterator $iterated
79 1     1   7 );
  1         2  
80 1     1   7 use constant ENCODED_FIELDS => ( 'Data' );
  1         2  
  1         128  
81 1     1   6 use constant FORMAT => ( Data => 'a*' );
  1         2  
  1         56  
82 1     1   13 use constant SUBFORMAT => ();
  1         2  
  1         44  
83 1     1   5 use constant DEFAULT_ARGS => ();
  1         1  
  1         56  
84 1     1   5 use constant DELEGATE_SUBS => ();
  1         1  
  1         49  
85 1     1   5 use constant DISPATCH_TABLE => ();
  1         2  
  1         57  
86              
87 1     1   6 use constant DISPATCH_FIELD => undef;
  1         1  
  1         48  
88 1     1   13 use constant BASE_CLASS => undef;
  1         2  
  1         42  
89 1     1   5 use constant ENCODING => undef;
  1         2  
  1         48  
90 1     1   5 use constant PADDING => undef;
  1         2  
  1         221  
91              
92             unless (eval { require Scalar::Util; 1 }) {
93             *Scalar::Util::weaken = sub { 1 };
94             *Scalar::Util::blessed = sub { UNIVERSAL::can($_[0], 'can') };
95             }
96              
97             ### Constructors ###
98              
99             sub new {
100 0     0 0   my ($self, $input, $attr) = @_;
101              
102 1     1   6 no strict 'refs';
  1         2  
  1         229  
103 0           my $class = $self->class;
104 0 0         $class->init unless ${"$class\::init_done"};
  0            
105              
106 0   0       $attr ||= {};
107 0 0 0       $attr->{filename} ||= $input unless ref $input;
108              
109 0           my $obj = $class->spawn;
110 0           %$obj = (%$obj, %$attr);
111              
112 0           my $data = $obj->read_data($input);
113 0           $obj->load($data, $attr);
114              
115 0 0         if ($obj->{lazy}) {
    0          
116 0           $obj->{lazy} = $obj;
117             }
118             elsif (!$obj->{iterator}) {
119 0           $obj->make_members;
120             }
121              
122 0           return $obj;
123             }
124              
125             sub dispatch_field {
126 0     0 0   return undef;
127             }
128              
129 1     1   6 use vars qw(%HasMembers %DefaultArgs);
  1         2  
  1         82  
130 1     1   6 use vars qw(%Fields %MemberFields %MemberClass %Packer %Parser %FieldPackFormat);
  1         3  
  1         160  
131 1     1   8 use vars qw(%DispatchField %DispatchTable);
  1         1  
  1         136  
132              
133             sub init {
134 1     1   7 no strict 'refs';
  1         2  
  1         152  
135 0 0   0 0   return if ${"$_[0]\::init_done"};
  0            
136              
137 0           my $class = shift;
138              
139 0 0   0     *{"$class\::class"} = sub { ref($_[0]) || $_[0] };
  0            
  0            
140 0           *{"$class\::is_type"} = \&is_type;
  0            
141              
142 0           foreach my $item ($class->PROPERTIES) {
143 1     1   6 no strict 'refs';
  1         1  
  1         6661  
144 0           my ($sigil, $name) = split(//, $item, 2);
145 0           *{"$class\::$name"} =
146 0     0     ($sigil eq '$') ? sub { $_[0]{$name} } :
147 0 0 0 0     ($sigil eq '@') ? sub { wantarray ? @{$_[0]{$name}||=[]} : ($_[0]{$name}||=[]) } :
  0   0        
148 0   0 0     ($sigil eq '%') ? sub { $_[0]{$name}||={} } :
149 0 0         die "Unknown sigil: $sigil";
    0          
    0          
150 0           *{"$class\::set_$name"} =
151 0     0     ($sigil eq '$') ? sub { $_[0]->{$name} = $_[1] } :
152 0 0 0 0     ($sigil eq '@') ? sub { @{$_[0]->{$name}||=$_[1]||[]} = @{$_[1]||[]} } :
  0   0        
  0            
153 0 0 0 0     ($sigil eq '%') ? sub { %{$_[0]->{$name}||=$_[1]||{}} = %{$_[1]||{}} } :
  0   0        
  0            
154 0 0         die "Unknown sigil: $sigil";
    0          
    0          
155             }
156              
157 0           my @args = $class->default_args;
158 0           *{"$class\::default_args"} = \@args;
  0            
159 0     0     *{"$class\::default_args"} = sub { @args };
  0            
  0            
160 0           my $delegate_subs = $class->delegate_subs;
161 0 0         if (defined(&{"$class\::DELEGATE_SUBS"})) {
  0            
162 0           $delegate_subs = { $class->DELEGATE_SUBS };
163             }
164 0     0     *{"$class\::delegate_subs"} = sub { $delegate_subs };
  0            
  0            
165 0           while (my ($subclass, $methods) = each %$delegate_subs) {
166 0 0         $methods = [ $methods ] unless ref $methods;
167 0           foreach my $method (grep length, @$methods) {
168 0           *{"$class\::$method"} = sub {
169 0     0     goto &{$_[0]->require_class($subclass)->can($method)};
  0            
170 0           };
171             }
172             }
173 0           my $dispatch_table = $class->dispatch_table;
174 0 0         if (defined(&{"$class\::DISPATCH_TABLE"})) {
  0            
175 0           $dispatch_table = { $class->DISPATCH_TABLE };
176             }
177 0           $DispatchTable{$class} = $dispatch_table;
178 0     0     *{"$class\::dispatch_table"} = sub { $dispatch_table };
  0            
  0            
179              
180 0           my $dispatch_field = undef;
181 0 0         if (defined(&{"$class\::DISPATCH_FIELD"})) {
  0            
182 0           $dispatch_field = $class->DISPATCH_FIELD;
183             }
184 0           $DispatchField{$class} = $dispatch_field;
185 0     0     *{"$class\::dispatch_field"} = sub { $dispatch_field };
  0            
  0            
186              
187 0           my @format = $class->format_list;
188 0 0         if (my @subformat = $class->subformat_list) {
189 0           my @new_format;
190 0           while (my ($field, $format) = splice(@format, 0, 2)) {
191 0 0         if ($field eq 'Data') {
192 0           push @new_format, @subformat;
193             }
194             else {
195 0           push @new_format, ($field => $format);
196             }
197             }
198 0           @format = @new_format;
199             }
200 0           my @format_list = @format;
201 0     0     *{"$class\::format_list"} = sub { @format_list };
  0            
  0            
202              
203 0           my (@fields, @formats, @pack_formats, $underscore_count);
204 0           my (%field_format, %field_pack_format);
205 0           my (%field_parser, %field_packer, %field_length);
206 0           my (@member_fields, %member_class);
207 0           while (my ($field, $format) = splice(@format, 0, 2)) {
208 0 0         if ($field eq '_') {
209             # "we don't care" fields
210 0           $underscore_count++;
211 0           $field = "_${underscore_count}_$class";
212 0           $field =~ s/:/_/g;
213             }
214              
215 0 0         if (ref $format) {
216 0           $member_class{$field} = $class->classname($field);
217 0           $field =~ s/:/_/g;
218 0           $member_class{$field} = $class->classname($field);
219 0           $class->require($member_class{$field});
220 0           push @member_fields, $field;
221             }
222             else {
223 0           $format = [ $format ];
224             }
225              
226 0           push @fields, $field;
227              
228 0           my $string = join(':', $field, @$format);
229 0           $field_format{$field} = [ @$format ];
230 0 0         if (!grep /\{/, @$format) {
231 0           $field_length{$field} = length(pack($format->[0], 0));
232 0           $field_parser{$field} = Parse::Binary::FixedFormat->new( [ $string ] );
233             }
234 0           push @formats, $string;
235              
236 0           s/\s*X\s*//g for @$format;
237 0           my $pack_string = join(':', $field, @$format);
238 0           $field_pack_format{$field} = [ @$format ];
239 0           $field_packer{$field} = Parse::Binary::FixedFormat->new( [ $pack_string ] );
240 0           push @pack_formats, $pack_string;
241             }
242              
243 0           my $parser = $class->make_formatter(@formats);
244 0           my $packer = $class->make_formatter(@pack_formats);
245              
246 0           $Packer{$class} = $packer;
247 0           $Parser{$class} = $parser;
248 0           $Fields{$class} = \@fields;
249 0 0         $HasMembers{$class} = @member_fields ? 1 : 0;
250 0           $DefaultArgs{$class} = \@args;
251 0           $MemberClass{$class} = \%member_class;
252 0           $MemberFields{$class} = \@member_fields;
253 0 0         $FieldPackFormat{$class} = { map { ref($_) ? $_->[0] : $_ } %field_pack_format };
  0            
254              
255 0           *{"$class\::fields"} = \@fields;
  0            
256 0           *{"$class\::member_fields"} = \@member_fields;
  0            
257 0 0   0     *{"$class\::has_members"} = @member_fields ? sub { 1 } : sub { 0 };
  0            
  0            
  0            
258 0     0     *{"$class\::fields"} = sub { @fields };
  0            
  0            
259 0     0     *{"$class\::formats"} = sub { @formats };
  0            
  0            
260 0     0     *{"$class\::member_fields"} = sub { @member_fields };
  0            
  0            
261 0     0     *{"$class\::member_class"} = sub { $member_class{$_[1]} };
  0            
  0            
262 0     0     *{"$class\::pack_formats"} = sub { @pack_formats };
  0            
  0            
263 0     0     *{"$class\::field_format"} = sub { $field_format{$_[1]}[0] };
  0            
  0            
264 0     0     *{"$class\::field_pack_format"} = sub { $field_pack_format{$_[1]}[0] };
  0            
  0            
265 0     0     *{"$class\::field_length"} = sub { $field_length{$_[1]} };
  0            
  0            
266              
267 0     0     *{"$class\::parser"} = sub { $parser };
  0            
  0            
268 0     0     *{"$class\::packer"} = sub { $packer };
  0            
  0            
269 0           *{"$class\::field_parser"} = sub {
270 0     0     my ($self, $field) = @_;
271 0 0         $field_parser{$field} || do {
272 0           Parse::Binary::FixedFormat->new( [
273             $self->eval_format(
274             $self->{struct},
275 0           join(':', $field, @{$field_format{$field}}),
276             ),
277             ] );
278             };
279 0           };
280              
281 0     0     *{"$class\::field_packer"} = sub { $field_packer{$_[1]} };
  0            
  0            
282 0     0     *{"$class\::has_field"} = sub { $field_packer{$_[1]} };
  0            
  0            
283              
284 0           my %enc_fields = map { ($_ => 1) } $class->ENCODED_FIELDS;
  0            
285              
286 0           foreach my $field (@fields) {
287 0 0         next if defined &{"$class\::$field"};
  0            
288              
289 0 0 0       if ($enc_fields{$field} and my $encoding = $class->ENCODING) {
290 0           require Encode;
291              
292 0           *{"$class\::$field"} = sub {
293 0     0     my ($self) = @_;
294 0           return Encode::decode($encoding => $self->{struct}{$field});
295 0           };
296              
297 0           *{"$class\::Set$field"} = sub {
298 0     0     my ($self, $data) = @_;
299 0           $self->{struct}{$field} = Encode::encode($encoding => $data);
300 0           };
301 0           next;
302             }
303              
304 0     0     *{"$class\::$field"} = sub { $_[0]->{struct}{$field} };
  0            
  0            
305 0     0     *{"$class\::Set$field"} = sub { $_[0]->{struct}{$field} = $_[1] };
  0            
  0            
306             }
307              
308 0           ${"$class\::init_done"} = 1;
  0            
309             }
310              
311             sub initialize {
312 0     0 0   return 1;
313             }
314              
315             ### Miscellanous ###
316              
317             sub field {
318 0     0 0   my ($self, $field) = @_;
319 0           return $self->{struct}{$field};
320             }
321              
322             sub set_field {
323 0     0 0   my ($self, $field, $data) = @_;
324 0           $self->{struct}{$field} = $data;
325             }
326              
327             sub classname {
328 0     0 0   my ($self, $class) = @_;
329 0 0         return undef unless $class;
330              
331 0           $class =~ s/__/::/g;
332              
333 0 0         my $base_class = $self->BASE_CLASS or return $class;
334 0 0         return $base_class if $class eq '::BASE::';
335              
336 0           return "$base_class\::$class";
337             }
338              
339             sub member_fields {
340 0     0 0   return ();
341             }
342              
343             sub dispatch_class {
344 0     0 0   my ($self, $field) = @_;
345 0           my $table = $DispatchTable{ref $self};
346 0 0         my $class = exists($table->{$field}) ? $table->{$field} : $table->{'*'};
347              
348 0 0         $class = &$class($self, $field) if UNIVERSAL::isa($class, 'CODE');
349 0 0         defined $class or return;
350              
351 0 0         if (my $members = $self->{parent}{callback_members}) {
352 0 0         return unless $members->{$class};
353             }
354 0 0         my $subclass = $self->classname($class) or return;
355 0 0         return if $subclass eq $class;
356 0           return $subclass;
357             }
358              
359             sub require {
360 0     0 0   my ($class, $module) = @_;
361 0 0         return unless defined $module;
362              
363 0           my $file = "$module.pm";
364 0           $file =~ s{::}{/}g;
365              
366 0 0         return $module if (eval { require $file; 1 });
  0            
  0            
367 0 0         die $@ unless $@ =~ /^Can't locate /;
368 0           return;
369             }
370              
371             sub require_class {
372 0     0 0   my ($class, $subclass) = @_;
373 0           return $class->require($class->classname($subclass));
374             }
375              
376             sub format_list {
377 0     0 0   my ($self) = @_;
378 0           return $self->FORMAT;
379             }
380              
381             sub subformat_list {
382 0     0 0   my ($self) = @_;
383 0 0         $self->SUBFORMAT ? $self->SUBFORMAT : ();
384             }
385              
386             sub default_args {
387 0     0 0   my ($self) = @_;
388 0 0         $self->DEFAULT_ARGS ? $self->DEFAULT_ARGS : ();
389             }
390              
391             sub dispatch_table {
392 0     0 0   my ($self) = @_;
393 0 0         $self->DISPATCH_TABLE ? { $self->DISPATCH_TABLE } : {};
394             }
395              
396             sub delegate_subs {
397 0     0 0   my ($self) = @_;
398 0 0         $self->DELEGATE_SUBS ? { $self->DELEGATE_SUBS } : {};
399             }
400              
401             sub class {
402 0     0 0   my ($self) = @_;
403 0   0       return(ref($self) || $self);
404             }
405              
406             sub make_formatter {
407 0     0 0   my ($self, @formats) = @_;
408 0           return Parse::Binary::FixedFormat->new( $self->make_format(@formats) );
409             }
410              
411             sub make_format {
412 0     0 0   my ($self, @formats) = @_;
413 0 0         return \@formats unless grep /\{/, @formats;
414              
415 0           my @prefix;
416 0           foreach my $format (@formats) {
417 0 0         last if $format =~ /\{/;
418 0           push @prefix, $format;
419             }
420             return {
421 0     0     Chooser => sub { $self->chooser(@_) },
422 0           Formats => [ \@prefix, \@formats ],
423             };
424             }
425              
426             sub chooser {
427 0     0 0   my ($self, $rec, $obj, $mode) = @_;
428 0           my $idx = @{$obj->{Layouts}};
  0            
429 0           my @format = $self->eval_format($rec, @{$obj->{Formats}[1]});
  0            
430 0           $obj->{Layouts}[$idx] = $self->make_formatter(@format);
431 0           return $idx;
432             }
433              
434             sub eval_format {
435 0     0 0   my ($self, $rec, @format) = @_;
436 0           foreach my $key (sort keys %$rec) {
437 0           s/\$$key\b/$rec->{$key}/ for @format;
438             }
439 0   0       !/\$/ and s/\{(.*?)\}/$1/eeg for @format;
  0            
440 0 0         die $@ if $@;
441 0           return @format;
442             }
443              
444             sub padding {
445 0     0 0   return '';
446             }
447              
448             sub load_struct {
449 0     0 0   my ($self, $data) = @_;
450 0           $self->{struct} = $Parser{ref $self}->unformat($$data . $self->padding, $self->{lazy}, $self);
451             }
452              
453             sub load_size {
454 0     0 0   my ($self, $data) = @_;
455 0           $self->{size} = length($$data);
456 0           return 1;
457             }
458              
459             sub lazy_load {
460 0     0 0   my ($self) = @_;
461 0 0         ref(my $sub = $self->{lazy}) or return;
462 0           $self->{lazy} = 1;
463 0 0         $self->make_members unless $self->{iterator};
464             }
465              
466             my %DispatchClass;
467             sub load {
468 0     0 0   my ($self, $data, $attr) = @_;
469 0 0         return $self unless defined $data;
470              
471 1     1   57 no strict 'refs';
  1         2  
  1         216  
472 0   0       my $class = ref($self) || $self;
473 0 0         $class->init unless ${"$class\::init_done"};
  0            
474              
475 0           $self->load_struct($data);
476 0           $self->load_size($data);
477              
478 0 0         if (my $field = $DispatchField{$class}) {
479 0 0 0       if (
480             my $subclass = $DispatchClass{$class}{ $self->{struct}{$field} }
481             ||= $self->dispatch_class( $self->{struct}{$field})
482             ) {
483 0           $self->require($subclass);
484 0           bless($self, $subclass);
485 0           $self->load($data, $attr);
486             }
487             }
488              
489 0           return $self;
490             }
491              
492             my (%classname, %fill_cache);
493             sub spawn {
494 0     0 0   my ($self, %args) = @_;
495 0   0       my $class = ref($self) || $self;
496              
497 1     1   6 no strict 'refs';
  1         3  
  1         1813  
498              
499 0 0         if (my $subclass = delete($args{Class})) {
500 0   0       $class = $classname{$subclass} ||= do {
501 0           my $name = $self->classname($subclass);
502 0           $self->require($name);
503 0           $name->init;
504 0           $name;
505             };
506             }
507              
508             bless({
509 0           struct => {
510             %args,
511 0   0       @{ $DefaultArgs{$class} },
512 0           %{ $fill_cache{$class} ||= $class->fill_in },
513             },
514             }, $class);
515             }
516              
517             sub fill_in {
518 0     0 0   my $class = shift;
519 0           my $entries = {};
520              
521 0           foreach my $super_class ($class->superclasses) {
522 0 0         my $field = $DispatchField{$super_class} or next;
523 0 0         my $table = $DispatchTable{$super_class} or next;
524 0           foreach my $code (reverse sort keys %$table) {
525 0 0         $class->is_type($table->{$code}) or next;
526 0           $entries->{$field} = $code;
527 0           last;
528             }
529             }
530              
531 0           return $entries;
532             }
533              
534             sub spawn_sibling {
535 0     0 0   my ($self, %args) = @_;
536 0 0         my $parent = $self->{parent} or die "$self has no parent";
537              
538 0           my $obj = $self->spawn(%args);
539 0           @{$obj}{qw( lazy parent output siblings )} =
  0            
540 0           @{$self}{qw( lazy parent output siblings )};
541 0           $obj->{size} = length($obj->dump);
542 0           $obj->refresh_parent;
543 0           $obj->initialize;
544              
545 0           return $obj;
546             }
547              
548             sub sibling_index {
549 0     0 0   my ($self, $obj) = @_;
550 0   0       $obj ||= $self;
551              
552 0           my @siblings = @{$self->{siblings}};
  0            
553 0   0       foreach my $index (($obj->{index}||0) .. $#siblings) {
554 0 0         return $index if $obj == $siblings[$index];
555             }
556              
557 0           return undef;
558             }
559              
560             sub gone {
561 0     0 0   my ($self, $obj) = @_;
562 0   0       $self->{parent}{struct}{Data} .= ($obj || $self)->dump;
563             }
564              
565             sub prepend_obj {
566 0     0 0   my ($self, %args) = @_;
567 0 0         if ($self->{lazy}) {
568 0           my $obj = $self->spawn(%args);
569 0           $self->gone($obj);
570 0           return;
571             }
572 0           my $obj = $self->spawn_sibling(%args);
573 0           my $siblings = $self->{siblings};
574 0 0         my $index = $self->{index} ? $self->{index}++ : $self->sibling_index;
575 0           $obj->{index} = $index;
576              
577 0           splice(@$siblings, $index, 0, $obj);
578 0           return $obj;
579             }
580              
581             sub append_obj {
582 0     0 0   my ($self, %args) = @_;
583 0           my $obj = $self->spawn_sibling(%args);
584              
585 0 0         @{$self->{siblings}} = (
  0            
586 0           map { $_, (($_ == $self) ? $obj : ()) } @{$self->{siblings}}
  0            
587             );
588 0           return $obj;
589             }
590              
591             sub remove {
592 0     0 0   my ($self, %args) = @_;
593 0           my $siblings = $self->{siblings};
594 0           splice(@$siblings, $self->sibling_index, 1, undef);
595              
596 0           Scalar::Util::weaken($self->{parent});
597 0           Scalar::Util::weaken($self);
598             }
599              
600             sub read_data {
601 0     0 0   my ($self, $data) = @_;
602 0 0         return undef unless defined $data;
603 0 0         return \($data->dump) if UNIVERSAL::can($data, 'dump');
604 0 0         return $data if UNIVERSAL::isa($data, 'SCALAR');
605 0           return \($self->read_file($data));
606             }
607              
608             sub read_file {
609 0     0 0   my ($self, $file) = @_;
610              
611 0           local *FH; local $/;
  0            
612 0 0         open FH, "< $file" or die "Cannot open $file for reading: $!";
613 0           binmode(FH);
614              
615 0           return scalar ;
616             }
617              
618             sub make_members {
619 0     0 0   my ($self) = @_;
620              
621 0 0         $HasMembers{ref $self} or return;
622 0           %{$self->{children}} = ();
  0            
623              
624 0           foreach my $field (@{$MemberFields{ref $self}}) {
  0            
625 0           my ($format) = $self->eval_format(
626             $self->{struct},
627             $FieldPackFormat{ref $self}{$field},
628             );
629              
630 0           my $members = [ map {
631 0           $self->new_member( $field, \pack($format, @$_) )
632             } $self->validate_memberdata($field) ];
633 0           $self->set_field_children( $field, $members );
634             }
635             }
636              
637             sub set_members {
638 0     0 0   my ($self, $field, $members) = @_;
639 0           $field =~ s/:/_/g;
640 0           $self->set_field_children(
641             $field,
642 0           [ map { $self->new_member( $field, $_ ) } @$members ],
643             );
644             }
645              
646             sub set_field_children {
647 0     0 0   my ($self, $field, $data) = @_;
648 0           my $children = $self->field_children($field);
649 0           @$children = @$data;
650 0           return $children;
651             }
652              
653             sub field_children {
654 0     0 0   my ($self, $field) = @_;
655 0   0       my $children = ($self->{children}{$field} ||= []);
656             # $_->lazy_load for @$children;
657 0 0         return(wantarray ? @$children : $children);
658             }
659              
660             sub validate_memberdata {
661 0     0 0   my ($self, $field) = @_;
662 0 0         return @{$self->{struct}{$field}||[]};
  0            
663             }
664              
665             sub first_member {
666 0     0 0   my ($self, $type) = @_;
667 0           $self->lazy_load;
668              
669 0 0         return undef unless $HasMembers{ref $self};
670              
671 1     1   9 no strict 'refs';
  1         2  
  1         540  
672 0           foreach my $field (@{$MemberFields{ref $self}}) {
  0            
673 0           foreach my $member ($self->field_children($field)) {
674 0 0         return $member if $member->is_type($type);
675             }
676             }
677 0           return undef;
678             }
679              
680             sub next_member {
681 0     0 0   my ($self, $type) = @_;
682 0 0         return undef unless $HasMembers{ref $self};
683              
684 0 0 0       if ($self->{lazy} and !$self->{iterated}) {
685 0 0         if (ref($self->{lazy})) {
686 0           %{$self->{children}} = ();
  0            
687 0           $self->{iterator} = $self->make_next_member;
688 0           $self->lazy_load;
689             }
690              
691 0           while (my $member = &{$self->{iterator}}) {
  0            
692 0 0         return $member if $member->is_type($type);
693             }
694 0           $self->{iterated} = 1;
695 0           return;
696             }
697              
698 0   0       $self->{_next_member}{$type} ||= $self->members($type);
699              
700 0 0         shift(@{$self->{_next_member}{$type}})
  0            
701             || undef($self->{_next_member}{$type});
702             }
703              
704             sub make_next_member {
705 0     0 0   my $self = shift;
706 0           my $class = ref($self);
707 0           my ($field_idx, $item_idx, $format) = (0, 0, undef);
708 0           my @fields = @{$MemberFields{$class}};
  0            
709 0           my $struct = $self->{struct};
710 0           my $formats = $FieldPackFormat{$class};
711              
712 0 0         sub { LOOP: {
713 0     0     my $field = $fields[$field_idx] or return;
714              
715 0           my $items = $struct->{$field};
716 0 0         if ($item_idx > $#$items) {
717 0           $field_idx++;
718 0           $item_idx = 0;
719 0           undef $format;
720 0           redo;
721             }
722              
723 0   0       $format ||= ($self->eval_format( $struct, $formats->{$field} ))[0];
724              
725 0           my $item = $items->[$item_idx++];
726 0 0         $item = $item->($self, $items) if UNIVERSAL::isa($item, 'CODE');
727 0 0         $self->valid_memberdata($item) or redo;
728              
729 0           my $member = $self->new_member( $field, \pack($format, @$item) );
730 0           $member->{index} = (push @{$self->{children}{$field}}, $member) - 1;
  0            
731 0           return $member;
732 0           } };
733             }
734              
735             sub members {
736 0     0 0   my ($self, $type) = @_;
737 0           $self->lazy_load;
738              
739 1     1   6 no strict 'refs';
  1         3  
  1         732  
740 0 0         my @members = map {
741 0           grep { $type ? $_->is_type($type) : 1 } $self->field_children($_)
  0            
742 0           } @{$MemberFields{ref $self}};
743 0 0         wantarray ? @members : \@members;
744             }
745              
746             sub members_recursive {
747 0     0 0   my ($self, $type) = @_;
748 0           my @members = (
749             ( $self->is_type($type) ? $self : () ),
750 0 0         map { $_->members_recursive($type) } $self->members
751             );
752 0 0         wantarray ? @members : \@members;
753             }
754              
755             sub new_member {
756 0     0 0   my ($self, $field, $data) = @_;
757 0           my $obj = $MemberClass{ref $self}{$field}->new(
758             $data, { lazy => $self->{lazy}, parent => $self }
759             );
760              
761 0           $obj->{output} = $self->{output};
762 0   0       $obj->{siblings} = $self->{children}{$field}||=[];
763 0           $obj->initialize;
764              
765 0           return $obj;
766             }
767              
768             sub valid_memberdata {
769 0     0 0   length($_[-1][0])
770             }
771              
772             sub dump_members {
773 0     0 0   my ($self) = @_;
774 0           return $Packer{ref $self}->format($self->{struct});
775             }
776              
777             sub dump {
778 0     0 0   my ($self) = @_;
779 0 0         return $self->dump_members if $HasMembers{ref $self};
780 0           return $Packer{ref $self}->format($self->{struct});
781             }
782              
783             sub write {
784 0     0 0   my ($self, $file) = @_;
785              
786 0 0 0       if (ref($file)) {
    0          
787 0           $$file = $self->dump;
788             }
789             elsif (!defined($file) and my $fh = $self->{output}) {
790 0           print $fh $self->dump;
791             }
792             else {
793 0 0         $file = $self->{filename} unless defined $file;
794 0 0         $self->write_file($file, $self->dump) if defined $file;
795             }
796             }
797              
798             sub write_file {
799 0     0 0   my ($self, $file, $data) = @_;
800 0           local *FH;
801 0 0         open FH, "> $file" or die "Cannot open $file for writing: $!";
802 0           binmode(FH);
803 0           print FH $data;
804             };
805              
806             sub superclasses {
807 0     0 0   my ($self) = @_;
808 0           my $class = $self->class;
809              
810 1     1   8 no strict 'refs';
  1         3  
  1         371  
811 0           return @{"$class\::ISA"};
  0            
812             }
813              
814             my %type_cache;
815             sub is_type {
816 0     0 0   my ($self, $type) = @_;
817 0 0         return 1 unless defined $type;
818              
819 0   0       my $class = ref($self) || $self;
820              
821 0 0         if (exists $type_cache{$class}{$type}) {
822 0           return $type_cache{$class}{$type};
823             }
824              
825 0           $type_cache{$class}{$type} = 1;
826              
827              
828 0           $type =~ s/__/::/g;
829 0           $type =~ s/[^\w:]//g;
830 0 0         return 1 if ($class =~ /::$type$/);
831              
832 1     1   50 no strict 'refs';
  1         3  
  1         870  
833 0           foreach my $super_class ($class->superclasses) {
834 0 0         return 1 if $super_class->is_type($type);
835             };
836              
837 0           $type_cache{$class}{$type} = 0;
838             }
839              
840             sub refresh {
841 0     0 0   my ($self) = @_;
842              
843 0           foreach my $field (@{$MemberFields{ref $self}}) {
  0            
844 0           my $parser = $self->field_parser($field);
845 0           my $padding = $self->padding;
846              
847 0     0     local $SIG{__WARN__} = sub {};
  0            
848 0           @{$self->{struct}{$field}} = map {
  0            
849 0 0         $parser->unformat( $_->dump . $padding, 0, $self)->{$field}[0]
850 0           } grep defined, @{$self->{children}{$field}||[]};
851              
852 0           $self->validate_memberdata;
853             }
854              
855 0           $self->refresh_parent;
856             }
857              
858             sub refresh_parent {
859 0     0 0   my ($self) = @_;
860 0 0         my $parent = $self->{parent} or return;
861 0 0 0       $parent->refresh unless !Scalar::Util::blessed($parent) or $parent->{lazy};
862             }
863              
864             sub first_parent {
865 0     0 0   my ($self, $type) = @_;
866 0 0         return $self if $self->is_type($type);
867 0 0         my $parent = $self->{parent} or return;
868 0           return $parent->first_parent($type);
869             }
870              
871             sub substr {
872 0     0 0   my $self = shift;
873 0           my $data = $self->Data;
874 0           my $offset = shift(@_) - ($self->{size} - length($data));
875 0 0         my $length = @_ ? shift(@_) : (length($data) - $offset);
876 0           my $replace = shift;
877              
878             # XXX - Check for "substr outside string"
879 0 0         return if $offset > length($data);
880              
881             # Fetch a range
882 0 0         return substr($data, $offset, $length) if !defined $replace;
883              
884             # Substitute a range
885 0           substr($data, $offset, $length, $replace);
886 0           $self->{struct}{Data} = $data;
887             }
888              
889             sub set_output_file {
890 0     0 0   my ($self, $file) = @_;
891              
892 0 0         open my $fh, '>', $file or die $!;
893 0           binmode($fh);
894 0           $self->{output} = $fh;
895             }
896              
897             my %callback_map;
898             sub callback {
899 0     0 0   my $self = shift;
900 0   0       my $pkg = shift || caller;
901 0 0         my $types = shift or return;
902              
903 0   0       my $map = $callback_map{"@$types"} ||= $self->callback_map($pkg, $types);
904 0 0 0       my $sub = $map->{ref $self} || $map->{'*'} or return;
905 0           unshift @_, $self;
906 0           goto &$sub;
907             }
908              
909             sub callback_map {
910 0     0 0   my ($self, $pkg, $types) = @_;
911 0           my %map;
912 0           my $base = $self->BASE_CLASS;
913 0           foreach my $type (map "$_", @$types) {
914 1     1   9 no strict 'refs';
  1         2  
  1         326  
915 0           my $method = $type;
916 0           $method =~ s/::/_/g;
917 0           $method =~ s/\*/__/g;
918              
919 0 0         defined &{"$pkg\::$method"} or next;
  0            
920              
921 0 0         $type = "$base\::$type" unless $type eq '*';
922 0           $map{$type} = \&{"$pkg\::$method"};
  0            
923             }
924 0           return \%map;
925             }
926              
927             sub callback_members {
928 0     0 0   my $self = shift;
929 0           $self->{callback_members} = { map { ($_ => 1) } @{$_[0]} };
  0            
  0            
930              
931 0           while (my $member = $self->next_member) {
932 0           $member->callback(scalar caller, @_);
933             }
934             }
935              
936             sub done {
937 0     0 0   my $self = shift;
938 0 0         return unless $self->{lazy};
939 0           $self->write;
940 0           $self->remove;
941             }
942              
943             1;
944              
945             __END__