Profile of Archive/Zip/ZipFileMember.pm

← Index
NYTProf Performance Profile «line view»
For t/bug-md-11.t
Run on Fri Mar 8 13:27:24 2024
Reported on Fri Mar 8 13:30:23 2024

Filename | /home/micha/.plenv/versions/5.38.2/lib/perl5/site_perl/5.38.2/Archive/Zip/ZipFileMember.pm
Statements | Executed 625 statements in 1.39ms

Subroutines
Calls | P | F | Exclusive Time | Inclusive Time | Subroutine
--- | --- | --- | --- | --- | ---
10 | 1 | 1 | 98µs | 178µs | Archive::Zip::ZipFileMember::::_readCentralDirectoryFileHeaderArchive::Zip::ZipFileMember::_readCentralDirectoryFileHeader
7 | 1 | 1 | 91µs | 1.85ms | Archive::Zip::ZipFileMember::::rewindDataArchive::Zip::ZipFileMember::rewindData
7 | 1 | 1 | 72µs | 162µs | Archive::Zip::ZipFileMember::::_skipLocalFileHeaderArchive::Zip::ZipFileMember::_skipLocalFileHeader
7 | 1 | 1 | 61µs | 193µs | Archive::Zip::ZipFileMember::::_seekToLocalHeaderArchive::Zip::ZipFileMember::_seekToLocalHeader
11 | 1 | 1 | 37µs | 247µs | Archive::Zip::ZipFileMember::::_newFromZipFileArchive::Zip::ZipFileMember::_newFromZipFile
21 | 2 | 2 | 28µs | 42µs | Archive::Zip::ZipFileMember::::isDirectoryArchive::Zip::ZipFileMember::isDirectory
7 | 1 | 1 | 22µs | 591µs | Archive::Zip::ZipFileMember::::_readRawChunkArchive::Zip::ZipFileMember::_readRawChunk
1 | 1 | 1 | 10µs | 12µs | Archive::Zip::ZipFileMember::::BEGIN@3Archive::Zip::ZipFileMember::BEGIN@3
1 | 1 | 1 | 9µs | 178µs | Archive::Zip::ZipFileMember::::BEGIN@11Archive::Zip::ZipFileMember::BEGIN@11
1 | 1 | 1 | 7µs | 7µs | Archive::Zip::ZipFileMember::::BEGIN@6Archive::Zip::ZipFileMember::BEGIN@6
7 | 1 | 1 | 4µs | 4µs | Archive::Zip::ZipFileMember::::dataOffsetArchive::Zip::ZipFileMember::dataOffset
7 | 1 | 1 | 4µs | 4µs | Archive::Zip::ZipFileMember::::localHeaderRelativeOffsetArchive::Zip::ZipFileMember::localHeaderRelativeOffset
1 | 1 | 1 | 3µs | 21µs | Archive::Zip::ZipFileMember::::BEGIN@4Archive::Zip::ZipFileMember::BEGIN@4
0 | 0 | 0 | 0s | 0s | Archive::Zip::ZipFileMember::::_becomeArchive::Zip::ZipFileMember::_become
0 | 0 | 0 | 0s | 0s | Archive::Zip::ZipFileMember::::_readDataDescriptorArchive::Zip::ZipFileMember::_readDataDescriptor
0 | 0 | 0 | 0s | 0s | Archive::Zip::ZipFileMember::::_readLocalFileHeaderArchive::Zip::ZipFileMember::_readLocalFileHeader
0 | 0 | 0 | 0s | 0s | Archive::Zip::ZipFileMember::::diskNumberStartArchive::Zip::ZipFileMember::diskNumberStart

Call graph for these subroutines as a Graphviz dot language file.

