| 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__ |