Filename | /var/www/foswikidev/core/lib/Foswiki/Serialise/Embedded.pm |
Statements | Executed 14027537 statements in 31.3s |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
927699 | 1 | 1 | 24.0s | 31.4s | _readKeyValues | Foswiki::Serialise::Embedded::
927699 | 2 | 1 | 13.6s | 55.5s | _readMETA | Foswiki::Serialise::Embedded::
26327 | 1 | 1 | 8.34s | 64.2s | read | Foswiki::Serialise::Embedded::
1 | 1 | 1 | 14µs | 27µs | BEGIN@16 | Foswiki::Serialise::Embedded::
1 | 1 | 1 | 10µs | 38µs | BEGIN@20 | Foswiki::Serialise::Embedded::
1 | 1 | 1 | 10µs | 14µs | BEGIN@17 | Foswiki::Serialise::Embedded::
1 | 1 | 1 | 10µs | 10µs | new | Foswiki::Serialise::Embedded::
1 | 1 | 1 | 7µs | 7µs | BEGIN@18 | Foswiki::Serialise::Embedded::
1 | 1 | 1 | 3µs | 3µs | BEGIN@19 | Foswiki::Serialise::Embedded::
0 | 0 | 0 | 0s | 0s | _writeKeyValue | Foswiki::Serialise::Embedded::
0 | 0 | 0 | 0s | 0s | _writeTypes | Foswiki::Serialise::Embedded::
0 | 0 | 0 | 0s | 0s | dataEncode | Foswiki::Serialise::Embedded::
0 | 0 | 0 | 0s | 0s | write | Foswiki::Serialise::Embedded::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | |||||
3 | =begin TML | ||||
4 | |||||
5 | ---+ package Foswiki::Serialise::Embedded | ||||
6 | |||||
7 | This is __the__ on disk format serialiser and deserialise for | ||||
8 | Foswiki topics legacy .txt format. | ||||
9 | |||||
10 | __WARNING__ this is only for Foswiki::Meta objects. | ||||
11 | |||||
12 | =cut | ||||
13 | |||||
14 | package Foswiki::Serialise::Embedded; | ||||
15 | |||||
16 | 2 | 28µs | 2 | 41µs | # spent 27µs (14+14) within Foswiki::Serialise::Embedded::BEGIN@16 which was called:
# once (14µs+14µs) by Foswiki::Serialise::_getSerialiser at line 16 # spent 27µs making 1 call to Foswiki::Serialise::Embedded::BEGIN@16
# spent 14µs making 1 call to strict::import |
17 | 2 | 28µs | 2 | 19µs | # spent 14µs (10+5) within Foswiki::Serialise::Embedded::BEGIN@17 which was called:
# once (10µs+5µs) by Foswiki::Serialise::_getSerialiser at line 17 # spent 14µs making 1 call to Foswiki::Serialise::Embedded::BEGIN@17
# spent 4µs making 1 call to warnings::import |
18 | 2 | 20µs | 1 | 7µs | # spent 7µs within Foswiki::Serialise::Embedded::BEGIN@18 which was called:
# once (7µs+0s) by Foswiki::Serialise::_getSerialiser at line 18 # spent 7µs making 1 call to Foswiki::Serialise::Embedded::BEGIN@18 |
19 | 2 | 22µs | 1 | 3µs | # spent 3µs within Foswiki::Serialise::Embedded::BEGIN@19 which was called:
# once (3µs+0s) by Foswiki::Serialise::_getSerialiser at line 19 # spent 3µs making 1 call to Foswiki::Serialise::Embedded::BEGIN@19 |
20 | 2 | 1.15ms | 2 | 67µs | # spent 38µs (10+28) within Foswiki::Serialise::Embedded::BEGIN@20 which was called:
# once (10µs+28µs) by Foswiki::Serialise::_getSerialiser at line 20 # spent 38µs making 1 call to Foswiki::Serialise::Embedded::BEGIN@20
# spent 28µs making 1 call to Exporter::import |
21 | |||||
22 | =begin TML | ||||
23 | |||||
24 | ---++ ClassMethod new( $class, ) -> $cereal | ||||
25 | |||||
26 | =cut | ||||
27 | |||||
28 | # spent 10µs within Foswiki::Serialise::Embedded::new which was called:
# once (10µs+0s) by Foswiki::Serialise::_getSerialiser at line 78 of /var/www/foswikidev/core/lib/Foswiki/Serialise.pm | ||||
29 | 1 | 500ns | my $class = shift; | ||
30 | 1 | 7µs | my $this = bless( {}, $class ); | ||
31 | 1 | 6µs | return $this; | ||
32 | } | ||||
33 | |||||
34 | # Generate the embedded store form of the topic. The embedded store | ||||
35 | # form has meta-data values embedded using %META: lines. The text | ||||
36 | # stored in the meta is taken as the topic text. | ||||
37 | # | ||||
38 | # TODO: Soooo.... if we wanted to make a meta->setPreference('VARIABLE', 'Values...'); we would have to change this to | ||||
39 | # 1 see if that preference is set in the {_text} using the * Set syntax, in which case, replace that | ||||
40 | # 2 or let the META::PREF.. work as it does now.. | ||||
41 | sub write { | ||||
42 | my ( $this, $meta ) = @_; | ||||
43 | |||||
44 | ASSERT( $meta->isa('Foswiki::Meta') ) if DEBUG; | ||||
45 | |||||
46 | my $ti = $meta->get('TOPICINFO'); | ||||
47 | delete $ti->{rev} if $ti; # don't want this written | ||||
48 | |||||
49 | my $text = _writeTypes( $meta, 'TOPICINFO', 'TOPICPARENT' ); | ||||
50 | $text .= ( $meta->text() || '' ); | ||||
51 | my $end = | ||||
52 | _writeTypes( $meta, 'FORM', 'FIELD', 'FILEATTACHMENT', 'TOPICMOVED' ) | ||||
53 | . _writeTypes( $meta, 'not', 'TOPICINFO', 'TOPICPARENT', 'FORM', 'FIELD', | ||||
54 | 'FILEATTACHMENT', 'TOPICMOVED' ); | ||||
55 | $text .= "\n" if $end; | ||||
56 | |||||
57 | $ti->{rev} = $ti->{version} if $ti; | ||||
58 | |||||
59 | return $text . $end; | ||||
60 | } | ||||
61 | |||||
62 | # spent 64.2s (8.34+55.9) within Foswiki::Serialise::Embedded::read which was called 26327 times, avg 2.44ms/call:
# 26327 times (8.34s+55.9s) by Foswiki::Serialise::deserialise at line 54 of /var/www/foswikidev/core/lib/Foswiki/Serialise.pm, avg 2.44ms/call | ||||
63 | 26327 | 37.4ms | my ( $this, $text, $meta ) = @_; | ||
64 | |||||
65 | ASSERT( $meta->isa('Foswiki::Meta') ) if DEBUG; | ||||
66 | 26327 | 6.10ms | my $format = Foswiki::Meta::EMBEDDING_FORMAT_VERSION; | ||
67 | |||||
68 | # head meta-data | ||||
69 | # NO THIS CANNOT BE /g - TOPICINFO is _only_ valid as the first line! | ||||
70 | 52653 | 279ms | 26326 | 2.37s | $text =~ s<^(%META:(TOPICINFO){(.*)}%\n)> # spent 2.37s making 26326 calls to Foswiki::Serialise::Embedded::_readMETA, avg 90µs/call |
71 | <_readMETA($meta, $1, $2, $3, 1)>e; | ||||
72 | |||||
73 | # WARNING: if the TOPICINFO *looks* valid but has has unrecognisable | ||||
74 | # fields in it, it will fail the Meta::isValidEmbedding test and | ||||
75 | # a default TOPICINFO wil be used - but the bad TOPICINFO will be left | ||||
76 | # in the topic so it looks like there are two TOPICINFOs. Admins | ||||
77 | # will have to fix up the broken topic. | ||||
78 | |||||
79 | 26327 | 42.8ms | 26327 | 69.8ms | my $ti = $meta->get('TOPICINFO'); # spent 69.8ms making 26327 calls to Foswiki::Meta::get, avg 3µs/call |
80 | 26327 | 9.30ms | if ($ti) { | ||
81 | 26326 | 13.5ms | $format = $ti->{format} || 0; | ||
82 | |||||
83 | # Make sure we update the topic format for when we save | ||||
84 | 26326 | 12.1ms | $ti->{format} = Foswiki::Meta::EMBEDDING_FORMAT_VERSION; | ||
85 | |||||
86 | # Clean up SVN and other malformed rev nums. This can happen | ||||
87 | # when old code (e.g. old plugins) generated the meta. | ||||
88 | 26326 | 50.4ms | 26326 | 127ms | $ti->{version} = Foswiki::Store::cleanUpRevID( $ti->{version} ); # spent 127ms making 26326 calls to Foswiki::Store::cleanUpRevID, avg 5µs/call |
89 | 26326 | 16.1ms | $ti->{rev} = $ti->{version}; # not used, maintained for compatibility | ||
90 | 26326 | 47.4ms | 24019 | 64.3ms | $ti->{reprev} = Foswiki::Store::cleanUpRevID( $ti->{reprev} ) # spent 64.3ms making 24019 calls to Foswiki::Store::cleanUpRevID, avg 3µs/call |
91 | if defined $ti->{reprev}; | ||||
92 | } | ||||
93 | else { | ||||
94 | |||||
95 | #defaults.. | ||||
96 | } | ||||
97 | |||||
98 | # Other meta-data | ||||
99 | 26327 | 5.16ms | my $endMeta = 0; | ||
100 | 26327 | 132ms | if ( $format !~ /^[\d.]+$/ || $format < 1.1 ) { | ||
101 | 1 | 71µs | require Foswiki::Compatibility; | ||
102 | 1 | 72µs | if ( | ||
103 | $text =~ s/^%META:([^{]+){(.*)}%\n/ | ||||
104 | 13 | 15µs | 13 | 428µs | Foswiki::Compatibility::readSymmetricallyEncodedMETA( # spent 428µs making 13 calls to Foswiki::Compatibility::readSymmetricallyEncodedMETA, avg 33µs/call |
105 | 13 | 3µs | $meta, $1, $2 ); ''/gem | ||
106 | ) | ||||
107 | { | ||||
108 | $endMeta = 1; | ||||
109 | } | ||||
110 | } | ||||
111 | else { | ||||
112 | 26326 | 3.52s | if ( | ||
113 | 901373 | 1.11s | 901373 | 53.1s | $text =~ s<^(%META:([^{]+){(.*)}%\n)> # spent 53.1s making 901373 calls to Foswiki::Serialise::Embedded::_readMETA, avg 59µs/call |
114 | <_readMETA($meta, $1, $2, $3, 0)>gem | ||||
115 | ) | ||||
116 | { | ||||
117 | $endMeta = 1; | ||||
118 | } | ||||
119 | } | ||||
120 | |||||
121 | # eat extra newlines put in to separate text from tail meta-data | ||||
122 | 26327 | 78.5ms | $text =~ s/\n$//s if $endMeta; | ||
123 | |||||
124 | # If there is no meta data then convert from old format | ||||
125 | 26327 | 47.5ms | 26327 | 57.6ms | if ( !$meta->count('TOPICINFO') ) { # spent 57.6ms making 26327 calls to Foswiki::Meta::count, avg 2µs/call |
126 | |||||
127 | # The T-word string must remain unchanged for the compatibility | ||||
128 | 1 | 4µs | if ( $text =~ m/<!--TWikiAttachment-->/ ) { | ||
129 | require Foswiki::Compatibility; | ||||
130 | $text = Foswiki::Compatibility::migrateToFileAttachmentMacro( | ||||
131 | $meta->{_session}, $meta, $text ); | ||||
132 | } | ||||
133 | |||||
134 | # The T-word string must remain unchanged for the compatibility | ||||
135 | 1 | 3µs | if ( $text =~ m/<!--TWikiCat-->/ ) { | ||
136 | require Foswiki::Compatibility; | ||||
137 | $text = | ||||
138 | Foswiki::Compatibility::upgradeCategoryTable( $meta->{_session}, | ||||
139 | $meta->{_web}, $meta->{_topic}, $meta, $text ); | ||||
140 | } | ||||
141 | } | ||||
142 | elsif ( $format eq '1.0beta' ) { | ||||
143 | require Foswiki::Compatibility; | ||||
144 | |||||
145 | # This format used live at DrKW for a few months | ||||
146 | # The T-word string must remain unchanged for the compatibility | ||||
147 | if ( $text =~ m/<!--TWikiCat-->/ ) { | ||||
148 | $text = | ||||
149 | Foswiki::Compatibility::upgradeCategoryTable( $meta->{_session}, | ||||
150 | $meta->{_web}, $meta->{_topic}, $meta, $text ); | ||||
151 | } | ||||
152 | Foswiki::Compatibility::upgradeFrom1v0beta( $meta->{_session}, $meta ); | ||||
153 | if ( $meta->count('TOPICMOVED') ) { | ||||
154 | my $moved = $meta->get('TOPICMOVED'); | ||||
155 | $meta->put( 'TOPICMOVED', $moved ); | ||||
156 | } | ||||
157 | } | ||||
158 | |||||
159 | 26327 | 40.0ms | if ( $format !~ /^[\d.]+$/ || $format < 1.1 ) { | ||
160 | |||||
161 | # compatibility; topics version 1.0 and earlier equivalenced tab | ||||
162 | # with three spaces. Respect that. | ||||
163 | 1 | 30µs | $text =~ s/\t/ /g; | ||
164 | } | ||||
165 | |||||
166 | 26327 | 121ms | 26327 | 75.7ms | $meta->text($text); # spent 75.9ms making 26327 calls to Foswiki::Meta::text, avg 3µs/call, recursion: max depth 1, sum of overlapping time 136µs |
167 | } | ||||
168 | |||||
169 | # STATIC Build a hash by parsing name=value comma separated pairs | ||||
170 | # SMELL: duplication of Foswiki::Attrs, using a different | ||||
171 | # system of escapes :-( | ||||
172 | # spent 31.4s (24.0+7.40) within Foswiki::Serialise::Embedded::_readKeyValues which was called 927699 times, avg 34µs/call:
# 927699 times (24.0s+7.40s) by Foswiki::Serialise::Embedded::_readMETA at line 186, avg 34µs/call | ||||
173 | 927699 | 426ms | my ($args) = @_; | ||
174 | 927699 | 84.9ms | my %res; | ||
175 | |||||
176 | # Format of data is name='value' name1='value1' [...] | ||||
177 | 927701 | 6.87s | 1 | 2.85ms | $args =~ s/\s*([^=]+)="([^"]*)"/ # spent 2.85ms making 1 call to utf8::AUTOLOAD |
178 | 3323098 | 6.17s | 3323098 | 7.39s | $res{$1} = Foswiki::Meta::dataDecode( $2 ), ''/ge; # spent 7.39s making 3323098 calls to Foswiki::Meta::dataDecode, avg 2µs/call |
179 | |||||
180 | 927699 | 4.10s | return \%res; | ||
181 | } | ||||
182 | |||||
183 | # spent 55.5s (13.6+41.9) within Foswiki::Serialise::Embedded::_readMETA which was called 927699 times, avg 60µs/call:
# 901373 times (13.2s+39.9s) by Foswiki::Serialise::Embedded::read at line 113, avg 59µs/call
# 26326 times (394ms+1.98s) by Foswiki::Serialise::Embedded::read at line 70, avg 90µs/call | ||||
184 | 927699 | 1.11s | my ( $meta, $expr, $type, $args, $readTOPICINFO ) = @_; | ||
185 | 927699 | 241ms | return $expr if $type eq 'TOPICINFO' && !$readTOPICINFO; | ||
186 | 927699 | 850ms | 927699 | 31.4s | my $keys = _readKeyValues($args); # spent 31.4s making 927699 calls to Foswiki::Serialise::Embedded::_readKeyValues, avg 34µs/call |
187 | 927699 | 984ms | 927699 | 6.20s | if ( Foswiki::Meta::isValidEmbedding( $type, $keys ) ) { # spent 6.20s making 927699 calls to Foswiki::Meta::isValidEmbedding, avg 7µs/call |
188 | 927459 | 1.19s | 875069 | 4.02s | if ( defined( $keys->{name} ) ) { # spent 4.02s making 875069 calls to Foswiki::Meta::putKeyed, avg 5µs/call |
189 | |||||
190 | # save it keyed if it has a name | ||||
191 | $meta->putKeyed( $type, $keys ); | ||||
192 | } | ||||
193 | else { | ||||
194 | 52390 | 74.5ms | 52390 | 329ms | $meta->put( $type, $keys ); # spent 329ms making 52390 calls to Foswiki::Meta::put, avg 6µs/call |
195 | } | ||||
196 | 927459 | 3.66s | return ''; | ||
197 | } | ||||
198 | else { | ||||
199 | 240 | 812µs | return $expr; | ||
200 | } | ||||
201 | } | ||||
202 | |||||
203 | # PRIVATE STATIC Write a meta-data key=value pair | ||||
204 | # The encoding is reversed in _readKeyValues | ||||
205 | sub _writeKeyValue { | ||||
206 | my ( $key, $value ) = @_; | ||||
207 | |||||
208 | if ( defined($value) ) { | ||||
209 | $value = dataEncode($value); | ||||
210 | } | ||||
211 | else { | ||||
212 | $value = ''; | ||||
213 | } | ||||
214 | |||||
215 | return $key . '="' . $value . '"'; | ||||
216 | } | ||||
217 | |||||
218 | # PRIVATE STATIC: Write all the key=value pairs for the types listed | ||||
219 | sub _writeTypes { | ||||
220 | my ( $meta, @types ) = @_; | ||||
221 | |||||
222 | my $text = ''; | ||||
223 | |||||
224 | if ( $types[0] eq 'not' ) { | ||||
225 | |||||
226 | # write all types that are not in the list | ||||
227 | my %seen; | ||||
228 | @seen{@types} = (); | ||||
229 | @types = (); # empty "not in list" | ||||
230 | foreach my $key ( keys %$meta ) { | ||||
231 | push( @types, $key ) | ||||
232 | unless ( exists $seen{$key} || $key =~ m/^_/ ); | ||||
233 | } | ||||
234 | } | ||||
235 | |||||
236 | foreach my $type (@types) { | ||||
237 | next if ( $type =~ m/^_/ ); | ||||
238 | my $data = $meta->{$type}; | ||||
239 | next if !defined $data; | ||||
240 | foreach my $item (@$data) { | ||||
241 | next if ( $item =~ m/^_/ ); | ||||
242 | my $sep = ''; | ||||
243 | $text .= '%META:' . $type . '{'; | ||||
244 | my $name = $item->{name}; | ||||
245 | if ($name) { | ||||
246 | |||||
247 | # If there's a name field, put first to make regexp | ||||
248 | # based searching easier | ||||
249 | $text .= _writeKeyValue( 'name', $item->{name} ); | ||||
250 | $sep = ' '; | ||||
251 | } | ||||
252 | foreach my $key ( sort keys %$item ) { | ||||
253 | |||||
254 | #next if ($key =~ m/^_/ ); | ||||
255 | if ( $key ne 'name' ) { | ||||
256 | $text .= $sep; | ||||
257 | $text .= _writeKeyValue( $key, $item->{$key} ); | ||||
258 | $sep = ' '; | ||||
259 | } | ||||
260 | } | ||||
261 | $text .= '}%' . "\n"; | ||||
262 | } | ||||
263 | } | ||||
264 | |||||
265 | return $text; | ||||
266 | } | ||||
267 | |||||
268 | =begin TML | ||||
269 | |||||
270 | ---++ StaticMethod dataEncode( $uncoded ) -> $coded | ||||
271 | |||||
272 | Encode meta-data field values, escaping out selected characters. | ||||
273 | The encoding is chosen to avoid problems with parsing the attribute | ||||
274 | values in embedded meta-data, while minimising the number of | ||||
275 | characters encoded so searches can still work (fairly) sensibly. | ||||
276 | |||||
277 | The encoding has to be exported because Foswiki (and plugins) use | ||||
278 | encoded field data in other places e.g. RDiff, mainly as a shorthand | ||||
279 | for the properly parsed meta object. Some day we may be able to | ||||
280 | eliminate that.... | ||||
281 | |||||
282 | =cut | ||||
283 | |||||
284 | sub dataEncode { | ||||
285 | my $datum = shift; | ||||
286 | |||||
287 | $datum =~ s/([%"\r\n{}])/'%'.sprintf('%02x',ord($1))/ge; | ||||
288 | return $datum; | ||||
289 | } | ||||
290 | |||||
291 | 1 | 2µs | 1; | ||
292 | __END__ |