| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Mac::Finder::DSStore::BuddyAllocator; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Mac::Finder::DSStore::BuddyAllocator - Allocate space within a file |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
C |
|
10
|
|
|
|
|
|
|
implements a buddy-allocation scheme within a file. It's used by |
|
11
|
|
|
|
|
|
|
C to read certain files created by the Macintosh |
|
12
|
|
|
|
|
|
|
Finder. |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
The allocation methods do not perform any actual file I/O. |
|
15
|
|
|
|
|
|
|
The contents of allocated blocks are read and written by the caller using |
|
16
|
|
|
|
|
|
|
methods on C. |
|
17
|
|
|
|
|
|
|
If the C and C methods are used, |
|
18
|
|
|
|
|
|
|
or if the C hash is modified, |
|
19
|
|
|
|
|
|
|
C must be called for the changes to be reflected in the file. |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 METHODS |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=cut |
|
24
|
|
|
|
|
|
|
|
|
25
|
5
|
|
|
5
|
|
111524
|
use strict; |
|
|
5
|
|
|
|
|
9
|
|
|
|
5
|
|
|
|
|
187
|
|
|
26
|
5
|
|
|
5
|
|
25
|
use warnings; |
|
|
5
|
|
|
|
|
10
|
|
|
|
5
|
|
|
|
|
140
|
|
|
27
|
5
|
|
|
5
|
|
28
|
use Carp; |
|
|
5
|
|
|
|
|
11
|
|
|
|
5
|
|
|
|
|
16848
|
|
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
our($VERSION) = '1.00'; |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# Debug logging. Uncomment these and all uses of them to activate. |
|
32
|
|
|
|
|
|
|
# It might be nice to make this more easily switchable. |
|
33
|
|
|
|
|
|
|
#our($loglevel) = 0; |
|
34
|
|
|
|
|
|
|
#sub logf { |
|
35
|
|
|
|
|
|
|
# print STDERR ( ' ' x $loglevel ) . sprintf($_[0], @_[1 .. $#_ ]) . "\n"; |
|
36
|
|
|
|
|
|
|
#} |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head2 $allocator = Mac::Finder::DSStore::BuddyAllocator->open($fh) |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
C constructs a new buddy allocator |
|
41
|
|
|
|
|
|
|
and initializes its state from the information in the file. |
|
42
|
|
|
|
|
|
|
The file handle is retained by the allocator for future |
|
43
|
|
|
|
|
|
|
operations. |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=cut |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub open { |
|
48
|
14
|
|
|
14
|
1
|
4163
|
my($class, $fh) = @_; |
|
49
|
|
|
|
|
|
|
|
|
50
|
14
|
|
|
|
|
86
|
binmode($fh); |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
# read the file header: 32 bytes, plus a mysterious extra |
|
53
|
|
|
|
|
|
|
# four bytes at the front |
|
54
|
14
|
|
|
|
|
31
|
my($fheader); |
|
55
|
14
|
100
|
|
|
|
106
|
$fh->read($fheader, 4 + 0x20) == 0x24 |
|
56
|
|
|
|
|
|
|
or die "Can't read file header: $!"; |
|
57
|
13
|
|
|
|
|
592
|
my($magic1, $magic, $offset, $size, $offset2, $unk2) = unpack('N a4 NNN a16', $fheader); |
|
58
|
13
|
50
|
33
|
|
|
366
|
die 'bad magic' unless $magic eq 'Bud1' and $magic1 == 1; |
|
59
|
13
|
50
|
|
|
|
46
|
die 'inconsistency: two root addresses are different' |
|
60
|
|
|
|
|
|
|
unless $offset == $offset2; |
|
61
|
|
|
|
|
|
|
|
|
62
|
13
|
|
|
|
|
73
|
my($self) = { |
|
63
|
|
|
|
|
|
|
fh => $fh, |
|
64
|
|
|
|
|
|
|
unk2 => $unk2, |
|
65
|
|
|
|
|
|
|
fudge => 4, # add this to offsets for some unknown reason |
|
66
|
|
|
|
|
|
|
}; |
|
67
|
13
|
|
33
|
|
|
89
|
bless($self, ref($class) || $class); |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# retrieve the root/index block which contains the allocator's |
|
70
|
|
|
|
|
|
|
# book-keeping data |
|
71
|
13
|
|
|
|
|
57
|
my ($rootblock) = $self->getBlock($offset, $size); |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# parse out the offsets of all the allocated blocks |
|
74
|
|
|
|
|
|
|
# these are in tagged offset format (27 bits offset, 5 bits size) |
|
75
|
13
|
|
|
|
|
55
|
my($offsetcount, $unk3) = $rootblock->read(8, 'NN'); |
|
76
|
|
|
|
|
|
|
# not sure what the word following the offset count is |
|
77
|
13
|
|
|
|
|
41
|
$self->{'unk3'} = $unk3; |
|
78
|
|
|
|
|
|
|
# For some reason, offsets are always stored in blocks of 256. |
|
79
|
13
|
|
|
|
|
18
|
my(@offsets); |
|
80
|
13
|
|
|
|
|
42
|
while($offsetcount > 0) { |
|
81
|
14
|
|
|
|
|
40
|
push(@offsets, $rootblock->read(1024, 'N256')); |
|
82
|
14
|
|
|
|
|
139
|
$offsetcount -= 256; |
|
83
|
|
|
|
|
|
|
} |
|
84
|
|
|
|
|
|
|
# 0 indicates an empty slot; don't need to keep those around |
|
85
|
13
|
|
|
|
|
51
|
while($offsets[$#offsets] == 0) { pop(@offsets); } |
|
|
2730
|
|
|
|
|
5323
|
|
|
86
|
13
|
100
|
|
|
|
38
|
grep { $_ = undef if $_ == 0 } @offsets; |
|
|
854
|
|
|
|
|
2476
|
|
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# Next, read N key/value pairs |
|
89
|
13
|
|
|
|
|
47
|
my($toccount) = $rootblock->read(4, 'N'); |
|
90
|
13
|
|
|
|
|
34
|
my($toc) = { |
|
91
|
|
|
|
|
|
|
}; |
|
92
|
13
|
|
|
|
|
44
|
while($toccount--) { |
|
93
|
22
|
|
|
|
|
58
|
my($len) = $rootblock->read(1, 'C'); |
|
94
|
22
|
|
|
|
|
72
|
my($name) = $rootblock->read($len); |
|
95
|
22
|
|
|
|
|
65
|
my($value) = $rootblock->read(4, 'N'); |
|
96
|
22
|
|
|
|
|
91
|
$toc->{$name} = $value; |
|
97
|
|
|
|
|
|
|
} |
|
98
|
|
|
|
|
|
|
|
|
99
|
13
|
|
|
|
|
38
|
$self->{'offsets'} = \@offsets; |
|
100
|
13
|
|
|
|
|
76
|
$self->{'toc'} = $toc; |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# Finally, read the free lists. |
|
103
|
13
|
|
|
|
|
30
|
my($freelists) = { }; |
|
104
|
13
|
|
|
|
|
52
|
for(my $width = 0; $width < 32; $width ++) { |
|
105
|
416
|
|
|
|
|
1061
|
my($blkcount) = $rootblock->read(4, 'N'); |
|
106
|
416
|
|
|
|
|
901
|
$freelists->{$width} = [ $rootblock->read(4 * $blkcount, 'N*') ]; |
|
107
|
|
|
|
|
|
|
} |
|
108
|
13
|
|
|
|
|
30
|
$self->{'freelist'} = $freelists; |
|
109
|
|
|
|
|
|
|
|
|
110
|
13
|
|
|
|
|
96
|
return $self; |
|
111
|
|
|
|
|
|
|
} |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=head2 $allocator = Mac::Finder::DSStore::BuddyAllocator->new($fh) |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
Similar to C, but does not read anything from the file. This |
|
116
|
|
|
|
|
|
|
can be used to create a new file from scratch. |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=cut |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub new { |
|
121
|
4
|
|
|
4
|
1
|
2395
|
my($cls, $fh) = @_; |
|
122
|
|
|
|
|
|
|
|
|
123
|
4
|
100
|
|
|
|
28
|
binmode($fh) if defined($fh); |
|
124
|
|
|
|
|
|
|
|
|
125
|
4
|
|
|
|
|
39
|
my($self) = { |
|
126
|
|
|
|
|
|
|
fh => $fh, |
|
127
|
|
|
|
|
|
|
toc => { }, |
|
128
|
|
|
|
|
|
|
offsets => [ ], |
|
129
|
|
|
|
|
|
|
freelist => { }, |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# And the mystery meat goes here... |
|
132
|
|
|
|
|
|
|
unk2 => pack('NNNN', 0x100C, 0x0087, 0x200B, 0 ), |
|
133
|
|
|
|
|
|
|
unk3 => 0, |
|
134
|
|
|
|
|
|
|
fudge => 4 |
|
135
|
|
|
|
|
|
|
}; |
|
136
|
4
|
|
33
|
|
|
39
|
bless($self, ref $cls || $cls); |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# All our freelists are empty... |
|
139
|
4
|
|
|
|
|
29
|
foreach my $width (0 .. 30) { |
|
140
|
124
|
|
|
|
|
369
|
$self->{freelist}->{$width} = [ ]; |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
# ... except for a single 2GB block starting at 0 |
|
143
|
4
|
|
|
|
|
25
|
$self->{freelist}->{31} = [ 0 ]; |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# Allocate the header block, 2^5 bytes wide |
|
146
|
4
|
|
|
|
|
23
|
my($hdr) = $self->_alloc(5); |
|
147
|
|
|
|
|
|
|
# it had better be at offset zero |
|
148
|
4
|
50
|
|
|
|
23
|
( $hdr == 0 ) or die; |
|
149
|
|
|
|
|
|
|
|
|
150
|
4
|
|
|
|
|
17
|
$self; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head2 $allocator->close( ) |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Closes the underlying file handle. |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=cut |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
sub close { |
|
160
|
12
|
|
|
12
|
1
|
9081
|
my($self) = @_; |
|
161
|
12
|
|
|
|
|
36
|
my($fh) = $self->{fh}; |
|
162
|
|
|
|
|
|
|
|
|
163
|
12
|
|
|
|
|
79
|
delete $self->{fh}; |
|
164
|
|
|
|
|
|
|
|
|
165
|
12
|
|
|
|
|
98
|
$fh->close; |
|
166
|
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=head2 $allocator->listBlocks($verbose) |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
List all the blocks in order and see if there are any gaps or overlaps. |
|
171
|
|
|
|
|
|
|
If C<$verbose> is true, then the blocks are listed to the current |
|
172
|
|
|
|
|
|
|
output filehandle. Returns true if the allocated and free blocks |
|
173
|
|
|
|
|
|
|
have no gaps or overlaps. |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=cut |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub listBlocks { |
|
178
|
58
|
|
|
58
|
1
|
12734
|
my($self, $verbose) = @_; |
|
179
|
58
|
|
|
|
|
84
|
my(%byaddr); |
|
180
|
58
|
|
|
|
|
74
|
my($addr, $len); |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# We store all blocks (allocated and free) in %byaddr, |
|
183
|
|
|
|
|
|
|
# then go through its keys in order |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# Store the implicit 32-byte block that holds the file header |
|
186
|
58
|
|
|
|
|
71
|
push(@{$byaddr{0}}, "5 (file header)"); |
|
|
58
|
|
|
|
|
186
|
|
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# Store all the numbered/allocated blocks from @offsets |
|
189
|
58
|
|
|
|
|
90
|
for my $blnum (0 .. $#{$self->{'offsets'}}) { |
|
|
58
|
|
|
|
|
184
|
|
|
190
|
2558
|
|
|
|
|
3932
|
my($addr_size) = $self->{'offsets'}->[$blnum]; |
|
191
|
2558
|
100
|
|
|
|
6132
|
next unless defined $addr_size; |
|
192
|
2265
|
|
|
|
|
2476
|
$addr = $addr_size & ~0x1F; |
|
193
|
2265
|
|
|
|
|
3881
|
$len = $addr_size & 0x1F; |
|
194
|
2265
|
|
|
|
|
2213
|
push(@{$byaddr{$addr}}, "$len (blkid $blnum)"); |
|
|
2265
|
|
|
|
|
8821
|
|
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
# Store all the blocks in the freelist(s) |
|
198
|
58
|
|
|
|
|
110
|
for $len (keys %{$self->{'freelist'}}) { |
|
|
58
|
|
|
|
|
489
|
|
|
199
|
1856
|
|
|
|
|
1938
|
for $addr (@{$self->{'freelist'}->{$len}}) { |
|
|
1856
|
|
|
|
|
3495
|
|
|
200
|
1635
|
|
|
|
|
1625
|
push(@{$byaddr{$addr}}, "$len (free)"); |
|
|
1635
|
|
|
|
|
5470
|
|
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
} |
|
203
|
|
|
|
|
|
|
|
|
204
|
58
|
|
|
|
|
223
|
my($gaps, $overlaps) = (0, 0); |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
# Loop through the blocks in order of address |
|
207
|
58
|
|
|
|
|
716
|
my(@addrs) = sort {$a <=> $b} keys %byaddr; |
|
|
21509
|
|
|
|
|
26591
|
|
|
208
|
58
|
|
|
|
|
266
|
$addr = 0; |
|
209
|
58
|
|
|
|
|
156
|
while(@addrs) { |
|
210
|
3958
|
|
|
|
|
5983
|
my($next) = shift @addrs; |
|
211
|
3958
|
50
|
|
|
|
10282
|
if ($next > $addr) { |
|
212
|
0
|
0
|
|
|
|
0
|
print "... ", ($next - $addr), " bytes unaccounted for\n" |
|
213
|
|
|
|
|
|
|
if $verbose; |
|
214
|
0
|
|
|
|
|
0
|
$gaps ++; |
|
215
|
|
|
|
|
|
|
} |
|
216
|
3958
|
|
|
|
|
5297
|
my(@uses) = @{$byaddr{$next}}; |
|
|
3958
|
|
|
|
|
9206
|
|
|
217
|
3958
|
50
|
|
|
|
7866
|
printf "%08x %s\n", $next, join(', ', @uses) |
|
218
|
|
|
|
|
|
|
if $verbose; |
|
219
|
3958
|
50
|
|
|
|
7236
|
$overlaps ++ if @uses > 1; |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# strip off the length (log_2(length) really) from the info str |
|
222
|
3958
|
|
|
|
|
19308
|
($len = $uses[0]) =~ s/ .*//; |
|
223
|
3958
|
|
|
|
|
12711
|
$addr = $next + ( 1 << (0 + $len) ); |
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
|
|
226
|
58
|
50
|
|
|
|
2016
|
( $gaps == 0 && $overlaps == 0 ); |
|
227
|
|
|
|
|
|
|
} |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=head2 $allocator->writeMetaData( ) |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
Writes the allocator's metadata (header block and root block) |
|
232
|
|
|
|
|
|
|
back to the file. |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=cut |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub writeMetaData { |
|
237
|
11
|
|
|
11
|
1
|
30
|
my($self) = @_; |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# Root block nr is hardcoded to 0. |
|
240
|
|
|
|
|
|
|
# We don't actually care, but the DSStore btree does. |
|
241
|
11
|
|
|
|
|
23
|
my($blocknr) = 0; |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
# Before computing the size of the rootblock to allocate it, |
|
244
|
|
|
|
|
|
|
# make sure it'll be large enough to hold its own (eventual) |
|
245
|
|
|
|
|
|
|
# allocation information. |
|
246
|
11
|
100
|
|
|
|
58
|
$self->{offsets}->[0] = undef unless exists $self->{offsets}->[0]; |
|
247
|
|
|
|
|
|
|
|
|
248
|
11
|
|
|
|
|
44
|
my($rbs) = $self->rootBlockSize(); |
|
249
|
11
|
|
|
|
|
49
|
$self->allocate($rbs, $blocknr); |
|
250
|
|
|
|
|
|
|
|
|
251
|
11
|
|
|
|
|
37
|
$self->writeRootblock($self->blockByNumber($blocknr, 1)); |
|
252
|
|
|
|
|
|
|
|
|
253
|
11
|
|
|
|
|
69
|
my($blockOffset, $blockLength) = $self->blockOffset($blocknr); |
|
254
|
|
|
|
|
|
|
|
|
255
|
11
|
|
|
|
|
54
|
$self->{fh}->seek(0, 0); |
|
256
|
11
|
|
|
|
|
5773
|
$self->{fh}->write(pack('N', 1)); # magic1 |
|
257
|
11
|
|
|
|
|
181
|
$self->_sought(0)->write(pack('a4 NNN a16', |
|
258
|
|
|
|
|
|
|
'Bud1', # magic |
|
259
|
|
|
|
|
|
|
$blockOffset, $blockLength, $blockOffset, |
|
260
|
|
|
|
|
|
|
$self->{unk2})); |
|
261
|
|
|
|
|
|
|
|
|
262
|
11
|
|
|
|
|
306
|
$self->{fh}->flush; |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub rootBlockSize { |
|
266
|
11
|
|
|
11
|
0
|
21
|
my($self) = @_; |
|
267
|
11
|
|
|
|
|
17
|
my($size); |
|
268
|
|
|
|
|
|
|
|
|
269
|
11
|
|
|
|
|
18
|
$size = 8; # The offset count and the unknown field that follows it |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# The offset blocks, rounded up to a multiple of 256 entries |
|
272
|
11
|
|
|
|
|
26
|
my($offsetcount) = scalar( @{$self->{'offsets'}} ); |
|
|
11
|
|
|
|
|
38
|
|
|
273
|
11
|
|
|
|
|
28
|
my($tail) = $offsetcount % 256; |
|
274
|
11
|
100
|
|
|
|
37
|
$offsetcount += 256 - $tail if ($tail); |
|
275
|
11
|
|
|
|
|
23
|
$size += 4 * $offsetcount; |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
# The table of contents |
|
278
|
11
|
|
|
|
|
21
|
$size += 4; # count |
|
279
|
11
|
|
|
|
|
16
|
$size += (5 + length($_)) foreach keys %{$self->{'toc'}}; |
|
|
11
|
|
|
|
|
83
|
|
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# The freelists |
|
282
|
11
|
|
|
|
|
34
|
foreach my $width (0 .. 31) { |
|
283
|
352
|
|
|
|
|
493
|
$size += 4 + 4 * scalar( @{$self->{'freelist'}->{$width}} ); |
|
|
352
|
|
|
|
|
724
|
|
|
284
|
|
|
|
|
|
|
} |
|
285
|
|
|
|
|
|
|
|
|
286
|
11
|
|
|
|
|
31
|
$size; |
|
287
|
|
|
|
|
|
|
} |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
sub writeRootblock { |
|
290
|
11
|
|
|
11
|
0
|
23
|
my($self, $into) = @_; |
|
291
|
|
|
|
|
|
|
|
|
292
|
11
|
|
|
|
|
15
|
my(@offsets) = @{$self->{'offsets'}}; |
|
|
11
|
|
|
|
|
173
|
|
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# Write the offset count & the unknown field that follows it |
|
295
|
11
|
|
|
|
|
49
|
$into->write('NN', scalar(@offsets), $self->{'unk3'}); |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
# Write the offsets (using 0 to indicate an unused slot) |
|
298
|
11
|
100
|
66
|
|
|
31
|
$into->write('N*', map { (defined($_) && $_ > 0)? $_ : 0 } @offsets); |
|
|
848
|
|
|
|
|
6454
|
|
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# The offsets are always written in blocks of 256. |
|
301
|
11
|
|
|
|
|
48
|
my($offsetcount) = scalar(@offsets) % 256; |
|
302
|
11
|
100
|
|
|
|
45
|
if ($offsetcount > 0) { |
|
303
|
|
|
|
|
|
|
# Fill out the last block |
|
304
|
10
|
|
|
|
|
76
|
$into->write('N*', (0) x (256-$offsetcount)); |
|
305
|
|
|
|
|
|
|
} |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# The DS_Store files only ever have one item in their |
|
308
|
|
|
|
|
|
|
# table of contents, so I'm not sure if it needs to be sorted or what |
|
309
|
11
|
|
|
|
|
19
|
my(@tockeys) = sort keys %{$self->{'toc'}}; |
|
|
11
|
|
|
|
|
67
|
|
|
310
|
11
|
|
|
|
|
38
|
$into->write('N', scalar(@tockeys)); |
|
311
|
11
|
|
|
|
|
28
|
foreach my $entry (@tockeys) { |
|
312
|
20
|
|
|
|
|
72
|
$into->write('C a* N', length($entry), $entry, $self->{'toc'}->{$entry}); |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
# And finally the freelists |
|
316
|
11
|
|
|
|
|
43
|
for my $width ( 0 .. 31 ) { |
|
317
|
352
|
|
|
|
|
861
|
my($blks) = $self->{'freelist'}->{$width}; |
|
318
|
352
|
|
|
|
|
7730
|
$into->write('N N*', scalar(@$blks), @$blks); |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
} |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=head2 $block = $allocator->blockByNumber(blocknumber[, write]) |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
Retrieves a block by its block number (I block ID). |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
If C is supplied and is true, then the returned block implements the |
|
327
|
|
|
|
|
|
|
C method but not the C method. |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=head2 $block = $allocator->getBlock(offset, size) |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
Retrieves a block (a BuddyAllocator::Block instance) by offset & length. |
|
332
|
|
|
|
|
|
|
Normally you should use C instead of this method. |
|
333
|
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
=cut |
|
335
|
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
sub getBlock { |
|
337
|
13
|
|
|
13
|
1
|
32
|
my($self, $offset, $size) = @_; |
|
338
|
|
|
|
|
|
|
|
|
339
|
13
|
|
|
|
|
108
|
return Mac::Finder::DSStore::BuddyAllocator::Block->new($self, $offset, $size); |
|
340
|
|
|
|
|
|
|
} |
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
# Retrieve a block by its block number (small integer) |
|
343
|
|
|
|
|
|
|
sub blockByNumber { |
|
344
|
269
|
|
|
269
|
1
|
445
|
my($self, $id, $write) = @_; |
|
345
|
269
|
|
|
|
|
533
|
my($addr) = $self->{offsets}->[$id]; |
|
346
|
269
|
50
|
|
|
|
655
|
return undef unless $addr; |
|
347
|
269
|
|
|
|
|
352
|
my($offset, $len); |
|
348
|
269
|
|
|
|
|
384
|
$offset = $addr & ~0x1F; |
|
349
|
269
|
|
|
|
|
344
|
$len = 1 << ( $addr & 0x1F ); |
|
350
|
|
|
|
|
|
|
# print " node id $id is $len bytes at 0x".sprintf('%x', $offset)."\n"; |
|
351
|
269
|
100
|
66
|
|
|
969
|
if (!defined($write) || !$write) { |
|
352
|
193
|
|
|
|
|
603
|
return Mac::Finder::DSStore::BuddyAllocator::Block->new($self, $offset, $len); |
|
353
|
|
|
|
|
|
|
} else { |
|
354
|
76
|
|
|
|
|
482
|
return Mac::Finder::DSStore::BuddyAllocator::WriteBlock->new($self, $offset, $len); |
|
355
|
|
|
|
|
|
|
} |
|
356
|
|
|
|
|
|
|
} |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=head2 ( $offset, $size ) = $allocator->blockOffset(blockid) |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
Retrieves the file offset and size in bytes of a given block. |
|
361
|
|
|
|
|
|
|
The offset doesn't include the 4-byte fudge. |
|
362
|
|
|
|
|
|
|
In scalar context, just returns the offset. |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=cut |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
sub blockOffset { |
|
367
|
204
|
|
|
204
|
1
|
1832
|
my($self, $id) = @_; |
|
368
|
204
|
|
|
|
|
321
|
my($addr) = $self->{offsets}->[$id]; |
|
369
|
204
|
100
|
|
|
|
515
|
croak "Block $id is not allocated" unless $addr; |
|
370
|
203
|
|
|
|
|
226
|
my($offset) = $addr & ~0x1F; |
|
371
|
203
|
100
|
|
|
|
490
|
return $offset unless wantarray; |
|
372
|
107
|
|
|
|
|
278
|
return ( $offset, 1 << ( $addr & 0x1F ) ); |
|
373
|
|
|
|
|
|
|
} |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
# Return freelist + index of a block's buddy in its freelist (or empty list) |
|
376
|
|
|
|
|
|
|
sub _buddy { |
|
377
|
620
|
|
|
620
|
|
1009
|
my($self, $offset, $width) = @_; |
|
378
|
620
|
|
|
|
|
2953
|
my($freelist, $buddyaddr); |
|
379
|
|
|
|
|
|
|
|
|
380
|
620
|
|
|
|
|
1247
|
$freelist = $self->{'freelist'}->{$width}; |
|
381
|
620
|
|
|
|
|
850
|
$buddyaddr = $offset ^ ( 1 << $width ); |
|
382
|
|
|
|
|
|
|
|
|
383
|
430
|
|
|
|
|
1077
|
return ($freelist, |
|
384
|
620
|
|
|
|
|
1425
|
grep { $freelist->[$_] == $buddyaddr } 0 .. $#$freelist ); |
|
385
|
|
|
|
|
|
|
} |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
# Free a block, coalescing ith buddies as needed. |
|
388
|
|
|
|
|
|
|
sub _free { |
|
389
|
620
|
|
|
620
|
|
901
|
my($self, $offset, $width) = @_; |
|
390
|
|
|
|
|
|
|
|
|
391
|
620
|
|
|
|
|
1210
|
my($freelist, $buddyindex) = $self->_buddy($offset, $width); |
|
392
|
|
|
|
|
|
|
|
|
393
|
620
|
100
|
|
|
|
1476
|
if(defined($buddyindex)) { |
|
394
|
|
|
|
|
|
|
# our buddy is free. Coalesce, and add the coalesced block to flist. |
|
395
|
84
|
|
|
|
|
168
|
my($buddyoffset) = splice(@$freelist, $buddyindex, 1); |
|
396
|
|
|
|
|
|
|
#&logf("Combining %x with buddy %x", $offset, $buddyoffset); |
|
397
|
84
|
|
|
|
|
246
|
$self->_free($offset & $buddyoffset, $width+1); |
|
398
|
|
|
|
|
|
|
} else { |
|
399
|
|
|
|
|
|
|
#&logf("Adding block %x to freelist %d", $offset, $width); |
|
400
|
536
|
|
|
|
|
2037
|
@$freelist = sort( @$freelist, $offset ); |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
} |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# Allocate a block of a specified width, splitting as needed. |
|
405
|
|
|
|
|
|
|
sub _alloc { |
|
406
|
803
|
|
|
803
|
|
1172
|
my($self, $width) = @_; |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
#&logf("Allocating a block of width %d", $width); |
|
409
|
|
|
|
|
|
|
#$loglevel ++; |
|
410
|
|
|
|
|
|
|
|
|
411
|
803
|
|
|
|
|
1868
|
my($flist) = $self->{'freelist'}->{$width}; |
|
412
|
803
|
100
|
|
|
|
1607
|
if (@$flist) { |
|
413
|
|
|
|
|
|
|
# There is a block of the desired size; return it. |
|
414
|
|
|
|
|
|
|
#&logf("Pulling %x from freelist", $flist->[0]); $loglevel --; |
|
415
|
365
|
|
|
|
|
912
|
return shift @$flist; |
|
416
|
|
|
|
|
|
|
} else { |
|
417
|
|
|
|
|
|
|
# Allocate a block of the next larger size; split it. |
|
418
|
438
|
|
|
|
|
1525
|
my($offset) = $self->_alloc($width + 1); |
|
419
|
|
|
|
|
|
|
# and put the other half on the free list. |
|
420
|
438
|
|
|
|
|
976
|
my($buddy) = $offset ^ ( 1 << $width ); |
|
421
|
|
|
|
|
|
|
#&logf("Splitting %x into %x and %x", $offset, $offset, $buddy); |
|
422
|
|
|
|
|
|
|
#$loglevel ++; |
|
423
|
438
|
|
|
|
|
1060
|
$self->_free($buddy, $width); |
|
424
|
|
|
|
|
|
|
#$loglevel -= 2; |
|
425
|
438
|
|
|
|
|
972
|
return $offset; |
|
426
|
|
|
|
|
|
|
} |
|
427
|
|
|
|
|
|
|
} |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=head2 $blocknumber = $allocator->allocate($size, [$blocknumber]) |
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
Allocates or re-allocates a block to be at least C<$size> bytes long. |
|
432
|
|
|
|
|
|
|
If C<$blocknumber> is given, the specified block will be grown or |
|
433
|
|
|
|
|
|
|
shrunk if needed, otherwise a new block number will be chosen and |
|
434
|
|
|
|
|
|
|
given to the allocated block. |
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
Unlike the libc C function, this may move a block even if the |
|
437
|
|
|
|
|
|
|
block is not grown. |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=head2 $allocator->free($blocknumer) |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
Releases the block number and the block associated with it back to the |
|
442
|
|
|
|
|
|
|
block pool. |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=cut |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
sub allocate { |
|
447
|
369
|
|
|
369
|
1
|
5883
|
my($self, $bytes, $blocknum) = @_; |
|
448
|
369
|
|
|
|
|
681
|
my($offsets) = $self->{'offsets'}; |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
#if(defined($blocknum)) { |
|
451
|
|
|
|
|
|
|
# &logf("(Re)allocating %d bytes for blkid %d", $bytes, $blocknum); |
|
452
|
|
|
|
|
|
|
#} |
|
453
|
|
|
|
|
|
|
|
|
454
|
369
|
100
|
|
|
|
886
|
if(!defined($blocknum)) { |
|
455
|
344
|
|
|
|
|
429
|
$blocknum = 1; |
|
456
|
|
|
|
|
|
|
# search for an empty slot, or extend the array |
|
457
|
344
|
|
|
|
|
28995
|
$blocknum++ while defined($offsets->[$blocknum]); |
|
458
|
|
|
|
|
|
|
#&logf("Allocating %d bytes, assigning blkid %d", $bytes, $blocknum); |
|
459
|
|
|
|
|
|
|
} |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
#$loglevel ++; |
|
462
|
|
|
|
|
|
|
|
|
463
|
369
|
|
|
|
|
568
|
my($wantwidth) = 5; |
|
464
|
|
|
|
|
|
|
# Minimum width, since that's how many low-order bits we steal for the tag |
|
465
|
369
|
|
|
|
|
1294
|
$wantwidth ++ while $bytes > 1 << $wantwidth; |
|
466
|
|
|
|
|
|
|
|
|
467
|
369
|
|
|
|
|
424
|
my($blkaddr, $blkwidth, $blkoffset); |
|
468
|
|
|
|
|
|
|
|
|
469
|
369
|
100
|
100
|
|
|
1001
|
if(exists($offsets->[$blocknum]) && $offsets->[$blocknum]) { |
|
470
|
22
|
|
|
|
|
38
|
$blkaddr = $offsets->[$blocknum]; |
|
471
|
22
|
|
|
|
|
34
|
$blkwidth = $blkaddr & 0x1F; |
|
472
|
22
|
|
|
|
|
30
|
$blkoffset = $blkaddr & ~0x1F; |
|
473
|
22
|
100
|
|
|
|
57
|
if ($blkwidth == $wantwidth) { |
|
474
|
|
|
|
|
|
|
#&logf("Block is already width %d, no change", $wantwidth); |
|
475
|
|
|
|
|
|
|
#$loglevel --; |
|
476
|
|
|
|
|
|
|
# The block is currently of the desired size. Leave it alone. |
|
477
|
8
|
|
|
|
|
21
|
return $blocknum; |
|
478
|
|
|
|
|
|
|
} else { |
|
479
|
|
|
|
|
|
|
#&logf("Freeing wrong-sized block"); |
|
480
|
|
|
|
|
|
|
#$loglevel ++; |
|
481
|
|
|
|
|
|
|
# Free the current block, allocate a new one. |
|
482
|
14
|
|
|
|
|
38
|
$self->_free($blkoffset, $blkwidth); |
|
483
|
14
|
|
|
|
|
25
|
delete $offsets->[$blocknum]; |
|
484
|
|
|
|
|
|
|
#$loglevel --; |
|
485
|
|
|
|
|
|
|
} |
|
486
|
|
|
|
|
|
|
} |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
# Allocate a block, update the offsets table, and return the new offset |
|
489
|
361
|
|
|
|
|
967
|
$blkoffset = $self->_alloc($wantwidth); |
|
490
|
361
|
|
|
|
|
558
|
$blkaddr = $blkoffset | $wantwidth; |
|
491
|
361
|
|
|
|
|
742
|
$offsets->[$blocknum] = $blkaddr; |
|
492
|
|
|
|
|
|
|
#$loglevel --; |
|
493
|
361
|
|
|
|
|
954
|
$blocknum; |
|
494
|
|
|
|
|
|
|
} |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
sub free { |
|
497
|
84
|
|
|
84
|
1
|
10458
|
my($self, $blknum) = @_; |
|
498
|
84
|
|
|
|
|
150
|
my($blkaddr) = $self->{'offsets'}->[$blknum]; |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
#&logf("Freeing block index %d", $blknum); |
|
501
|
|
|
|
|
|
|
#$loglevel ++; |
|
502
|
|
|
|
|
|
|
|
|
503
|
84
|
50
|
|
|
|
177
|
if($blkaddr) { |
|
504
|
84
|
|
|
|
|
116
|
my($blkoffset, $blkwidth); |
|
505
|
84
|
|
|
|
|
112
|
$blkwidth = $blkaddr & 0x1F; |
|
506
|
84
|
|
|
|
|
88
|
$blkoffset = $blkaddr & ~0x1F; |
|
507
|
84
|
|
|
|
|
173
|
$self->_free($blkoffset, $blkwidth); |
|
508
|
|
|
|
|
|
|
} |
|
509
|
|
|
|
|
|
|
|
|
510
|
84
|
|
|
|
|
178
|
delete $self->{'offsets'}->[$blknum]; |
|
511
|
|
|
|
|
|
|
#$loglevel --; |
|
512
|
84
|
|
|
|
|
359
|
undef; |
|
513
|
|
|
|
|
|
|
} |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
|
516
|
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
=head2 $allocator->{toc} |
|
518
|
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
C holds a hashref whose keys are short strings and whose values |
|
520
|
|
|
|
|
|
|
are integers. This table of contents is read and written as part of the |
|
521
|
|
|
|
|
|
|
allocator's metadata but is not otherwise used by the allocator; |
|
522
|
|
|
|
|
|
|
users of the allocator use it to find their data within the file. |
|
523
|
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=head2 $allocator->{fh} |
|
525
|
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
The file handle passed in to C or C. If you find yourself needing |
|
527
|
|
|
|
|
|
|
to use this, you should probably try to extend the class so that you don't. |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
=cut |
|
530
|
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
# Used by ...::Block to get a positioned file handle. |
|
532
|
|
|
|
|
|
|
sub _sought { |
|
533
|
413
|
|
|
413
|
|
671
|
my($self, $offset) = @_; |
|
534
|
|
|
|
|
|
|
|
|
535
|
413
|
|
|
|
|
987
|
my($fh) = $self->{fh}; |
|
536
|
413
|
50
|
|
|
|
1887
|
$fh->seek($offset + $self->{fudge}, 0) |
|
537
|
|
|
|
|
|
|
or croak; |
|
538
|
413
|
|
|
|
|
13565
|
$fh; |
|
539
|
|
|
|
|
|
|
} |
|
540
|
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
package Mac::Finder::DSStore::BuddyAllocator::Block; |
|
542
|
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
=head1 BuddyAllocator::Block |
|
544
|
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
C instances are returned by the |
|
546
|
|
|
|
|
|
|
C and C methods. They hold a pointer into |
|
547
|
|
|
|
|
|
|
the file and provide a handful of useful methods. |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
(There are also two other classes, C and C, |
|
550
|
|
|
|
|
|
|
which might be returned instead. Think of this as an interface rather |
|
551
|
|
|
|
|
|
|
than as a concrete class.) |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=head2 $block->read(length, [format]) |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
Reads C bytes from the block (advancing the read pointer |
|
556
|
|
|
|
|
|
|
correspondingly). If C is specified, the bytes read are |
|
557
|
|
|
|
|
|
|
unpacked using the format; otherwise a byte string is returned. |
|
558
|
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
=head2 $block->length( ) |
|
560
|
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
Returns the length (or size) of this block. |
|
562
|
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
=head2 $block->seek(position[, whence]) |
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
Adjusts the read/write pointer within the block. |
|
566
|
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
=head2 $block->write(bytes) |
|
568
|
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=head2 $block->write(format, items...) |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
Writes data to the underlying file, at the position represented by this |
|
572
|
|
|
|
|
|
|
block. If multiple arguments are given, the first is a format string |
|
573
|
|
|
|
|
|
|
and the rest are the remaining arguments to C. |
|
574
|
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
=head2 $block->close([ zerofill ]) |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
This is generally a no-op, but if called on a writable block with |
|
578
|
|
|
|
|
|
|
C, then zeroes will be written from the current |
|
579
|
|
|
|
|
|
|
location to the end of the allocated block. |
|
580
|
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
=head2 $block->copyback( ) |
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
Returns the block's contents as a string. For write blocks, this |
|
584
|
|
|
|
|
|
|
reads from the file. This is just here for debugging purposes and |
|
585
|
|
|
|
|
|
|
might change. |
|
586
|
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
=cut |
|
588
|
|
|
|
|
|
|
|
|
589
|
5
|
|
|
5
|
|
56
|
use strict; |
|
|
5
|
|
|
|
|
11
|
|
|
|
5
|
|
|
|
|
183
|
|
|
590
|
5
|
|
|
5
|
|
31
|
use warnings; |
|
|
5
|
|
|
|
|
10
|
|
|
|
5
|
|
|
|
|
178
|
|
|
591
|
5
|
|
|
5
|
|
35
|
use Carp; |
|
|
5
|
|
|
|
|
9
|
|
|
|
5
|
|
|
|
|
2476
|
|
|
592
|
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
# |
|
594
|
|
|
|
|
|
|
# Block objects are created by the buddy allocator; they're a |
|
595
|
|
|
|
|
|
|
# reference to an array with the following components: |
|
596
|
|
|
|
|
|
|
# |
|
597
|
|
|
|
|
|
|
# [ $allocator, $value, $position] |
|
598
|
|
|
|
|
|
|
# |
|
599
|
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
sub new { |
|
601
|
266
|
|
|
266
|
|
466
|
my($class, $allocator, $offset, $size) = @_; |
|
602
|
|
|
|
|
|
|
|
|
603
|
266
|
|
|
|
|
320
|
my($value); |
|
604
|
266
|
50
|
|
|
|
645
|
$allocator->_sought($offset)->read($value, $size) |
|
605
|
|
|
|
|
|
|
> 0 or die; |
|
606
|
|
|
|
|
|
|
# Previously, this died if we couldn't read the full block. |
|
607
|
|
|
|
|
|
|
# Not sure if it's really an error not to read the full |
|
608
|
|
|
|
|
|
|
# block if the next layer up doesn't need the full block. |
|
609
|
|
|
|
|
|
|
# So now we're succeeding as long as we get something; if |
|
610
|
|
|
|
|
|
|
# the reader overruns it'll die in substr(). |
|
611
|
|
|
|
|
|
|
|
|
612
|
266
|
|
33
|
|
|
8775
|
bless([ $allocator, $value, 0 ], ref $class || $class); |
|
613
|
|
|
|
|
|
|
} |
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
sub read { |
|
616
|
9240
|
|
|
9240
|
|
14494
|
my($self, $len, $unpack) = @_; |
|
617
|
|
|
|
|
|
|
|
|
618
|
9240
|
|
|
|
|
12887
|
my($pos) = $self->[2]; |
|
619
|
9240
|
50
|
|
|
|
20277
|
die "out of range: pos=$pos len=$len max=".(length($self->[1])) if $pos + $len > length($self->[1]); |
|
620
|
9240
|
|
|
|
|
18206
|
my($bytes) = substr($self->[1], $pos, $len); |
|
621
|
9240
|
|
|
|
|
16302
|
$self->[2] = $pos + $len; |
|
622
|
|
|
|
|
|
|
|
|
623
|
9240
|
100
|
|
|
|
37563
|
$unpack? unpack($unpack, $bytes) : $bytes; |
|
624
|
|
|
|
|
|
|
} |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
sub length { |
|
627
|
0
|
|
|
0
|
|
0
|
return CORE::length($_[0]->[1]); |
|
628
|
|
|
|
|
|
|
} |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
sub close { |
|
631
|
0
|
|
|
0
|
|
0
|
1; |
|
632
|
|
|
|
|
|
|
} |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
sub seek { |
|
635
|
6
|
|
|
6
|
|
14
|
my($self, $pos, $whence) = @_; |
|
636
|
6
|
50
|
|
|
|
24
|
$whence = 0 unless defined $whence; |
|
637
|
6
|
50
|
|
|
|
16
|
if ($whence == 0) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
# pos = pos |
|
639
|
|
|
|
|
|
|
} elsif ($whence == 1) { |
|
640
|
0
|
|
|
|
|
0
|
$pos += $self->[2]; |
|
641
|
|
|
|
|
|
|
} elsif ($whence == 2) { |
|
642
|
0
|
|
|
|
|
0
|
$pos += $self->length(); |
|
643
|
|
|
|
|
|
|
} else { |
|
644
|
0
|
|
|
|
|
0
|
croak "seek: whence=$whence"; |
|
645
|
|
|
|
|
|
|
} |
|
646
|
6
|
|
|
|
|
17
|
$self->[2] = $pos; |
|
647
|
|
|
|
|
|
|
} |
|
648
|
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
sub copyback { |
|
650
|
61
|
|
|
61
|
|
235
|
return $_[0]->[1]; |
|
651
|
|
|
|
|
|
|
} |
|
652
|
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
package Mac::Finder::DSStore::BuddyAllocator::WriteBlock; |
|
654
|
|
|
|
|
|
|
|
|
655
|
5
|
|
|
5
|
|
32
|
use strict; |
|
|
5
|
|
|
|
|
9
|
|
|
|
5
|
|
|
|
|
139
|
|
|
656
|
5
|
|
|
5
|
|
28
|
use warnings; |
|
|
5
|
|
|
|
|
10
|
|
|
|
5
|
|
|
|
|
138
|
|
|
657
|
5
|
|
|
5
|
|
23
|
use Carp; |
|
|
5
|
|
|
|
|
9
|
|
|
|
5
|
|
|
|
|
3095
|
|
|
658
|
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
# |
|
660
|
|
|
|
|
|
|
# Write blocks |
|
661
|
|
|
|
|
|
|
# |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
sub new { |
|
664
|
76
|
|
|
76
|
|
188
|
my($class, $allocator, $offset, $size) = @_; |
|
665
|
|
|
|
|
|
|
|
|
666
|
76
|
50
|
33
|
|
|
361
|
croak "Missing arguments" |
|
667
|
|
|
|
|
|
|
unless defined($offset) && defined($size); |
|
668
|
76
|
50
|
|
|
|
218
|
croak "Bad offset" |
|
669
|
|
|
|
|
|
|
if $offset <= 0; |
|
670
|
|
|
|
|
|
|
|
|
671
|
76
|
|
33
|
|
|
900
|
bless([ $allocator, undef, 0, $offset, $size ], ref $class || $class); |
|
672
|
|
|
|
|
|
|
} |
|
673
|
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
sub read { |
|
675
|
0
|
|
|
0
|
|
0
|
my($self) = @_; |
|
676
|
|
|
|
|
|
|
|
|
677
|
0
|
|
|
|
|
0
|
croak "This is a write-only block"; |
|
678
|
|
|
|
|
|
|
} |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
sub length { |
|
681
|
0
|
|
|
0
|
|
0
|
return ($_[0]->[4]); |
|
682
|
|
|
|
|
|
|
} |
|
683
|
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
sub seek { |
|
685
|
0
|
|
|
0
|
|
0
|
my($self, $pos, $whence) = @_; |
|
686
|
0
|
0
|
|
|
|
0
|
if ($whence == 0) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
687
|
0
|
|
|
|
|
0
|
$self->[2] = $pos; |
|
688
|
|
|
|
|
|
|
} elsif ($whence == 1) { |
|
689
|
0
|
|
|
|
|
0
|
$self->[2] += $pos; |
|
690
|
|
|
|
|
|
|
} elsif ($whence == 2) { |
|
691
|
0
|
|
|
|
|
0
|
$self->[2] = $self->length + $pos; |
|
692
|
|
|
|
|
|
|
} else { |
|
693
|
0
|
|
|
|
|
0
|
croak "seek: whence=$whence"; |
|
694
|
|
|
|
|
|
|
} |
|
695
|
0
|
|
|
|
|
0
|
undef $self->[1]; |
|
696
|
0
|
|
|
|
|
0
|
$self; |
|
697
|
|
|
|
|
|
|
} |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
sub write { |
|
700
|
2459
|
|
|
2459
|
|
29863
|
my($self, $what, @args) = @_;; |
|
701
|
|
|
|
|
|
|
|
|
702
|
2459
|
100
|
|
|
|
5953
|
if (!defined($self->[1])) { |
|
703
|
136
|
|
|
|
|
633
|
$self->[1] = $self->[0]->_sought($self->[2] + $self->[3]); |
|
704
|
|
|
|
|
|
|
} |
|
705
|
|
|
|
|
|
|
|
|
706
|
2459
|
100
|
|
|
|
5412
|
if (@args) { |
|
707
|
1777
|
|
|
|
|
5170
|
$what = pack($what, @args); |
|
708
|
|
|
|
|
|
|
} |
|
709
|
|
|
|
|
|
|
|
|
710
|
2459
|
|
|
|
|
4471
|
my($wlen) = CORE::length($what); |
|
711
|
|
|
|
|
|
|
|
|
712
|
2459
|
50
|
|
|
|
8853
|
croak "Writing past end of block (writing $wlen at ".($self->[2]).", end is at ".($self->[4])."), died" |
|
713
|
|
|
|
|
|
|
if $self->[2]+$wlen > $self->[4]; |
|
714
|
|
|
|
|
|
|
|
|
715
|
2459
|
|
|
|
|
8364
|
$self->[1]->write($what); |
|
716
|
2459
|
|
|
|
|
92584
|
$self->[2] += $wlen; |
|
717
|
|
|
|
|
|
|
} |
|
718
|
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
sub close { |
|
720
|
65
|
|
|
65
|
|
256
|
my($self, $fill) = @_; |
|
721
|
65
|
50
|
66
|
|
|
751
|
if (defined($fill) && $fill && $self->[2] < $self->[4]) { |
|
|
|
|
66
|
|
|
|
|
|
722
|
60
|
|
|
|
|
456
|
$self->write("\0" x ($self->[4] - $self->[2])); |
|
723
|
|
|
|
|
|
|
} |
|
724
|
65
|
|
|
|
|
161
|
undef $self->[1]; |
|
725
|
65
|
|
|
|
|
152
|
1; |
|
726
|
|
|
|
|
|
|
} |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
# |
|
729
|
|
|
|
|
|
|
# This is just here for debugging/testing purposes |
|
730
|
|
|
|
|
|
|
# |
|
731
|
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
sub copyback { |
|
733
|
60
|
|
|
60
|
|
139
|
my($self) = @_; |
|
734
|
|
|
|
|
|
|
|
|
735
|
60
|
|
|
|
|
150
|
my($r) = Mac::Finder::DSStore::BuddyAllocator::Block->new(@{$self}[0, 3, 2]); |
|
|
60
|
|
|
|
|
420
|
|
|
736
|
|
|
|
|
|
|
|
|
737
|
60
|
|
|
|
|
147
|
undef $self->[1]; # probably need to re-seek now |
|
738
|
|
|
|
|
|
|
|
|
739
|
60
|
|
|
|
|
337
|
return $r; |
|
740
|
|
|
|
|
|
|
} |
|
741
|
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
package Mac::Finder::DSStore::BuddyAllocator::StringBlock; |
|
743
|
|
|
|
|
|
|
|
|
744
|
5
|
|
|
5
|
|
50
|
use strict; |
|
|
5
|
|
|
|
|
11
|
|
|
|
5
|
|
|
|
|
180
|
|
|
745
|
5
|
|
|
5
|
|
34
|
use warnings; |
|
|
5
|
|
|
|
|
7
|
|
|
|
5
|
|
|
|
|
867
|
|
|
746
|
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
# |
|
748
|
|
|
|
|
|
|
# This one's kind of handy, really, but is only used for debugging and |
|
749
|
|
|
|
|
|
|
# test harnesses right now. |
|
750
|
|
|
|
|
|
|
# |
|
751
|
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
sub new { |
|
753
|
61
|
|
|
61
|
|
104
|
my($x) = ''; |
|
754
|
61
|
|
33
|
|
|
418
|
bless(\$x, ref $_[0] || $_[0]); |
|
755
|
|
|
|
|
|
|
} |
|
756
|
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
sub write { |
|
758
|
2005
|
|
|
2005
|
|
19990
|
my($self, $what, @args) = @_;; |
|
759
|
|
|
|
|
|
|
|
|
760
|
2005
|
100
|
|
|
|
4348
|
if (@args) { |
|
761
|
1376
|
|
|
|
|
4156
|
$what = pack($what, @args); |
|
762
|
|
|
|
|
|
|
} |
|
763
|
|
|
|
|
|
|
|
|
764
|
2005
|
|
|
|
|
2490
|
${$self} .= $what; |
|
|
2005
|
|
|
|
|
8207
|
|
|
765
|
|
|
|
|
|
|
} |
|
766
|
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
sub copyback { |
|
768
|
61
|
|
|
61
|
|
78
|
${$_[0]}; |
|
|
61
|
|
|
|
|
288
|
|
|
769
|
|
|
|
|
|
|
} |
|
770
|
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
=head1 AUTHOR |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
Written by Wim Lewis as part of the Mac::Finder::DSStore package. |
|
774
|
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
This file is copyright 2008 by Wim Lewis. |
|
776
|
|
|
|
|
|
|
All rights reserved. |
|
777
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or |
|
778
|
|
|
|
|
|
|
modify it under the same terms as Perl itself. |
|
779
|
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
=cut |
|
782
|
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
1; |