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