Line | State ments | Time on line | Calls | Time in subs | Code
--- | --- | --- | --- | --- | ---
1 |  |  |  |  | package Archive::Zip::ZipFileMember;
2 |  |  |  |  |
3 | 2 | 21µs | 2 | 13µs | # spent 12µs (10+2) within Archive::Zip::ZipFileMember::BEGIN@3 which was called: # once (10µs+2µs) by Spreadsheet::ParseXLSX::BEGIN@11 at line 3 use strict; # spent 12µs making 1 call to Archive::Zip::ZipFileMember::BEGIN@3 # spent 2µs making 1 call to strict::import
4 | 2 | 22µs | 2 | 38µs | # spent 21µs (3+17) within Archive::Zip::ZipFileMember::BEGIN@4 which was called: # once (3µs+17µs) by Spreadsheet::ParseXLSX::BEGIN@11 at line 4 use vars qw( $VERSION @ISA ); # spent 21µs making 1 call to Archive::Zip::ZipFileMember::BEGIN@4 # spent 17µs making 1 call to vars::import
5 |  |  |  |  |
6 |  |  |  |  | # spent 7µs within Archive::Zip::ZipFileMember::BEGIN@6 which was called: # once (7µs+0s) by Spreadsheet::ParseXLSX::BEGIN@11 at line 9 BEGIN {
7 | 1 | 200ns |  |  | $VERSION = '1.68';
8 | 1 | 7µs |  |  | @ISA = qw ( Archive::Zip::FileMember );
9 | 1 | 15µs | 1 | 7µs | } # spent 7µs making 1 call to Archive::Zip::ZipFileMember::BEGIN@6
10 |  |  |  |  |
11 | 1 | 2µs | 1 | 169µs | # spent 178µs (9+169) within Archive::Zip::ZipFileMember::BEGIN@11 which was called: # once (9µs+169µs) by Spreadsheet::ParseXLSX::BEGIN@11 at line 16 use Archive::Zip qw( # spent 169µs making 1 call to Exporter::import
12 |  |  |  |  | :CONSTANTS
13 |  |  |  |  | :ERROR_CODES
14 |  |  |  |  | :PKZIP_CONSTANTS
15 |  |  |  |  | :UTILITY_METHODS
16 | 1 | 968µs | 1 | 178µs | ); # spent 178µs making 1 call to Archive::Zip::ZipFileMember::BEGIN@11
17 |  |  |  |  |
18 |  |  |  |  | # Create a new Archive::Zip::ZipFileMember
19 |  |  |  |  | # given a filename and optional open file handle
20 |  |  |  |  | #
21 |  |  |  |  | # spent 247µs (37+210) within Archive::Zip::ZipFileMember::_newFromZipFile which was called 11 times, avg 22µs/call: # 11 times (37µs+210µs) by Archive::Zip::Member::_newFromZipFile at line 47 of Archive/Zip/Member.pm, avg 22µs/call sub _newFromZipFile {
22 | 11 | 1µs |  |  | my $class = shift;
23 | 11 | 1µs |  |  | my $fh = shift;
24 | 11 | 2µs |  |  | my $externalFileName = shift;
25 | 11 | 2µs |  |  | my $archiveZip64 = @_ ? shift : 0;
26 | 11 | 1µs |  |  | my $possibleEocdOffset = @_ ? shift : 0; # normally 0
27 |  |  |  |  |
28 | 11 | 10µs | 11 | 210µs | my $self = $class->new( # spent 210µs making 11 calls to Archive::Zip::Member::new, avg 19µs/call
29 |  |  |  |  | 'eocdCrc32' => 0,
30 |  |  |  |  | 'diskNumberStart' => 0,
31 |  |  |  |  | 'localHeaderRelativeOffset' => 0,
32 |  |  |  |  | 'dataOffset' => 0, # localHeaderRelativeOffset + header length
33 |  |  |  |  | @_
34 |  |  |  |  | );
35 | 11 | 3µs |  |  | $self->{'externalFileName'} = $externalFileName;
36 | 11 | 2µs |  |  | $self->{'fh'} = $fh;
37 | 11 | 2µs |  |  | $self->{'archiveZip64'} = $archiveZip64;
38 | 11 | 2µs |  |  | $self->{'possibleEocdOffset'} = $possibleEocdOffset;
39 | 11 | 9µs |  |  | return $self;
40 |  |  |  |  | }
41 |  |  |  |  |
42 |  |  |  |  | # spent 42µs (28+13) within Archive::Zip::ZipFileMember::isDirectory which was called 21 times, avg 2µs/call: # 11 times (15µs+7µs) by Archive::Zip::Member::unixFileAttributes at line 399 of Archive/Zip/Member.pm, avg 2µs/call # 10 times (13µs+7µs) by Archive::Zip::Archive::readFromFileHandle at line 797 of Archive/Zip/Archive.pm, avg 2µs/call sub isDirectory {
43 | 21 | 2µs |  |  | my $self = shift;
44 | 21 | 28µs | 21 | 13µs | return (substr($self->fileName, -1, 1) eq '/' # spent 13µs making 21 calls to Archive::Zip::Member::fileName, avg 633ns/call
45 |  |  |  |  | and $self->uncompressedSize == 0);
46 |  |  |  |  | }
47 |  |  |  |  |
48 |  |  |  |  | # Seek to the beginning of the local header, just past the signature.
49 |  |  |  |  | # Verify that the local header signature is in fact correct.
50 |  |  |  |  | # Update the localHeaderRelativeOffset if necessary by adding the possibleEocdOffset.
51 |  |  |  |  | # Returns status.
52 |  |  |  |  |
53 |  |  |  |  | # spent 193µs (61+132) within Archive::Zip::ZipFileMember::_seekToLocalHeader which was called 7 times, avg 28µs/call: # 7 times (61µs+132µs) by Archive::Zip::ZipFileMember::rewindData at line 450, avg 28µs/call sub _seekToLocalHeader {
54 | 7 | 700ns |  |  | my $self = shift;
55 | 7 | 1µs |  |  | my $where = shift; # optional
56 | 7 | 500ns |  |  | my $previousWhere = shift; # optional
57 |  |  |  |  |
58 | 7 | 7µs | 7 | 4µs | $where = $self->localHeaderRelativeOffset() unless defined($where); # spent 4µs making 7 calls to Archive::Zip::ZipFileMember::localHeaderRelativeOffset, avg 500ns/call
59 |  |  |  |  |
60 |  |  |  |  | # avoid loop on certain corrupt files (from Julian Field)
61 | 7 | 2µs |  |  | return _formatError("corrupt zip file")
62 |  |  |  |  | if defined($previousWhere) && $where == $previousWhere;
63 |  |  |  |  |
64 | 7 | 1µs |  |  | my $status;
65 |  |  |  |  | my $signature;
66 |  |  |  |  |
67 | 7 | 9µs | 14 | 31µs | $status = $self->fh()->seek($where, IO::Seekable::SEEK_SET); # spent 20µs making 7 calls to IO::Seekable::seek, avg 3µs/call # spent 11µs making 7 calls to Archive::Zip::FileMember::fh, avg 2µs/call
68 | 7 | 1µs |  |  | return _ioError("seeking to local header") unless $status;
69 |  |  |  |  |
70 | 7 | 13µs | 21 | 97µs | ($status, $signature) = # spent 84µs making 7 calls to Archive::Zip::_readSignature, avg 12µs/call # spent 10µs making 7 calls to Archive::Zip::FileMember::fh, avg 1µs/call # spent 3µs making 7 calls to Archive::Zip::FileMember::externalFileName, avg 429ns/call
71 |  |  |  |  | _readSignature($self->fh(), $self->externalFileName(),
72 |  |  |  |  | LOCAL_FILE_HEADER_SIGNATURE, 1);
73 | 7 | 1µs |  |  | return $status if $status == AZ_IO_ERROR;
74 |  |  |  |  |
75 |  |  |  |  | # retry with EOCD offset if any was given.
76 | 7 | 1µs |  |  | if ($status == AZ_FORMAT_ERROR && $self->{'possibleEocdOffset'}) {
77 |  |  |  |  | $status = $self->_seekToLocalHeader(
78 |  |  |  |  | $self->localHeaderRelativeOffset() + $self->{'possibleEocdOffset'},
79 |  |  |  |  | $where
80 |  |  |  |  | );
81 |  |  |  |  | if ($status == AZ_OK) {
82 |  |  |  |  | $self->{'localHeaderRelativeOffset'} +=
83 |  |  |  |  | $self->{'possibleEocdOffset'};
84 |  |  |  |  | $self->{'possibleEocdOffset'} = 0;
85 |  |  |  |  | }
86 |  |  |  |  | }
87 |  |  |  |  |
88 | 7 | 8µs |  |  | return $status;
89 |  |  |  |  | }
90 |  |  |  |  |
91 |  |  |  |  | # Because I'm going to delete the file handle, read the local file
92 |  |  |  |  | # header if the file handle is seekable. If it is not, I assume that
93 |  |  |  |  | # I've already read the local header.
94 |  |  |  |  | # Return ( $status, $self )
95 |  |  |  |  |
96 |  |  |  |  | sub _become {
97 |  |  |  |  | my $self = shift;
98 |  |  |  |  | my $newClass = shift;
99 |  |  |  |  | return $self if ref($self) eq $newClass;
100 |  |  |  |  |
101 |  |  |  |  | my $status = AZ_OK;
102 |  |  |  |  |
103 |  |  |  |  | if (_isSeekable($self->fh())) {
104 |  |  |  |  | my $here = $self->fh()->tell();
105 |  |  |  |  | $status = $self->_seekToLocalHeader();
106 |  |  |  |  | $status = $self->_readLocalFileHeader() if $status == AZ_OK;
107 |  |  |  |  | $self->fh()->seek($here, IO::Seekable::SEEK_SET);
108 |  |  |  |  | return $status unless $status == AZ_OK;
109 |  |  |  |  | }
110 |  |  |  |  |
111 |  |  |  |  | delete($self->{'eocdCrc32'});
112 |  |  |  |  | delete($self->{'diskNumberStart'});
113 |  |  |  |  | delete($self->{'localHeaderRelativeOffset'});
114 |  |  |  |  | delete($self->{'dataOffset'});
115 |  |  |  |  | delete($self->{'archiveZip64'});
116 |  |  |  |  | delete($self->{'possibleEocdOffset'});
117 |  |  |  |  |
118 |  |  |  |  | return $self->SUPER::_become($newClass);
119 |  |  |  |  | }
120 |  |  |  |  |
121 |  |  |  |  | sub diskNumberStart {
122 |  |  |  |  | shift->{'diskNumberStart'};
123 |  |  |  |  | }
124 |  |  |  |  |
125 |  |  |  |  | # spent 4µs within Archive::Zip::ZipFileMember::localHeaderRelativeOffset which was called 7 times, avg 500ns/call: # 7 times (4µs+0s) by Archive::Zip::ZipFileMember::_seekToLocalHeader at line 58, avg 500ns/call sub localHeaderRelativeOffset {
126 | 7 | 7µs |  |  | shift->{'localHeaderRelativeOffset'};
127 |  |  |  |  | }
128 |  |  |  |  |
129 |  |  |  |  | # spent 4µs within Archive::Zip::ZipFileMember::dataOffset which was called 7 times, avg 557ns/call: # 7 times (4µs+0s) by Archive::Zip::ZipFileMember::rewindData at line 458, avg 557ns/call sub dataOffset {
130 | 7 | 7µs |  |  | shift->{'dataOffset'};
131 |  |  |  |  | }
132 |  |  |  |  |
133 |  |  |  |  | # Skip local file header, updating only extra field stuff.
134 |  |  |  |  | # Assumes that fh is positioned before signature.
135 |  |  |  |  | # spent 162µs (72+89) within Archive::Zip::ZipFileMember::_skipLocalFileHeader which was called 7 times, avg 23µs/call: # 7 times (72µs+89µs) by Archive::Zip::ZipFileMember::rewindData at line 454, avg 23µs/call sub _skipLocalFileHeader {
136 | 7 | 800ns |  |  | my $self = shift;
137 | 7 | 500ns |  |  | my $header;
138 | 7 | 7µs | 14 | 22µs | my $bytesRead = $self->fh()->read($header, LOCAL_FILE_HEADER_LENGTH); # spent 13µs making 7 calls to Archive::Zip::FileMember::fh, avg 2µs/call # spent 9µs making 7 calls to IO::Handle::read, avg 1µs/call
139 | 7 | 1µs |  |  | if ($bytesRead != LOCAL_FILE_HEADER_LENGTH) {
140 |  |  |  |  | return _ioError("reading local file header");
141 |  |  |  |  | }
142 | 7 | 1µs |  |  | my $fileNameLength;
143 |  |  |  |  | my $extraFieldLength;
144 |  |  |  |  | my $bitFlag;
145 |  |  |  |  | (
146 |  |  |  |  | undef, # $self->{'versionNeededToExtract'},
147 | 7 | 15µs | 7 | 5µs | $bitFlag, # spent 5µs making 7 calls to CORE::unpack, avg 657ns/call
148 |  |  |  |  | undef, # $self->{'compressionMethod'},
149 |  |  |  |  | undef, # $self->{'lastModFileDateTime'},
150 |  |  |  |  | undef, # $crc32,
151 |  |  |  |  | undef, # $compressedSize,
152 |  |  |  |  | undef, # $uncompressedSize,
153 |  |  |  |  | $fileNameLength,
154 |  |  |  |  | $extraFieldLength
155 |  |  |  |  | ) = unpack(LOCAL_FILE_HEADER_FORMAT, $header);
156 |  |  |  |  |
157 | 7 | 8µs | 14 | 34µs | if ($fileNameLength) { # spent 23µs making 7 calls to IO::Seekable::seek, avg 3µs/call # spent 11µs making 7 calls to Archive::Zip::FileMember::fh, avg 2µs/call
158 |  |  |  |  | $self->fh()->seek($fileNameLength, IO::Seekable::SEEK_CUR)
159 |  |  |  |  | or return _ioError("skipping local file name");
160 |  |  |  |  | }
161 |  |  |  |  |
162 | 7 | 1µs |  |  | my $zip64 = 0;
163 | 7 | 1µs |  |  | if ($extraFieldLength) {
164 |  |  |  |  | $bytesRead =
165 | 2 | 3µs | 4 | 9µs | $self->fh()->read($self->{'localExtraField'}, $extraFieldLength); # spent 6µs making 2 calls to IO::Handle::read, avg 3µs/call # spent 3µs making 2 calls to Archive::Zip::FileMember::fh, avg 1µs/call
166 | 2 | 1µs |  |  | if ($bytesRead != $extraFieldLength) {
167 |  |  |  |  | return _ioError("reading local extra field");
168 |  |  |  |  | }
169 | 2 | 700ns |  |  | if ($self->{'archiveZip64'}) {
170 |  |  |  |  | my $status;
171 |  |  |  |  | ($status, $zip64) =
172 |  |  |  |  | $self->_extractZip64ExtraField($self->{'localExtraField'}, undef, undef);
173 |  |  |  |  | return $status if $status != AZ_OK;
174 |  |  |  |  | $self->{'zip64'} ||= $zip64;
175 |  |  |  |  | }
176 |  |  |  |  | }
177 |  |  |  |  |
178 | 7 | 8µs | 14 | 20µs | $self->{'dataOffset'} = $self->fh()->tell(); # spent 10µs making 7 calls to Archive::Zip::FileMember::fh, avg 1µs/call # spent 10µs making 7 calls to IO::Seekable::tell, avg 1µs/call
179 |  |  |  |  |
180 | 7 | 2µs |  |  | if ($bitFlag & GPBF_HAS_DATA_DESCRIPTOR_MASK) {
181 |  |  |  |  |
182 |  |  |  |  | # Read the crc32, compressedSize, and uncompressedSize from the
183 |  |  |  |  | # extended data descriptor, which directly follows the compressed data.
184 |  |  |  |  | #
185 |  |  |  |  | # Skip over the compressed file data (assumes that EOCD compressedSize
186 |  |  |  |  | # was correct)
187 |  |  |  |  | $self->fh()->seek($self->{'compressedSize'}, IO::Seekable::SEEK_CUR)
188 |  |  |  |  | or return _ioError("seeking to extended local header");
189 |  |  |  |  |
190 |  |  |  |  | # these values should be set correctly from before.
191 |  |  |  |  | my $oldCrc32 = $self->{'eocdCrc32'};
192 |  |  |  |  | my $oldCompressedSize = $self->{'compressedSize'};
193 |  |  |  |  | my $oldUncompressedSize = $self->{'uncompressedSize'};
194 |  |  |  |  |
195 |  |  |  |  | my $status = $self->_readDataDescriptor($zip64);
196 |  |  |  |  | return $status unless $status == AZ_OK;
197 |  |  |  |  |
198 |  |  |  |  | # The buffer with encrypted data is prefixed with a new
199 |  |  |  |  | # encrypted 12 byte header. The size only changes when
200 |  |  |  |  | # the buffer is also compressed
201 |  |  |  |  | $self->isEncrypted && $oldUncompressedSize > $self->{'uncompressedSize'}
202 |  |  |  |  | and $oldUncompressedSize -= DATA_DESCRIPTOR_LENGTH;
203 |  |  |  |  |
204 |  |  |  |  | return _formatError(
205 |  |  |  |  | "CRC or size mismatch while skipping data descriptor")
206 |  |  |  |  | if ( $oldCrc32 != $self->{'crc32'}
207 |  |  |  |  | || $oldUncompressedSize != $self->{'uncompressedSize'});
208 |  |  |  |  |
209 |  |  |  |  | $self->{'crc32'} = 0
210 |  |  |  |  | if $self->compressionMethod() == COMPRESSION_STORED ;
211 |  |  |  |  | }
212 |  |  |  |  |
213 | 7 | 8µs |  |  | return AZ_OK;
214 |  |  |  |  | }
215 |  |  |  |  |
216 |  |  |  |  | # Read from a local file header into myself. Returns AZ_OK (in
217 |  |  |  |  | # scalar context) or a pair (AZ_OK, $headerSize) (in list
218 |  |  |  |  | # context) if successful.
219 |  |  |  |  | # Assumes that fh is positioned after signature.
220 |  |  |  |  | # Note that crc32, compressedSize, and uncompressedSize will be 0 if
221 |  |  |  |  | # GPBF_HAS_DATA_DESCRIPTOR_MASK is set in the bitFlag.
222 |  |  |  |  |
223 |  |  |  |  | sub _readLocalFileHeader {
224 |  |  |  |  | my $self = shift;
225 |  |  |  |  | my $header;
226 |  |  |  |  | my $bytesRead = $self->fh()->read($header, LOCAL_FILE_HEADER_LENGTH);
227 |  |  |  |  | if ($bytesRead != LOCAL_FILE_HEADER_LENGTH) {
228 |  |  |  |  | return _ioError("reading local file header");
229 |  |  |  |  | }
230 |  |  |  |  | my $fileNameLength;
231 |  |  |  |  | my $crc32;
232 |  |  |  |  | my $compressedSize;
233 |  |  |  |  | my $uncompressedSize;
234 |  |  |  |  | my $extraFieldLength;
235 |  |  |  |  | (
236 |  |  |  |  | $self->{'versionNeededToExtract'}, $self->{'bitFlag'},
237 |  |  |  |  | $self->{'compressionMethod'}, $self->{'lastModFileDateTime'},
238 |  |  |  |  | $crc32, $compressedSize,
239 |  |  |  |  | $uncompressedSize, $fileNameLength,
240 |  |  |  |  | $extraFieldLength
241 |  |  |  |  | ) = unpack(LOCAL_FILE_HEADER_FORMAT, $header);
242 |  |  |  |  |
243 |  |  |  |  | if ($fileNameLength) {
244 |  |  |  |  | my $fileName;
245 |  |  |  |  | $bytesRead = $self->fh()->read($fileName, $fileNameLength);
246 |  |  |  |  | if ($bytesRead != $fileNameLength) {
247 |  |  |  |  | return _ioError("reading local file name");
248 |  |  |  |  | }
249 |  |  |  |  | $self->fileName($fileName);
250 |  |  |  |  | }
251 |  |  |  |  |
252 |  |  |  |  | my $zip64 = 0;
253 |  |  |  |  | if ($extraFieldLength) {
254 |  |  |  |  | $bytesRead =
255 |  |  |  |  | $self->fh()->read($self->{'localExtraField'}, $extraFieldLength);
256 |  |  |  |  | if ($bytesRead != $extraFieldLength) {
257 |  |  |  |  | return _ioError("reading local extra field");
258 |  |  |  |  | }
259 |  |  |  |  | if ($self->{'archiveZip64'}) {
260 |  |  |  |  | my $status;
261 |  |  |  |  | ($status, $zip64) =
262 |  |  |  |  | $self->_extractZip64ExtraField($self->{'localExtraField'},
263 |  |  |  |  | $uncompressedSize,
264 |  |  |  |  | $compressedSize);
265 |  |  |  |  | return $status if $status != AZ_OK;
266 |  |  |  |  | $self->{'zip64'} ||= $zip64;
267 |  |  |  |  | }
268 |  |  |  |  | }
269 |  |  |  |  |
270 |  |  |  |  | $self->{'dataOffset'} = $self->fh()->tell();
271 |  |  |  |  |
272 |  |  |  |  | if ($self->hasDataDescriptor()) {
273 |  |  |  |  |
274 |  |  |  |  | # Read the crc32, compressedSize, and uncompressedSize from the
275 |  |  |  |  | # extended data descriptor.
276 |  |  |  |  | # Skip over the compressed file data (assumes that EOCD compressedSize
277 |  |  |  |  | # was correct)
278 |  |  |  |  | $self->fh()->seek($self->{'compressedSize'}, IO::Seekable::SEEK_CUR)
279 |  |  |  |  | or return _ioError("seeking to extended local header");
280 |  |  |  |  |
281 |  |  |  |  | my $status = $self->_readDataDescriptor($zip64);
282 |  |  |  |  | return $status unless $status == AZ_OK;
283 |  |  |  |  | } else {
284 |  |  |  |  | return _formatError(
285 |  |  |  |  | "CRC or size mismatch after reading data descriptor")
286 |  |  |  |  | if ( $self->{'crc32'} != $crc32
287 |  |  |  |  | || $self->{'uncompressedSize'} != $uncompressedSize);
288 |  |  |  |  | }
289 |  |  |  |  |
290 |  |  |  |  | return
291 |  |  |  |  | wantarray
292 |  |  |  |  | ? (AZ_OK,
293 |  |  |  |  | SIGNATURE_LENGTH,
294 |  |  |  |  | LOCAL_FILE_HEADER_LENGTH +
295 |  |  |  |  | $fileNameLength +
296 |  |  |  |  | $extraFieldLength)
297 |  |  |  |  | : AZ_OK;
298 |  |  |  |  | }
299 |  |  |  |  |
300 |  |  |  |  | # This will read the data descriptor, which is after the end of compressed file
301 |  |  |  |  | # data in members that have GPBF_HAS_DATA_DESCRIPTOR_MASK set in their bitFlag.
302 |  |  |  |  | # The only reliable way to find these is to rely on the EOCD compressedSize.
303 |  |  |  |  | # Assumes that file is positioned immediately after the compressed data.
304 |  |  |  |  | # Returns status; sets crc32, compressedSize, and uncompressedSize.
305 |  |  |  |  | sub _readDataDescriptor {
306 |  |  |  |  | my $self = shift;
307 |  |  |  |  | my $zip64 = shift;
308 |  |  |  |  | my $signatureData;
309 |  |  |  |  | my $header;
310 |  |  |  |  | my $crc32;
311 |  |  |  |  | my $compressedSize;
312 |  |  |  |  | my $uncompressedSize;
313 |  |  |  |  |
314 |  |  |  |  | my $bytesRead = $self->fh()->read($signatureData, SIGNATURE_LENGTH);
315 |  |  |  |  | return _ioError("reading header signature")
316 |  |  |  |  | if $bytesRead != SIGNATURE_LENGTH;
317 |  |  |  |  | my $signature = unpack(SIGNATURE_FORMAT, $signatureData);
318 |  |  |  |  |
319 |  |  |  |  | my $dataDescriptorLength;
320 |  |  |  |  | my $dataDescriptorFormat;
321 |  |  |  |  | my $dataDescriptorLengthNoSig;
322 |  |  |  |  | my $dataDescriptorFormatNoSig;
323 |  |  |  |  | if (! $zip64) {
324 |  |  |  |  | $dataDescriptorLength = DATA_DESCRIPTOR_LENGTH;
325 |  |  |  |  | $dataDescriptorFormat = DATA_DESCRIPTOR_FORMAT;
326 |  |  |  |  | $dataDescriptorLengthNoSig = DATA_DESCRIPTOR_LENGTH_NO_SIG;
327 |  |  |  |  | $dataDescriptorFormatNoSig = DATA_DESCRIPTOR_FORMAT_NO_SIG
328 |  |  |  |  | }
329 |  |  |  |  | else {
330 |  |  |  |  | $dataDescriptorLength = DATA_DESCRIPTOR_ZIP64_LENGTH;
331 |  |  |  |  | $dataDescriptorFormat = DATA_DESCRIPTOR_ZIP64_FORMAT;
332 |  |  |  |  | $dataDescriptorLengthNoSig = DATA_DESCRIPTOR_ZIP64_LENGTH_NO_SIG;
333 |  |  |  |  | $dataDescriptorFormatNoSig = DATA_DESCRIPTOR_ZIP64_FORMAT_NO_SIG
334 |  |  |  |  | }
335 |  |  |  |  |
336 |  |  |  |  | # unfortunately, the signature appears to be optional.
337 |  |  |  |  | if ($signature == DATA_DESCRIPTOR_SIGNATURE
338 |  |  |  |  | && ($signature != $self->{'crc32'})) {
339 |  |  |  |  | $bytesRead = $self->fh()->read($header, $dataDescriptorLength);
340 |  |  |  |  | return _ioError("reading data descriptor")
341 |  |  |  |  | if $bytesRead != $dataDescriptorLength;
342 |  |  |  |  |
343 |  |  |  |  | ($crc32, $compressedSize, $uncompressedSize) =
344 |  |  |  |  | unpack($dataDescriptorFormat, $header);
345 |  |  |  |  | } else {
346 |  |  |  |  | $bytesRead = $self->fh()->read($header, $dataDescriptorLengthNoSig);
347 |  |  |  |  | return _ioError("reading data descriptor")
348 |  |  |  |  | if $bytesRead != $dataDescriptorLengthNoSig;
349 |  |  |  |  |
350 |  |  |  |  | $crc32 = $signature;
351 |  |  |  |  | ($compressedSize, $uncompressedSize) =
352 |  |  |  |  | unpack($dataDescriptorFormatNoSig, $header);
353 |  |  |  |  | }
354 |  |  |  |  |
355 |  |  |  |  | $self->{'eocdCrc32'} = $self->{'crc32'}
356 |  |  |  |  | unless defined($self->{'eocdCrc32'});
357 |  |  |  |  | $self->{'crc32'} = $crc32;
358 |  |  |  |  | $self->{'compressedSize'} = $compressedSize;
359 |  |  |  |  | $self->{'uncompressedSize'} = $uncompressedSize;
360 |  |  |  |  |
361 |  |  |  |  | return AZ_OK;
362 |  |  |  |  | }
363 |  |  |  |  |
364 |  |  |  |  | # Read a Central Directory header. Return AZ_OK on success.
365 |  |  |  |  | # Assumes that fh is positioned right after the signature.
366 |  |  |  |  |
367 |  |  |  |  | # spent 178µs (98+80) within Archive::Zip::ZipFileMember::_readCentralDirectoryFileHeader which was called 10 times, avg 18µs/call: # 10 times (98µs+80µs) by Archive::Zip::Archive::readFromFileHandle at line 792 of Archive/Zip/Archive.pm, avg 18µs/call sub _readCentralDirectoryFileHeader {
368 | 10 | 400ns |  |  | my $self = shift;
369 | 10 | 6µs | 10 | 23µs | my $fh = $self->fh(); # spent 23µs making 10 calls to Archive::Zip::FileMember::fh, avg 2µs/call
370 | 10 | 1µs |  |  | my $header = '';
371 | 10 | 5µs | 10 | 12µs | my $bytesRead = $fh->read($header, CENTRAL_DIRECTORY_FILE_HEADER_LENGTH); # spent 12µs making 10 calls to IO::Handle::read, avg 1µs/call
372 | 10 | 1µs |  |  | if ($bytesRead != CENTRAL_DIRECTORY_FILE_HEADER_LENGTH) {
373 |  |  |  |  | return _ioError("reading central dir header");
374 |  |  |  |  | }
375 | 10 | 1µs |  |  | my ($fileNameLength, $extraFieldLength, $fileCommentLength);
376 |  |  |  |  | (
377 |  |  |  |  | $self->{'versionMadeBy'},
378 |  |  |  |  | $self->{'fileAttributeFormat'},
379 |  |  |  |  | $self->{'versionNeededToExtract'},
380 |  |  |  |  | $self->{'bitFlag'},
381 |  |  |  |  | $self->{'compressionMethod'},
382 |  |  |  |  | $self->{'lastModFileDateTime'},
383 |  |  |  |  | $self->{'crc32'},
384 |  |  |  |  | $self->{'compressedSize'},
385 |  |  |  |  | $self->{'uncompressedSize'},
386 |  |  |  |  | $fileNameLength,
387 |  |  |  |  | $extraFieldLength,
388 |  |  |  |  | $fileCommentLength,
389 |  |  |  |  | $self->{'diskNumberStart'},
390 |  |  |  |  | $self->{'internalFileAttributes'},
391 |  |  |  |  | $self->{'externalFileAttributes'},
392 | 10 | 25µs | 10 | 5µs | $self->{'localHeaderRelativeOffset'} # spent 5µs making 10 calls to CORE::unpack, avg 540ns/call
393 |  |  |  |  | ) = unpack(CENTRAL_DIRECTORY_FILE_HEADER_FORMAT, $header);
394 |  |  |  |  |
395 | 10 | 2µs |  |  | $self->{'eocdCrc32'} = $self->{'crc32'};
396 |  |  |  |  |
397 | 10 | 2µs |  |  | if ($fileNameLength) {
398 | 10 | 5µs | 10 | 11µs | $bytesRead = $fh->read($self->{'fileName'}, $fileNameLength); # spent 11µs making 10 calls to IO::Handle::read, avg 1µs/call
399 | 10 | 1µs |  |  | if ($bytesRead != $fileNameLength) {
400 |  |  |  |  | _ioError("reading central dir filename");
401 |  |  |  |  | }
402 |  |  |  |  | }
403 | 10 | 1µs |  |  | if ($extraFieldLength) {
404 |  |  |  |  | $bytesRead = $fh->read($self->{'cdExtraField'}, $extraFieldLength);
405 |  |  |  |  | if ($bytesRead != $extraFieldLength) {
406 |  |  |  |  | return _ioError("reading central dir extra field");
407 |  |  |  |  | }
408 |  |  |  |  | if ($self->{'archiveZip64'}) {
409 |  |  |  |  | my ($status, $zip64) =
410 |  |  |  |  | $self->_extractZip64ExtraField($self->{'cdExtraField'},
411 |  |  |  |  | $self->{'uncompressedSize'},
412 |  |  |  |  | $self->{'compressedSize'},
413 |  |  |  |  | $self->{'localHeaderRelativeOffset'},
414 |  |  |  |  | $self->{'diskNumberStart'});
415 |  |  |  |  | return $status if $status != AZ_OK;
416 |  |  |  |  | $self->{'zip64'} ||= $zip64;
417 |  |  |  |  | }
418 |  |  |  |  | }
419 | 10 | 900ns |  |  | if ($fileCommentLength) {
420 |  |  |  |  | $bytesRead = $fh->read($self->{'fileComment'}, $fileCommentLength);
421 |  |  |  |  | if ($bytesRead != $fileCommentLength) {
422 |  |  |  |  | return _ioError("reading central dir file comment");
423 |  |  |  |  | }
424 |  |  |  |  | }
425 |  |  |  |  |
426 |  |  |  |  | # NK 10/21/04: added to avoid problems with manipulated headers
427 | 10 | 3µs |  |  | if ( $self->{'uncompressedSize'} != $self->{'compressedSize'}
428 |  |  |  |  | and $self->{'compressionMethod'} == COMPRESSION_STORED) {
429 |  |  |  |  | $self->{'uncompressedSize'} = $self->{'compressedSize'};
430 |  |  |  |  | }
431 |  |  |  |  |
432 | 10 | 11µs | 20 | 29µs | $self->desiredCompressionMethod($self->compressionMethod()); # spent 24µs making 10 calls to Archive::Zip::Member::desiredCompressionMethod, avg 2µs/call # spent 5µs making 10 calls to Archive::Zip::Member::compressionMethod, avg 470ns/call
433 |  |  |  |  |
434 | 10 | 9µs |  |  | return AZ_OK;
435 |  |  |  |  | }
436 |  |  |  |  |
437 |  |  |  |  | # spent 1.85ms (91µs+1.76) within Archive::Zip::ZipFileMember::rewindData which was called 7 times, avg 264µs/call: # 7 times (91µs+1.76ms) by Archive::Zip::Member::contents at line 1290 of Archive/Zip/Member.pm, avg 264µs/call sub rewindData {
438 | 7 | 800ns |  |  | my $self = shift;
439 |  |  |  |  |
440 | 7 | 7µs | 7 | 912µs | my $status = $self->SUPER::rewindData(@_); # spent 912µs making 7 calls to Archive::Zip::Member::rewindData, avg 130µs/call
441 | 7 | 2µs |  |  | return $status unless $status == AZ_OK;
442 |  |  |  |  |
443 | 7 | 6µs | 7 | 425µs | return AZ_IO_ERROR unless $self->fh(); # spent 425µs making 7 calls to Archive::Zip::FileMember::fh, avg 61µs/call
444 |  |  |  |  |
445 | 7 | 23µs | 14 | 24µs | $self->fh()->clearerr(); # spent 18µs making 7 calls to Archive::Zip::FileMember::fh, avg 2µs/call # spent 7µs making 7 calls to IO::Handle::clearerr, avg 957ns/call
446 |  |  |  |  |
447 |  |  |  |  | # Seek to local file header.
448 |  |  |  |  | # The only reason that I'm doing this this way is that the extraField
449 |  |  |  |  | # length seems to be different between the CD header and the LF header.
450 | 7 | 6µs | 7 | 193µs | $status = $self->_seekToLocalHeader(); # spent 193µs making 7 calls to Archive::Zip::ZipFileMember::_seekToLocalHeader, avg 28µs/call
451 | 7 | 1µs |  |  | return $status unless $status == AZ_OK;
452 |  |  |  |  |
453 |  |  |  |  | # skip local file header
454 | 7 | 7µs | 7 | 162µs | $status = $self->_skipLocalFileHeader(); # spent 162µs making 7 calls to Archive::Zip::ZipFileMember::_skipLocalFileHeader, avg 23µs/call
455 | 7 | 1µs |  |  | return $status unless $status == AZ_OK;
456 |  |  |  |  |
457 |  |  |  |  | # Seek to beginning of file data
458 | 7 | 10µs | 21 | 42µs | $self->fh()->seek($self->dataOffset(), IO::Seekable::SEEK_SET) # spent 21µs making 7 calls to Archive::Zip::FileMember::fh, avg 3µs/call # spent 17µs making 7 calls to IO::Seekable::seek, avg 2µs/call # spent 4µs making 7 calls to Archive::Zip::ZipFileMember::dataOffset, avg 557ns/call
459 |  |  |  |  | or return _ioError("seeking to beginning of file data");
460 |  |  |  |  |
461 | 7 | 8µs |  |  | return AZ_OK;
462 |  |  |  |  | }
463 |  |  |  |  |
464 |  |  |  |  | # Return bytes read. Note that first parameter is a ref to a buffer.
465 |  |  |  |  | # my $data;
466 |  |  |  |  | # my ( $bytesRead, $status) = $self->readRawChunk( \$data, $chunkSize );
467 |  |  |  |  | # spent 591µs (22+569) within Archive::Zip::ZipFileMember::_readRawChunk which was called 7 times, avg 84µs/call: # 7 times (22µs+569µs) by Archive::Zip::Member::readChunk at line 1118 of Archive/Zip/Member.pm, avg 84µs/call sub _readRawChunk {
468 | 7 | 2µs |  |  | my ($self, $dataRef, $chunkSize) = @_;
469 | 7 | 1µs |  |  | return (0, AZ_OK) unless $chunkSize;
470 | 7 | 7µs | 14 | 569µs | my $bytesRead = $self->fh()->read($$dataRef, $chunkSize) # spent 558µs making 7 calls to IO::Handle::read, avg 80µs/call # spent 11µs making 7 calls to Archive::Zip::FileMember::fh, avg 2µs/call
471 |  |  |  |  | or return (0, _ioError("reading data"));
472 | 7 | 7µs |  |  | return ($bytesRead, AZ_OK);
473 |  |  |  |  | }
474 |  |  |  |  |
475 | 1 | 2µs |  |  | 1;