Filename | /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs/Stack.pm |
Statements | Executed 22282 statements in 73.6ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
20 | 1 | 1 | 29.3ms | 29.3ms | restore | Foswiki::Prefs::Stack::
69 | 7 | 1 | 9.80ms | 11.0ms | newLevel | Foswiki::Prefs::Stack::
325 | 2 | 1 | 8.25ms | 20.1ms | insert | Foswiki::Prefs::Stack::
618 | 1 | 1 | 7.26ms | 7.26ms | finalizedBefore | Foswiki::Prefs::Stack::
724 | 3 | 1 | 3.54ms | 3.54ms | backAtLevel | Foswiki::Prefs::Stack::
618 | 1 | 1 | 3.15ms | 3.15ms | prefIsDefined | Foswiki::Prefs::Stack::
167 | 1 | 1 | 2.32ms | 4.07ms | getDefinitionLevel | Foswiki::Prefs::Stack::
207 | 2 | 1 | 2.10ms | 2.10ms | _getLevel | Foswiki::Prefs::Stack::
67 | 1 | 1 | 1.87ms | 2.52ms | getPreference | Foswiki::Prefs::Stack::
54 | 1 | 1 | 474µs | 474µs | finalized | Foswiki::Prefs::Stack::
1 | 1 | 1 | 358µs | 365µs | BEGIN@28 | Foswiki::Prefs::Stack::
4 | 2 | 2 | 294µs | 418µs | finish | Foswiki::Prefs::Stack::
24 | 2 | 1 | 229µs | 229µs | size | Foswiki::Prefs::Stack::
4 | 2 | 1 | 112µs | 112µs | new | Foswiki::Prefs::Stack::
1 | 1 | 1 | 25µs | 34µs | BEGIN@26 | Foswiki::Prefs::Stack::
1 | 1 | 1 | 22µs | 42µs | BEGIN@27 | Foswiki::Prefs::Stack::
0 | 0 | 0 | 0s | 0s | clone | Foswiki::Prefs::Stack::
0 | 0 | 0 | 0s | 0s | prefs | Foswiki::Prefs::Stack::
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 | ---+ UNPUBLISHED package Foswiki::Prefs::Stack | ||||
6 | |||||
7 | Foswiki preferences mechanism are like stacks: | ||||
8 | * Preferences pushed later have precedence over ones pushed earlier I must | ||||
9 | * be able to return (restore) to a state I was earlier | ||||
10 | |||||
11 | This stack can exist as an index, so preference data is not copied everywhere. | ||||
12 | |||||
13 | The index is composed by three elements: | ||||
14 | * A bitstring map. Each preference has a bitmap. Each bit corresponds to a | ||||
15 | level. The bit is 1 if the preference is defined at that level and 0 | ||||
16 | otherwise. If a preference is "defined" in some level, but it was | ||||
17 | finalized, then the corresponding bit is 0. | ||||
18 | * A level list storing a backend object that is associated with each level | ||||
19 | * A final hash that maps preferences to the level they were finalized. | ||||
20 | |||||
21 | This class deals with this stuff and must be used only by =Foswiki::Prefs= | ||||
22 | |||||
23 | =cut | ||||
24 | |||||
25 | package Foswiki::Prefs::Stack; | ||||
26 | 2 | 46µs | 2 | 42µs | # spent 34µs (25+8) within Foswiki::Prefs::Stack::BEGIN@26 which was called:
# once (25µs+8µs) by Foswiki::Prefs::BEGIN@70 at line 26 # spent 34µs making 1 call to Foswiki::Prefs::Stack::BEGIN@26
# spent 8µs making 1 call to strict::import |
27 | 2 | 45µs | 2 | 63µs | # spent 42µs (22+20) within Foswiki::Prefs::Stack::BEGIN@27 which was called:
# once (22µs+20µs) by Foswiki::Prefs::BEGIN@70 at line 27 # spent 42µs making 1 call to Foswiki::Prefs::Stack::BEGIN@27
# spent 20µs making 1 call to warnings::import |
28 | 2 | 2.01ms | 2 | 371µs | # spent 365µs (358+6) within Foswiki::Prefs::Stack::BEGIN@28 which was called:
# once (358µs+6µs) by Foswiki::Prefs::BEGIN@70 at line 28 # spent 365µs making 1 call to Foswiki::Prefs::Stack::BEGIN@28
# spent 6µs making 1 call to bytes::import |
29 | |||||
30 | =begin TML | ||||
31 | |||||
32 | ---++ ClassMethod new( $session ) | ||||
33 | |||||
34 | Creates a new Stack object. | ||||
35 | |||||
36 | =cut | ||||
37 | |||||
38 | # spent 112µs within Foswiki::Prefs::Stack::new which was called 4 times, avg 28µs/call:
# 3 times (89µs+0s) by Foswiki::Prefs::_getWebPrefsObj at line 181 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 30µs/call
# once (23µs+0s) by Foswiki::Prefs::new at line 85 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm | ||||
39 | 4 | 10µs | my $proto = shift; | ||
40 | 4 | 8µs | my $class = ref($proto) || $proto; | ||
41 | 4 | 41µs | my $this = { | ||
42 | 'final' => {}, # Map preferences to the level the were finalized. | ||||
43 | 'levels' => [], # Maps leves to the corresponding backend objects. | ||||
44 | 'map' => {}, # Associate each preference with its bitstring map. | ||||
45 | }; | ||||
46 | 4 | 50µs | return bless $this, $class; | ||
47 | } | ||||
48 | |||||
49 | =begin TML | ||||
50 | |||||
51 | ---++ ObjectMethod finish() | ||||
52 | |||||
53 | Break circular references. | ||||
54 | |||||
55 | =cut | ||||
56 | |||||
57 | # Note to developers; please undef *all* fields in the object explicitly, | ||||
58 | # whether they are references or not. That way this method is "golden | ||||
59 | # documentation" of the live fields in the object. | ||||
60 | # spent 418µs (294+123) within Foswiki::Prefs::Stack::finish which was called 4 times, avg 104µs/call:
# 3 times (95µs+24µs) by Foswiki::Prefs::Web::finish at line 53 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs/Web.pm, avg 40µs/call
# once (199µs+99µs) by Foswiki::Prefs::finish at line 115 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm | ||||
61 | 4 | 6µs | my $this = shift; | ||
62 | 4 | 23µs | undef $this->{'final'}; | ||
63 | 4 | 13µs | if ( $this->{'levels'} ) { | ||
64 | 8 | 27µs | foreach my $back ( @{ $this->{'levels'} } ) { | ||
65 | 9 | 169µs | 9 | 123µs | $back->finish(); # spent 119µs making 8 calls to Foswiki::Prefs::TopicRAM::finish, avg 15µs/call
# spent 4µs making 1 call to Foswiki::Prefs::HASH::finish |
66 | } | ||||
67 | } | ||||
68 | 4 | 21µs | undef $this->{'levels'}; | ||
69 | 4 | 26µs | undef $this->{'map'}; | ||
70 | } | ||||
71 | |||||
72 | =begin TML | ||||
73 | |||||
74 | ---++ ObjectMethod size() -> $size | ||||
75 | |||||
76 | Returns the size of the stack in number of levels. | ||||
77 | |||||
78 | =cut | ||||
79 | |||||
80 | # spent 229µs within Foswiki::Prefs::Stack::size which was called 24 times, avg 10µs/call:
# 21 times (200µs+0s) by Foswiki::Prefs::pushTopicContext at line 267 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 10µs/call
# 3 times (29µs+0s) by Foswiki::Prefs::_pushWebInStack at line 156 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 10µs/call | ||||
81 | 48 | 259µs | return scalar @{ $_[0]->{levels} }; | ||
82 | } | ||||
83 | |||||
84 | =begin TML | ||||
85 | |||||
86 | ---++ ObjectMethod backAtLevel($level) -> $back | ||||
87 | |||||
88 | Returns the backend object corresponding to $level. If $level is negative, | ||||
89 | consider that number from the top of the stack. -1 means the top element. | ||||
90 | |||||
91 | =cut | ||||
92 | |||||
93 | # spent 3.54ms within Foswiki::Prefs::Stack::backAtLevel which was called 724 times, avg 5µs/call:
# 517 times (2.51ms+0s) by Foswiki::Prefs::getPreference at line 439 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 5µs/call
# 167 times (842µs+0s) by Foswiki::Prefs::getPreference at line 445 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 5µs/call
# 40 times (194µs+0s) by Foswiki::Prefs::popTopicContext at line 309 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 5µs/call | ||||
94 | 724 | 4.45ms | return $_[0]->{levels}->[ $_[1] ]; | ||
95 | } | ||||
96 | |||||
97 | =begin TML | ||||
98 | |||||
99 | ---++ ObjectMethod finalizedBefore($pref, $level) -> $boolean | ||||
100 | |||||
101 | Returns true if $pref was finalized somewhere earlier than $level. If $pref is | ||||
102 | finalized *in* $level or it's not finalized, returns true. | ||||
103 | |||||
104 | =cut | ||||
105 | |||||
106 | # spent 7.26ms within Foswiki::Prefs::Stack::finalizedBefore which was called 618 times, avg 12µs/call:
# 618 times (7.26ms+0s) by Foswiki::Prefs::getPreference at line 439 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 12µs/call | ||||
107 | 618 | 1.07ms | my ( $this, $key, $level ) = @_; | ||
108 | 1236 | 3.16ms | $level += @{ $this->{levels} } if $level < 0; | ||
109 | 618 | 4.22ms | return exists $this->{final}{$key} && $this->{final}{$key} < $level; | ||
110 | } | ||||
111 | |||||
112 | =begin TML | ||||
113 | |||||
114 | ---++ ObjectMethod finalized($pref) -> $boolean | ||||
115 | |||||
116 | Returns true if $pref in finalized. | ||||
117 | |||||
118 | =cut | ||||
119 | |||||
120 | # spent 474µs within Foswiki::Prefs::Stack::finalized which was called 54 times, avg 9µs/call:
# 54 times (474µs+0s) by Foswiki::Prefs::setSessionPreferences at line 394 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 9µs/call | ||||
121 | 54 | 129µs | my ( $this, $key ) = @_; | ||
122 | 54 | 435µs | return exists $this->{final}{$key}; | ||
123 | } | ||||
124 | |||||
125 | =begin TML | ||||
126 | |||||
127 | ---++ ObjectMethod prefs() -> @prefs | ||||
128 | |||||
129 | Returns a list with the name of all defined prefs in the stack. | ||||
130 | |||||
131 | =cut | ||||
132 | |||||
133 | sub prefs { | ||||
134 | return keys %{ $_[0]->{'map'} }; | ||||
135 | } | ||||
136 | |||||
137 | =begin TML | ||||
138 | |||||
139 | ---++ ObjectMethod prefIsDefined($pref) -> $boolean | ||||
140 | |||||
141 | Returns true if $pref is defined somewhere in the stack. | ||||
142 | |||||
143 | =cut | ||||
144 | |||||
145 | # spent 3.15ms within Foswiki::Prefs::Stack::prefIsDefined which was called 618 times, avg 5µs/call:
# 618 times (3.15ms+0s) by Foswiki::Prefs::getPreference at line 441 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 5µs/call | ||||
146 | 618 | 3.92ms | return exists $_[0]->{'map'}{ $_[1] }; | ||
147 | } | ||||
148 | |||||
149 | =begin TML | ||||
150 | |||||
151 | ---++ ObjectMethod insert($type, $pref, $value) -> $num | ||||
152 | |||||
153 | Define preference named $pref of type $type as $value. $type can be 'Local' or | ||||
154 | 'Set'. | ||||
155 | |||||
156 | Returns the number of inserted preferences (0 or 1). | ||||
157 | |||||
158 | =cut | ||||
159 | |||||
160 | # spent 20.1ms (8.25+11.9) within Foswiki::Prefs::Stack::insert which was called 325 times, avg 62µs/call:
# 271 times (6.64ms+9.48ms) by Foswiki::Prefs::pushTopicContext at line 284 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 59µs/call
# 54 times (1.62ms+2.39ms) by Foswiki::Prefs::setSessionPreferences at line 395 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 74µs/call | ||||
161 | 325 | 450µs | my $this = shift; | ||
162 | |||||
163 | 325 | 575µs | my $back = $this->{levels}->[-1]; | ||
164 | 325 | 1.61ms | 325 | 11.9ms | my $num = $back->insert(@_); # spent 11.9ms making 325 calls to Foswiki::Prefs::HASH::insert, avg 37µs/call |
165 | |||||
166 | 325 | 508µs | my $key = $_[1]; | ||
167 | 325 | 675µs | $this->{'map'}{$key} = '' unless exists $this->{'map'}{$key}; | ||
168 | |||||
169 | 650 | 1.44ms | my $level = $#{ $this->{levels} }; | ||
170 | 325 | 1.11ms | vec( $this->{'map'}{$key}, $level, 1 ) = 1; | ||
171 | |||||
172 | 325 | 1.78ms | return $num; | ||
173 | } | ||||
174 | |||||
175 | =begin TML | ||||
176 | |||||
177 | ---++ ObjectMethod newLevel($back, $prefix) | ||||
178 | |||||
179 | Pushes all preferences in $back on the stack, except for the finalized ones. | ||||
180 | Optionally $prefix preferences name in the index. This feature is used by | ||||
181 | plugins: A preference PREF defined in MyPlugin topic should be referenced by | ||||
182 | MYPLUGIN_PREF. In this example $prefix is MYPLUGIN_. | ||||
183 | |||||
184 | =cut | ||||
185 | |||||
186 | # spent 11.0ms (9.80+1.20) within Foswiki::Prefs::Stack::newLevel which was called 69 times, avg 159µs/call:
# 21 times (5.20ms+431µs) by Foswiki::Prefs::pushTopicContext at line 277 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 268µs/call
# 21 times (1.05ms+315µs) by Foswiki::Prefs::pushTopicContext at line 280 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 65µs/call
# 21 times (842µs+282µs) by Foswiki::Prefs::pushTopicContext at line 281 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 54µs/call
# 3 times (850µs+73µs) by Foswiki::Prefs::_pushWebInStack at line 162 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 308µs/call
# once (1.35ms+56µs) by Foswiki::Prefs::loadDefaultPreferences at line 360 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm
# once (472µs+26µs) by Foswiki::Prefs::loadSitePreferences at line 377 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm
# once (37µs+15µs) by Foswiki::Prefs::setUserPreferences at line 345 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm | ||||
187 | 69 | 141µs | my ( $this, $back, $prefix ) = @_; | ||
188 | |||||
189 | 138 | 344µs | push @{ $this->{levels} }, $back; | ||
190 | 138 | 318µs | my $level = $#{ $this->{levels} }; | ||
191 | 69 | 112µs | $prefix ||= ''; | ||
192 | 692 | 2.64ms | 69 | 709µs | foreach ( map { $prefix . $_ } $back->prefs ) { # spent 577µs making 48 calls to Foswiki::Prefs::TopicRAM::prefs, avg 12µs/call
# spent 131µs making 21 calls to Foswiki::Prefs::HASH::prefs, avg 6µs/call |
193 | 623 | 983µs | next if exists $this->{final}{$_}; | ||
194 | 483 | 950µs | $this->{'map'}{$_} = '' unless exists $this->{'map'}{$_}; | ||
195 | 483 | 1.84ms | vec( $this->{'map'}{$_}, $level, 1 ) = 1; | ||
196 | } | ||||
197 | |||||
198 | 69 | 748µs | 69 | 489µs | my @finalPrefs = split /[,\s]+/, ( $back->get('FINALPREFERENCES') || '' ); # spent 338µs making 48 calls to Foswiki::Prefs::TopicRAM::get, avg 7µs/call
# spent 150µs making 21 calls to Foswiki::Prefs::HASH::get, avg 7µs/call |
199 | 69 | 224µs | foreach (@finalPrefs) { | ||
200 | 276 | 867µs | $this->{final}{$_} = $level | ||
201 | unless exists $this->{final}{$_}; | ||||
202 | } | ||||
203 | |||||
204 | 69 | 426µs | return $back; | ||
205 | } | ||||
206 | |||||
207 | =begin TML | ||||
208 | |||||
209 | ---++ ObjectMethod getDefinitionLevel($pref) -> $level | ||||
210 | |||||
211 | Returns the $level in which $pref was defined or undef if it's not defined. | ||||
212 | |||||
213 | =cut | ||||
214 | |||||
215 | # spent 4.07ms (2.32+1.75) within Foswiki::Prefs::Stack::getDefinitionLevel which was called 167 times, avg 24µs/call:
# 167 times (2.32ms+1.75ms) by Foswiki::Prefs::getPreference at line 442 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 24µs/call | ||||
216 | 167 | 349µs | my ( $this, $pref ) = @_; | ||
217 | return | ||||
218 | 167 | 1.66ms | 167 | 1.75ms | exists $this->{'map'}{$pref} ? _getLevel( $this->{'map'}{$pref} ) : undef; # spent 1.75ms making 167 calls to Foswiki::Prefs::Stack::_getLevel, avg 10µs/call |
219 | } | ||||
220 | |||||
221 | # Used to get the level of the highest 1, given a bitstring map. | ||||
222 | sub _getLevel { | ||||
223 | 207 | 379µs | my $map = shift; | ||
224 | return | ||||
225 | 207 | 2.22ms | int( log( ord( substr( $map, -1 ) ) ) / log(2) ) + | ||
226 | ( ( length($map) - 1 ) * 8 ); | ||||
227 | } | ||||
228 | |||||
229 | =begin TML | ||||
230 | |||||
231 | ---++ ObjectMethod getPreference($pref [, $level] ) -> $value | ||||
232 | |||||
233 | Returns the $value of $pref, considering the stack rules (values in higher | ||||
234 | levels overrides those in lower levels). | ||||
235 | |||||
236 | Optionally consider preferences at most $level. This is usefull to get a | ||||
237 | preference of Web if the stack has Web/Subweb. This makes it possible to use | ||||
238 | the same stack for Web and Web/Subweb. | ||||
239 | |||||
240 | =cut | ||||
241 | |||||
242 | # spent 2.52ms (1.87+643µs) within Foswiki::Prefs::Stack::getPreference which was called 67 times, avg 38µs/call:
# 67 times (1.87ms+643µs) by Foswiki::Prefs::Web::get at line 110 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs/Web.pm, avg 38µs/call | ||||
243 | 67 | 185µs | my ( $this, $key, $level ) = @_; | ||
244 | 67 | 177µs | my $map = $this->{'map'}{$key}; | ||
245 | 67 | 215µs | return unless defined $map; | ||
246 | 40 | 93µs | if ( defined $level ) { | ||
247 | 40 | 234µs | my $mask = | ||
248 | ( chr(0xFF) x int( $level / 8 ) ) | ||||
249 | . chr( ( 2**( ( $level % 8 ) + 1 ) ) - 1 ); | ||||
250 | 40 | 72µs | $map &= $mask; | ||
251 | 40 | 171µs | substr( $map, -1 ) = '' | ||
252 | while length($map) > 0 && ord( substr( $map, -1 ) ) == 0; | ||||
253 | 40 | 59µs | return unless length($map) > 0; | ||
254 | } | ||||
255 | 40 | 539µs | 80 | 644µs | return $this->{levels}->[ _getLevel($map) ]->get($key); # spent 354µs making 40 calls to Foswiki::Prefs::Stack::_getLevel, avg 9µs/call
# spent 289µs making 40 calls to Foswiki::Prefs::TopicRAM::get, avg 7µs/call |
256 | } | ||||
257 | |||||
258 | =begin TML | ||||
259 | |||||
260 | ---++ ObjectMethod clone($level ) -> $stack | ||||
261 | |||||
262 | This constructs a new $stack object as a clone of this one, up to the given | ||||
263 | $level. If no $level is given, the resulting object is an extac copy. | ||||
264 | |||||
265 | =cut | ||||
266 | |||||
267 | sub clone { | ||||
268 | my ( $this, $level ) = @_; | ||||
269 | |||||
270 | my $clone = $this->new(); | ||||
271 | $clone->{'map'} = { %{ $this->{'map'} } }; | ||||
272 | $clone->{'levels'} = [ @{ $this->{levels} } ]; | ||||
273 | $clone->{'final'} = { %{ $this->{final} } }; | ||||
274 | $clone->restore($level) if defined $level; | ||||
275 | |||||
276 | return $clone; | ||||
277 | } | ||||
278 | |||||
279 | =begin TML | ||||
280 | |||||
281 | ---++ ObjectMethod restore($level) | ||||
282 | |||||
283 | Restores tha stack to the state it was in the given $level. | ||||
284 | |||||
285 | =cut | ||||
286 | |||||
287 | # spent 29.3ms within Foswiki::Prefs::Stack::restore which was called 20 times, avg 1.47ms/call:
# 20 times (29.3ms+0s) by Foswiki::Prefs::popTopicContext at line 306 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Prefs.pm, avg 1.47ms/call | ||||
288 | 20 | 41µs | my ( $this, $level ) = @_; | ||
289 | |||||
290 | 480 | 1.61ms | my @keys = grep { $this->{final}{$_} > $level } keys %{ $this->{final} }; | ||
291 | 40 | 93µs | delete @{ $this->{final} }{@keys}; | ||
292 | 40 | 310µs | splice @{ $this->{levels} }, $level + 1; | ||
293 | |||||
294 | 20 | 148µs | my $mask = | ||
295 | ( chr(0xFF) x int( $level / 8 ) ) | ||||
296 | . chr( ( 2**( ( $level % 8 ) + 1 ) ) - 1 ); | ||||
297 | 40 | 1.05ms | foreach my $p ( keys %{ $this->{'map'} } ) { | ||
298 | 3372 | 5.53ms | $this->{'map'}{$p} &= $mask; | ||
299 | |||||
300 | 3372 | 10.1ms | while ( length( $this->{'map'}{$p} ) > 0 | ||
301 | && ord( substr( $this->{'map'}{$p}, -1 ) ) == 0 ) | ||||
302 | { | ||||
303 | 204 | 1.04ms | substr( $this->{'map'}{$p}, -1 ) = ''; | ||
304 | } | ||||
305 | |||||
306 | 3372 | 9.46ms | delete $this->{'map'}{$p} if length( $this->{'map'}{$p} ) == 0; | ||
307 | } | ||||
308 | } | ||||
309 | |||||
310 | 1 | 4µs | 1; | ||
311 | __END__ |