Filename | /usr/share/perl5/MARC/File/USMARC.pm |
Statements | Executed 15667 statements in 47.1ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
25 | 1 | 1 | 42.8ms | 138ms | decode | MARC::File::USMARC::
1707 | 6 | 1 | 3.15ms | 3.15ms | CORE:match (opcode) | MARC::File::USMARC::
544 | 1 | 1 | 1.53ms | 1.53ms | CORE:unpack (opcode) | MARC::File::USMARC::
1 | 1 | 1 | 926µs | 1.09ms | BEGIN@15 | MARC::File::USMARC::
1 | 1 | 1 | 287µs | 9.53ms | BEGIN@13 | MARC::File::USMARC::
1 | 1 | 1 | 19µs | 24µs | BEGIN@9 | MARC::File::USMARC::
1 | 1 | 1 | 15µs | 75µs | BEGIN@20 | MARC::File::USMARC::
1 | 1 | 1 | 13µs | 16µs | BEGIN@10 | MARC::File::USMARC::
1 | 1 | 1 | 13µs | 42µs | BEGIN@16 | MARC::File::USMARC::
1 | 1 | 1 | 12µs | 57µs | BEGIN@18 | MARC::File::USMARC::
1 | 1 | 1 | 11µs | 44µs | BEGIN@22 | MARC::File::USMARC::
1 | 1 | 1 | 9µs | 45µs | BEGIN@21 | MARC::File::USMARC::
1 | 1 | 1 | 9µs | 39µs | BEGIN@12 | MARC::File::USMARC::
1 | 1 | 1 | 9µs | 9µs | BEGIN@19 | MARC::File::USMARC::
1 | 1 | 1 | 9µs | 40µs | BEGIN@23 | MARC::File::USMARC::
0 | 0 | 0 | 0s | 0s | _build_tag_directory | MARC::File::USMARC::
0 | 0 | 0 | 0s | 0s | _next | MARC::File::USMARC::
0 | 0 | 0 | 0s | 0s | encode | MARC::File::USMARC::
0 | 0 | 0 | 0s | 0s | update_leader | MARC::File::USMARC::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package MARC::File::USMARC; | ||||
2 | |||||
3 | =head1 NAME | ||||
4 | |||||
- - | |||||
9 | 3 | 36µs | 2 | 29µs | # spent 24µs (19+5) within MARC::File::USMARC::BEGIN@9 which was called:
# once (19µs+5µs) by C4::Biblio::BEGIN@28 at line 9 # spent 24µs making 1 call to MARC::File::USMARC::BEGIN@9
# spent 5µs making 1 call to strict::import |
10 | 3 | 31µs | 2 | 20µs | # spent 16µs (13+4) within MARC::File::USMARC::BEGIN@10 which was called:
# once (13µs+4µs) by C4::Biblio::BEGIN@28 at line 10 # spent 16µs making 1 call to MARC::File::USMARC::BEGIN@10
# spent 4µs making 1 call to integer::import |
11 | |||||
12 | 3 | 32µs | 2 | 69µs | # spent 39µs (9+30) within MARC::File::USMARC::BEGIN@12 which was called:
# once (9µs+30µs) by C4::Biblio::BEGIN@28 at line 12 # spent 39µs making 1 call to MARC::File::USMARC::BEGIN@12
# spent 30µs making 1 call to vars::import |
13 | 3 | 161µs | 2 | 9.61ms | # spent 9.53ms (287µs+9.24) within MARC::File::USMARC::BEGIN@13 which was called:
# once (287µs+9.24ms) by C4::Biblio::BEGIN@28 at line 13 # spent 9.53ms making 1 call to MARC::File::USMARC::BEGIN@13
# spent 85µs making 1 call to Exporter::import |
14 | |||||
15 | 3 | 150µs | 1 | 1.09ms | # spent 1.09ms (926µs+160µs) within MARC::File::USMARC::BEGIN@15 which was called:
# once (926µs+160µs) by C4::Biblio::BEGIN@28 at line 15 # spent 1.09ms making 1 call to MARC::File::USMARC::BEGIN@15 |
16 | 4 | 59µs | 2 | 72µs | # spent 42µs (13+30) within MARC::File::USMARC::BEGIN@16 which was called:
# once (13µs+30µs) by C4::Biblio::BEGIN@28 at line 16 # spent 42µs making 1 call to MARC::File::USMARC::BEGIN@16
# spent 30µs making 1 call to vars::import |
17 | |||||
18 | 3 | 34µs | 2 | 102µs | # spent 57µs (12+45) within MARC::File::USMARC::BEGIN@18 which was called:
# once (12µs+45µs) by C4::Biblio::BEGIN@28 at line 18 # spent 57µs making 1 call to MARC::File::USMARC::BEGIN@18
# spent 45µs making 1 call to Exporter::import |
19 | 3 | 37µs | 1 | 9µs | # spent 9µs within MARC::File::USMARC::BEGIN@19 which was called:
# once (9µs+0s) by C4::Biblio::BEGIN@28 at line 19 # spent 9µs making 1 call to MARC::File::USMARC::BEGIN@19 |
20 | 3 | 44µs | 2 | 135µs | # spent 75µs (15+60) within MARC::File::USMARC::BEGIN@20 which was called:
# once (15µs+60µs) by C4::Biblio::BEGIN@28 at line 20 # spent 75µs making 1 call to MARC::File::USMARC::BEGIN@20
# spent 60µs making 1 call to constant::import |
21 | 3 | 37µs | 2 | 80µs | # spent 45µs (9+35) within MARC::File::USMARC::BEGIN@21 which was called:
# once (9µs+35µs) by C4::Biblio::BEGIN@28 at line 21 # spent 45µs making 1 call to MARC::File::USMARC::BEGIN@21
# spent 35µs making 1 call to constant::import |
22 | 3 | 32µs | 2 | 78µs | # spent 44µs (11+34) within MARC::File::USMARC::BEGIN@22 which was called:
# once (11µs+34µs) by C4::Biblio::BEGIN@28 at line 22 # spent 44µs making 1 call to MARC::File::USMARC::BEGIN@22
# spent 34µs making 1 call to constant::import |
23 | 3 | 1.19ms | 2 | 72µs | # spent 40µs (9+31) within MARC::File::USMARC::BEGIN@23 which was called:
# once (9µs+31µs) by C4::Biblio::BEGIN@28 at line 23 # spent 40µs making 1 call to MARC::File::USMARC::BEGIN@23
# spent 31µs making 1 call to constant::import |
24 | |||||
25 | =head1 SYNOPSIS | ||||
26 | |||||
- - | |||||
45 | sub _next { | ||||
46 | my $self = shift; | ||||
47 | my $fh = $self->{fh}; | ||||
48 | |||||
49 | my $reclen; | ||||
50 | return if eof($fh); | ||||
51 | |||||
52 | local $/ = END_OF_RECORD; | ||||
53 | my $usmarc = <$fh>; | ||||
54 | |||||
55 | # remove illegal garbage that sometimes occurs between records | ||||
56 | $usmarc =~ s/^[ \x00\x0a\x0d\x1a]+//; | ||||
57 | |||||
58 | return $usmarc; | ||||
59 | } | ||||
60 | |||||
61 | =head2 decode( $string [, \&filter_func ] ) | ||||
62 | |||||
- - | |||||
97 | # spent 138ms (42.8+95.1) within MARC::File::USMARC::decode which was called 25 times, avg 5.51ms/call:
# 25 times (42.8ms+95.1ms) by C4::Search::searchResults at line 1701 of /usr/share/koha/lib/C4/Search.pm, avg 5.51ms/call | ||||
98 | |||||
99 | 500 | 2.91ms | my $text; | ||
100 | my $location = ''; | ||||
101 | |||||
102 | ## decode can be called in a variety of ways | ||||
103 | ## $object->decode( $string ) | ||||
104 | ## MARC::File::USMARC->decode( $string ) | ||||
105 | ## MARC::File::USMARC::decode( $string ) | ||||
106 | ## this bit of code covers all three | ||||
107 | |||||
108 | my $self = shift; | ||||
109 | 50 | 650µs | 25 | 100µs | if ( ref($self) =~ /^MARC::File/ ) { # spent 100µs making 25 calls to MARC::File::USMARC::CORE:match, avg 4µs/call |
110 | $location = 'in record '.$self->{recnum}; | ||||
111 | $text = shift; | ||||
112 | } else { | ||||
113 | $location = 'in record 1'; | ||||
114 | 25 | 80µs | $text = $self=~/MARC::File/ ? shift : $self; # spent 80µs making 25 calls to MARC::File::USMARC::CORE:match, avg 3µs/call | ||
115 | } | ||||
116 | my $filter_func = shift; | ||||
117 | |||||
118 | # ok this the empty shell we will fill | ||||
119 | 25 | 569µs | my $marc = MARC::Record->new(); # spent 569µs making 25 calls to MARC::Record::new, avg 23µs/call | ||
120 | |||||
121 | # Check for an all-numeric record length | ||||
122 | 25 | 255µs | ($text =~ /^(\d{5})/) # spent 255µs making 25 calls to MARC::File::USMARC::CORE:match, avg 10µs/call | ||
123 | or return $marc->_warn( "Record length \"", substr( $text, 0, 5 ), "\" is not numeric $location" ); | ||||
124 | |||||
125 | my $reclen = $1; | ||||
126 | 25 | 831µs | my $realLength = bytes::length( $text ); # spent 658µs making 1 call to bytes::AUTOLOAD
# spent 174µs making 24 calls to bytes::length, avg 7µs/call | ||
127 | $marc->_warn( "Invalid record length $location: Leader says $reclen " . | ||||
128 | "bytes but it's actually $realLength" ) unless $reclen == $realLength; | ||||
129 | |||||
130 | (substr($text, -1, 1) eq END_OF_RECORD) | ||||
131 | or $marc->_warn( "Invalid record terminator $location" ); | ||||
132 | |||||
133 | 25 | 370µs | $marc->leader( substr( $text, 0, LEADER_LEN ) ); # spent 370µs making 25 calls to MARC::Record::leader, avg 15µs/call | ||
134 | |||||
135 | # bytes 12 - 16 of leader give offset to the body of the record | ||||
136 | 25 | 187µs | my $data_start = 0 + bytes::substr( $text, 12, 5 ); # spent 187µs making 25 calls to bytes::substr, avg 7µs/call | ||
137 | |||||
138 | # immediately after the leader comes the directory (no separator) | ||||
139 | my $dir = substr( $text, LEADER_LEN, $data_start - LEADER_LEN - 1 ); # -1 to allow for \x1e at end of directory | ||||
140 | |||||
141 | # character after the directory must be \x1e | ||||
142 | (substr($text, $data_start-1, 1) eq END_OF_FIELD) | ||||
143 | or $marc->_warn( "No directory found $location" ); | ||||
144 | |||||
145 | # all directory entries 12 bytes long, so length % 12 must be 0 | ||||
146 | (length($dir) % DIRECTORY_ENTRY_LEN == 0) | ||||
147 | or $marc->_warn( "Invalid directory length $location" ); | ||||
148 | |||||
149 | |||||
150 | # go through all the fields | ||||
151 | my $nfields = length($dir)/DIRECTORY_ENTRY_LEN; | ||||
152 | 6528 | 23.6ms | for ( my $n = 0; $n < $nfields; $n++ ) { | ||
153 | 544 | 1.53ms | my ( $tagno, $len, $offset ) = unpack( "A3 A4 A5", substr($dir, $n*DIRECTORY_ENTRY_LEN, DIRECTORY_ENTRY_LEN) ); # spent 1.53ms making 544 calls to MARC::File::USMARC::CORE:unpack, avg 3µs/call | ||
154 | |||||
155 | # Check directory validity | ||||
156 | 544 | 1.37ms | ($tagno =~ /^[0-9A-Za-z]{3}$/) # spent 1.37ms making 544 calls to MARC::File::USMARC::CORE:match, avg 3µs/call | ||
157 | or $marc->_warn( "Invalid tag in directory $location: \"$tagno\"" ); | ||||
158 | |||||
159 | 544 | 695µs | ($len =~ /^\d{4}$/) # spent 695µs making 544 calls to MARC::File::USMARC::CORE:match, avg 1µs/call | ||
160 | or $marc->_warn( "Invalid length in directory $location tag $tagno: \"$len\"" ); | ||||
161 | |||||
162 | 544 | 654µs | ($offset =~ /^\d{5}$/) # spent 654µs making 544 calls to MARC::File::USMARC::CORE:match, avg 1µs/call | ||
163 | or $marc->_warn( "Invalid offset in directory $location tag $tagno: \"$offset\"" ); | ||||
164 | |||||
165 | ($offset + $len <= $reclen) | ||||
166 | or $marc->_warn( "Directory entry $location runs off the end of the record tag $tagno" ); | ||||
167 | |||||
168 | 544 | 2.66ms | my $tagdata = bytes::substr( $text, $data_start+$offset, $len ); # spent 2.66ms making 544 calls to bytes::substr, avg 5µs/call | ||
169 | |||||
170 | # if utf8 the we encode the string as utf8 | ||||
171 | 1088 | 38.3ms | if ( $marc->encoding() eq 'UTF-8' ) { # spent 31.6ms making 544 calls to MARC::File::Encode::marc_to_utf8, avg 58µs/call
# spent 6.70ms making 544 calls to MARC::Record::encoding, avg 12µs/call | ||
172 | $tagdata = marc_to_utf8( $tagdata ); | ||||
173 | } | ||||
174 | |||||
175 | 544 | 1.41ms | $marc->_warn( "Invalid length in directory for tag $tagno $location" ) # spent 1.41ms making 544 calls to bytes::length, avg 3µs/call | ||
176 | unless ( $len == bytes::length($tagdata) ); | ||||
177 | |||||
178 | 1088 | 1.04ms | if ( substr($tagdata, -1, 1) eq END_OF_FIELD ) { | ||
179 | # get rid of the end-of-tag character | ||||
180 | chop $tagdata; | ||||
181 | --$len; | ||||
182 | } else { | ||||
183 | $marc->_warn( "field does not end in end of field character in tag $tagno $location" ); | ||||
184 | } | ||||
185 | |||||
186 | warn "Specs: ", join( "|", $tagno, $len, $offset, $tagdata ), "\n" if $MARC::Record::DEBUG; | ||||
187 | |||||
188 | if ( $filter_func ) { | ||||
189 | next unless $filter_func->( $tagno, $tagdata ); | ||||
190 | } | ||||
191 | |||||
192 | 5120 | 12.7ms | 608 | 8.72ms | if ( MARC::Field->is_controlfield_tag($tagno) ) { # spent 6.43ms making 544 calls to MARC::Field::is_controlfield_tag, avg 12µs/call
# spent 1.30ms making 32 calls to MARC::Field::new, avg 41µs/call
# spent 990µs making 32 calls to MARC::Record::append_fields, avg 31µs/call |
193 | $marc->append_fields( MARC::Field->new( $tagno, $tagdata ) ); | ||||
194 | } else { | ||||
195 | my @subfields = split( SUBFIELD_INDICATOR, $tagdata ); | ||||
196 | my $indicators = shift @subfields; | ||||
197 | my ($ind1, $ind2); | ||||
198 | |||||
199 | 1024 | 858µs | if ( length( $indicators ) > 2 or length( $indicators ) == 0 ) { | ||
200 | $marc->_warn( "Invalid indicators \"$indicators\" forced to blanks $location for tag $tagno\n" ); | ||||
201 | ($ind1,$ind2) = (" ", " "); | ||||
202 | } else { | ||||
203 | $ind1 = substr( $indicators,0, 1 ); | ||||
204 | $ind2 = substr( $indicators,1, 1 ); | ||||
205 | } | ||||
206 | |||||
207 | # Split the subfield data into subfield name and data pairs | ||||
208 | my @subfield_data; | ||||
209 | for ( @subfields ) { | ||||
210 | 1319 | 3.57ms | if ( length > 0 ) { | ||
211 | push( @subfield_data, substr($_,0,1),substr($_,1) ); | ||||
212 | } else { | ||||
213 | $marc->_warn( "Entirely empty subfield found in tag $tagno" ); | ||||
214 | } | ||||
215 | } | ||||
216 | |||||
217 | if ( !@subfield_data ) { | ||||
218 | $marc->_warn( "no subfield data found $location for tag $tagno" ); | ||||
219 | next; | ||||
220 | } | ||||
221 | |||||
222 | 512 | 26.7ms | my $field = MARC::Field->new($tagno, $ind1, $ind2, @subfield_data ); # spent 26.7ms making 512 calls to MARC::Field::new, avg 52µs/call | ||
223 | 512 | 1.98ms | if ( $field->warnings() ) { # spent 1.98ms making 512 calls to MARC::Field::warnings, avg 4µs/call | ||
224 | $marc->_warn( $field->warnings() ); | ||||
225 | } | ||||
226 | 512 | 8.61ms | $marc->append_fields( $field ); # spent 8.61ms making 512 calls to MARC::Record::append_fields, avg 17µs/call | ||
227 | } | ||||
228 | } # looping through all the fields | ||||
229 | |||||
230 | |||||
231 | return $marc; | ||||
232 | } | ||||
233 | |||||
234 | =head2 update_leader() | ||||
235 | |||||
- - | |||||
243 | sub update_leader() { | ||||
244 | my $self = shift; | ||||
245 | |||||
246 | my (undef,undef,$reclen,$baseaddress) = $self->_build_tag_directory(); | ||||
247 | |||||
248 | $self->_set_leader_lengths( $reclen, $baseaddress ); | ||||
249 | } | ||||
250 | |||||
251 | =head2 _build_tag_directory() | ||||
252 | |||||
- - | |||||
262 | sub _build_tag_directory { | ||||
263 | my $marc = shift; | ||||
264 | $marc = shift if (ref($marc)||$marc) =~ /^MARC::File/; | ||||
265 | die "Wanted a MARC::Record but got a ", ref($marc) unless ref($marc) eq "MARC::Record"; | ||||
266 | |||||
267 | my @fields; | ||||
268 | my @directory; | ||||
269 | |||||
270 | my $dataend = 0; | ||||
271 | for my $field ( $marc->fields() ) { | ||||
272 | # Dump data into proper format | ||||
273 | my $str = $field->as_usmarc; | ||||
274 | push( @fields, $str ); | ||||
275 | |||||
276 | # Create directory entry | ||||
277 | my $len = bytes::length( $str ); | ||||
278 | |||||
279 | my $direntry = sprintf( "%03s%04d%05d", $field->tag, $len, $dataend ); | ||||
280 | push( @directory, $direntry ); | ||||
281 | $dataend += $len; | ||||
282 | } | ||||
283 | |||||
284 | my $baseaddress = | ||||
285 | LEADER_LEN + # better be 24 | ||||
286 | ( @directory * DIRECTORY_ENTRY_LEN ) + | ||||
287 | # all the directory entries | ||||
288 | 1; # end-of-field marker | ||||
289 | |||||
290 | |||||
291 | my $total = | ||||
292 | $baseaddress + # stuff before first field | ||||
293 | $dataend + # Length of the fields | ||||
294 | 1; # End-of-record marker | ||||
295 | |||||
- - | |||||
298 | return (\@fields, \@directory, $total, $baseaddress); | ||||
299 | } | ||||
300 | |||||
301 | =head2 encode() | ||||
302 | |||||
- - | |||||
308 | sub encode() { | ||||
309 | my $marc = shift; | ||||
310 | $marc = shift if (ref($marc)||$marc) =~ /^MARC::File/; | ||||
311 | |||||
312 | my ($fields,$directory,$reclen,$baseaddress) = _build_tag_directory($marc); | ||||
313 | $marc->set_leader_lengths( $reclen, $baseaddress ); | ||||
314 | |||||
315 | # Glomp it all together | ||||
316 | return join("",$marc->leader, @$directory, END_OF_FIELD, @$fields, END_OF_RECORD); | ||||
317 | } | ||||
318 | 1 | 5µs | 1; | ||
319 | |||||
320 | __END__ | ||||
# spent 3.15ms within MARC::File::USMARC::CORE:match which was called 1707 times, avg 2µs/call:
# 544 times (1.37ms+0s) by MARC::File::USMARC::decode at line 156, avg 3µs/call
# 544 times (695µs+0s) by MARC::File::USMARC::decode at line 159, avg 1µs/call
# 544 times (654µs+0s) by MARC::File::USMARC::decode at line 162, avg 1µs/call
# 25 times (255µs+0s) by MARC::File::USMARC::decode at line 122, avg 10µs/call
# 25 times (100µs+0s) by MARC::File::USMARC::decode at line 109, avg 4µs/call
# 25 times (80µs+0s) by MARC::File::USMARC::decode at line 114, avg 3µs/call | |||||
# spent 1.53ms within MARC::File::USMARC::CORE:unpack which was called 544 times, avg 3µs/call:
# 544 times (1.53ms+0s) by MARC::File::USMARC::decode at line 153, avg 3µs/call |