Filename | /home/ss5/perl5/perlbrew/perls/perl-5.14.1/lib/site_perl/5.14.1/Data/DPath/Context.pm |
Statements | Executed 60 statements in 15.3ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 10.9ms | 181ms | BEGIN@23 | Data::DPath::Context::
1 | 1 | 1 | 7.18ms | 59.7ms | BEGIN@19 | Data::DPath::Context::
1 | 1 | 1 | 2.91ms | 9.16ms | BEGIN@18 | Data::DPath::Context::
1 | 1 | 1 | 2.77ms | 48.6ms | BEGIN@22 | Data::DPath::Context::
1 | 1 | 1 | 2.58ms | 15.3ms | BEGIN@16 | Data::DPath::Context::
1 | 1 | 1 | 2.33ms | 3.68ms | BEGIN@17 | Data::DPath::Context::
1 | 1 | 1 | 65µs | 3.40ms | BEGIN@29 | Data::DPath::Context::
1 | 1 | 1 | 61µs | 256µs | BEGIN@20 | Data::DPath::Context::
1 | 1 | 1 | 56µs | 796µs | BEGIN@64 | Data::DPath::Context::
1 | 1 | 1 | 48µs | 82µs | BEGIN@223 | Data::DPath::Context::
1 | 1 | 1 | 46µs | 580µs | BEGIN@57 | Data::DPath::Context::
1 | 1 | 1 | 44µs | 104µs | BEGIN@102 | Data::DPath::Context::
1 | 1 | 1 | 43µs | 108µs | BEGIN@424 | Data::DPath::Context::
1 | 1 | 1 | 37µs | 37µs | BEGIN@2 | Data::DPath::Context::
1 | 1 | 1 | 36µs | 90µs | BEGIN@158 | Data::DPath::Context::
1 | 1 | 1 | 36µs | 81µs | BEGIN@281 | Data::DPath::Context::
1 | 1 | 1 | 35µs | 80µs | BEGIN@204 | Data::DPath::Filters::
1 | 1 | 1 | 31µs | 81µs | BEGIN@425 | Data::DPath::Context::
1 | 1 | 1 | 31µs | 72µs | BEGIN@159 | Data::DPath::Context::
1 | 1 | 1 | 30µs | 64µs | BEGIN@305 | Data::DPath::Context::
1 | 1 | 1 | 29µs | 71µs | BEGIN@222 | Data::DPath::Context::
1 | 1 | 1 | 29µs | 162µs | BEGIN@13 | Data::DPath::Context::
1 | 1 | 1 | 28µs | 43µs | BEGIN@10 | Data::DPath::Context::
1 | 1 | 1 | 27µs | 56µs | BEGIN@11 | Data::DPath::Context::
1 | 1 | 1 | 27µs | 27µs | BEGIN@33 | Data::DPath::Filters::
1 | 1 | 1 | 27µs | 390µs | BEGIN@14 | Data::DPath::Context::
1 | 1 | 1 | 27µs | 2.05ms | BEGIN@15 | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | __ANON__[:411] | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _all | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _any | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _filter_points | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _filter_points_eval | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _filter_points_index | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _iter | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _search | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _select_ancestor | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _select_ancestor_or_self | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _select_anystep | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _select_anywhere | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _select_key | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _select_nostep | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _select_parent | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _select_root | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | _splice_threads | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | all_points | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | deref | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | first_point | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | isearch | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | match | Data::DPath::Context::
0 | 0 | 0 | 0s | 0s | ref | Data::DPath::Context::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package Data::DPath::Context; | ||||
2 | # spent 37µs within Data::DPath::Context::BEGIN@2 which was called:
# once (37µs+0s) by Data::DPath::Path::BEGIN@1.2 at line 4 | ||||
3 | 1 | 20µs | $Data::DPath::Context::AUTHORITY = 'cpan:SCHWIGON'; | ||
4 | 1 | 124µs | 1 | 37µs | } # spent 37µs making 1 call to Data::DPath::Context::BEGIN@2 |
5 | { | ||||
6 | 2 | 6µs | $Data::DPath::Context::VERSION = '0.48'; | ||
7 | } | ||||
8 | # ABSTRACT: Abstraction for a current context that enables incremental searches | ||||
9 | |||||
10 | 2 | 89µs | 2 | 58µs | # spent 43µs (28+15) within Data::DPath::Context::BEGIN@10 which was called:
# once (28µs+15µs) by Data::DPath::Path::BEGIN@1.2 at line 10 # spent 43µs making 1 call to Data::DPath::Context::BEGIN@10
# spent 15µs making 1 call to strict::import |
11 | 2 | 93µs | 2 | 85µs | # spent 56µs (27+29) within Data::DPath::Context::BEGIN@11 which was called:
# once (27µs+29µs) by Data::DPath::Path::BEGIN@1.2 at line 11 # spent 56µs making 1 call to Data::DPath::Context::BEGIN@11
# spent 29µs making 1 call to warnings::import |
12 | |||||
13 | 2 | 103µs | 2 | 295µs | # spent 162µs (29+133) within Data::DPath::Context::BEGIN@13 which was called:
# once (29µs+133µs) by Data::DPath::Path::BEGIN@1.2 at line 13 # spent 162µs making 1 call to Data::DPath::Context::BEGIN@13
# spent 133µs making 1 call to Exporter::import |
14 | 2 | 109µs | 2 | 390µs | # spent 390µs (27+363) within Data::DPath::Context::BEGIN@14 which was called:
# once (27µs+363µs) by Data::DPath::Path::BEGIN@1.2 at line 14 # spent 390µs making 1 call to Data::DPath::Context::BEGIN@14
# spent 363µs making 1 call to aliased::import, recursion: max depth 1, sum of overlapping time 363µs |
15 | 2 | 135µs | 2 | 2.05ms | # spent 2.05ms (27µs+2.03) within Data::DPath::Context::BEGIN@15 which was called:
# once (27µs+2.03ms) by Data::DPath::Path::BEGIN@1.2 at line 15 # spent 2.05ms making 1 call to Data::DPath::Context::BEGIN@15
# spent 2.03ms making 1 call to aliased::import, recursion: max depth 1, sum of overlapping time 2.03ms |
16 | 2 | 466µs | 2 | 15.8ms | # spent 15.3ms (2.58+12.7) within Data::DPath::Context::BEGIN@16 which was called:
# once (2.58ms+12.7ms) by Data::DPath::Path::BEGIN@1.2 at line 16 # spent 15.3ms making 1 call to Data::DPath::Context::BEGIN@16
# spent 452µs making 1 call to Exporter::import |
17 | 2 | 482µs | 2 | 3.96ms | # spent 3.68ms (2.33+1.35) within Data::DPath::Context::BEGIN@17 which was called:
# once (2.33ms+1.35ms) by Data::DPath::Path::BEGIN@1.2 at line 17 # spent 3.68ms making 1 call to Data::DPath::Context::BEGIN@17
# spent 278µs making 1 call to Exporter::import |
18 | 2 | 490µs | 1 | 9.16ms | # spent 9.16ms (2.91+6.25) within Data::DPath::Context::BEGIN@18 which was called:
# once (2.91ms+6.25ms) by Data::DPath::Path::BEGIN@1.2 at line 18 # spent 9.16ms making 1 call to Data::DPath::Context::BEGIN@18 |
19 | 2 | 475µs | 2 | 60.1ms | # spent 59.7ms (7.18+52.5) within Data::DPath::Context::BEGIN@19 which was called:
# once (7.18ms+52.5ms) by Data::DPath::Path::BEGIN@1.2 at line 19 # spent 59.7ms making 1 call to Data::DPath::Context::BEGIN@19
# spent 354µs making 1 call to Exporter::import |
20 | 2 | 137µs | 2 | 450µs | # spent 256µs (61+195) within Data::DPath::Context::BEGIN@20 which was called:
# once (61µs+195µs) by Data::DPath::Path::BEGIN@1.2 at line 20 # spent 256µs making 1 call to Data::DPath::Context::BEGIN@20
# spent 195µs making 1 call to Exporter::import |
21 | #use Sys::CPU; | ||||
22 | 2 | 530µs | 2 | 74.4ms | # spent 48.6ms (2.77+45.8) within Data::DPath::Context::BEGIN@22 which was called:
# once (2.77ms+45.8ms) by Data::DPath::Path::BEGIN@1.2 at line 22 # spent 48.6ms making 1 call to Data::DPath::Context::BEGIN@22
# spent 25.8ms making 1 call to POSIX::import |
23 | 2 | 732µs | 1 | 181ms | # spent 181ms (10.9+170) within Data::DPath::Context::BEGIN@23 which was called:
# once (10.9ms+170ms) by Data::DPath::Path::BEGIN@1.2 at line 23 # spent 181ms making 1 call to Data::DPath::Context::BEGIN@23 |
24 | |||||
25 | # run filter expressions in own Safe.pm compartment | ||||
26 | 1 | 600ns | our $COMPARTMENT; | ||
27 | 1 | 500ns | our $THREADCOUNT; | ||
28 | |||||
29 | # spent 3.40ms (65µs+3.33) within Data::DPath::Context::BEGIN@29 which was called:
# once (65µs+3.33ms) by Data::DPath::Path::BEGIN@1.2 at line 51 | ||||
30 | #$THREADCOUNT = $Data::DPath::PARALLELIZE ? Sys::CPU::cpu_count : 1; | ||||
31 | #print "THREADCOUNT: $THREADCOUNT\n"; | ||||
32 | package Data::DPath::Filters; | ||||
33 | # spent 27µs within Data::DPath::Filters::BEGIN@33 which was called:
# once (27µs+0s) by Data::DPath::Path::BEGIN@1.2 at line 35 | ||||
34 | 1 | 18µs | $Data::DPath::Filters::AUTHORITY = 'cpan:SCHWIGON'; | ||
35 | 1 | 286µs | 1 | 27µs | } # spent 27µs making 1 call to Data::DPath::Filters::BEGIN@33 |
36 | { | ||||
37 | 5 | 45µs | $Data::DPath::Filters::VERSION = '0.48'; | ||
38 | } | ||||
39 | 1 | 2.87ms | $COMPARTMENT = Safe->new; # spent 2.87ms making 1 call to Safe::new | ||
40 | 1 | 44µs | $COMPARTMENT->permit(qw":base_core"); # spent 44µs making 1 call to Safe::permit | ||
41 | # map DPath filter functions into new namespace | ||||
42 | 1 | 418µs | $COMPARTMENT->share(qw(affe # spent 418µs making 1 call to Safe::share | ||
43 | idx | ||||
44 | size | ||||
45 | key | ||||
46 | value | ||||
47 | isa | ||||
48 | reftype | ||||
49 | is_reftype | ||||
50 | )); | ||||
51 | 1 | 209µs | 1 | 3.40ms | } # spent 3.40ms making 1 call to Data::DPath::Context::BEGIN@29 |
52 | |||||
53 | # print "use $]\n" if $] >= 5.010; # allow new-school Perl inside filter expressions | ||||
54 | # eval "use $]" if $] >= 5.010; # allow new-school Perl inside filter expressions | ||||
55 | |||||
56 | use Class::XSAccessor::Array | ||||
57 | 1 | 534µs | # spent 580µs (46+534) within Data::DPath::Context::BEGIN@57 which was called:
# once (46µs+534µs) by Data::DPath::Path::BEGIN@1.2 at line 62 # spent 534µs making 1 call to Class::XSAccessor::Array::import | ||
58 | constructor => 'new', | ||||
59 | accessors => { | ||||
60 | current_points => 0, | ||||
61 | give_references => 1, | ||||
62 | 2 | 346µs | 1 | 580µs | }; # spent 580µs making 1 call to Data::DPath::Context::BEGIN@57 |
63 | |||||
64 | 1 | 740µs | # spent 796µs (56+740) within Data::DPath::Context::BEGIN@64 which was called:
# once (56µs+740µs) by Data::DPath::Path::BEGIN@1.2 at line 75 # spent 740µs making 1 call to constant::import | ||
65 | ARRAY => 'ARRAY', | ||||
66 | SCALAR => 'SCALAR', | ||||
67 | ROOT => 'ROOT', | ||||
68 | ANYWHERE => 'ANYWHERE', | ||||
69 | KEY => 'KEY', | ||||
70 | ANYSTEP => 'ANYSTEP', | ||||
71 | NOSTEP => 'NOSTEP', | ||||
72 | PARENT => 'PARENT', | ||||
73 | ANCESTOR => 'ANCESTOR', | ||||
74 | ANCESTOR_OR_SELF => 'ANCESTOR_OR_SELF', | ||||
75 | 2 | 697µs | 1 | 796µs | }; # spent 796µs making 1 call to Data::DPath::Context::BEGIN@64 |
76 | |||||
77 | sub _splice_threads { | ||||
78 | my ($cargo) = @_; | ||||
79 | |||||
80 | my $nr_cargo = @$cargo; | ||||
81 | |||||
82 | return [[]] unless $nr_cargo; | ||||
83 | |||||
84 | my $threadcount = $THREADCOUNT || 1; | ||||
85 | my $blocksize = ceil ($nr_cargo / $threadcount); | ||||
86 | |||||
87 | my @result = map { | ||||
88 | my $first = $_ * $blocksize; | ||||
89 | my $last = min(($_+1) * $blocksize - 1, $nr_cargo-1); | ||||
90 | ($first <= $last) ? [ @$cargo[$first .. $last]] : (); | ||||
91 | } 0 .. $threadcount-1; | ||||
92 | |||||
93 | return \@result; | ||||
94 | } | ||||
95 | |||||
96 | # only finds "inner" values; if you need the outer start value | ||||
97 | # then just wrap it into one more level of array brackets. | ||||
98 | sub _any | ||||
99 | { | ||||
100 | my ($out, $in, $lookahead_key) = @_; | ||||
101 | |||||
102 | 2 | 1.48ms | 2 | 165µs | # spent 104µs (44+60) within Data::DPath::Context::BEGIN@102 which was called:
# once (44µs+60µs) by Data::DPath::Path::BEGIN@1.2 at line 102 # spent 104µs making 1 call to Data::DPath::Context::BEGIN@102
# spent 60µs making 1 call to warnings::unimport |
103 | |||||
104 | $in = defined $in ? $in : []; | ||||
105 | return @$out unless @$in; | ||||
106 | |||||
107 | my @newin; | ||||
108 | my @newout; | ||||
109 | my $tmp_ref; | ||||
110 | my $tmp_deref; | ||||
111 | my $tmp_reftype; | ||||
112 | |||||
113 | foreach my $point (@$in) { | ||||
114 | my @values; | ||||
115 | next unless defined $point; | ||||
116 | my $ref = $point->ref; | ||||
117 | |||||
118 | # speed optimization: first try faster ref, then reftype | ||||
119 | if (ref($$ref) eq HASH or reftype($$ref) eq HASH) { | ||||
120 | @values = | ||||
121 | map { { val_ref => \($$ref->{$_}), key => $_ } } | ||||
122 | grep { | ||||
123 | # speed optimization: only consider a key if lookahead looks promising | ||||
124 | not defined $lookahead_key | ||||
125 | or $_ eq $lookahead_key | ||||
126 | or ($tmp_ref = ref($tmp_deref =$$ref->{$_})) eq HASH | ||||
127 | or $tmp_ref eq ARRAY | ||||
128 | or ($tmp_reftype = reftype($tmp_deref)) eq HASH | ||||
129 | or $tmp_reftype eq ARRAY | ||||
130 | # or HASH_or_ARRAY(\($$ref->{$_})) | ||||
131 | } | ||||
132 | keys %{$$ref}; | ||||
133 | } | ||||
134 | elsif (ref($$ref) eq ARRAY or reftype($$ref) eq ARRAY) { | ||||
135 | @values = map { { val_ref => \$_ } } @{$$ref} | ||||
136 | } | ||||
137 | else { | ||||
138 | next | ||||
139 | } | ||||
140 | |||||
141 | foreach (@values) | ||||
142 | { | ||||
143 | my $key = $_->{key}; | ||||
144 | my $val_ref = $_->{val_ref}; | ||||
145 | my $newpoint = Point->new->ref($val_ref)->parent($point); | ||||
146 | $newpoint->attrs( Attrs->new(key => $key)) if $key; | ||||
147 | push @newout, $newpoint; | ||||
148 | push @newin, $newpoint; | ||||
149 | } | ||||
150 | } | ||||
151 | push @$out, @newout; | ||||
152 | return _any ($out, \@newin, $lookahead_key); | ||||
153 | } | ||||
154 | |||||
155 | sub _all { | ||||
156 | my ($self) = @_; | ||||
157 | |||||
158 | 2 | 106µs | 2 | 144µs | # spent 90µs (36+54) within Data::DPath::Context::BEGIN@158 which was called:
# once (36µs+54µs) by Data::DPath::Path::BEGIN@1.2 at line 158 # spent 90µs making 1 call to Data::DPath::Context::BEGIN@158
# spent 54µs making 1 call to strict::unimport |
159 | 2 | 915µs | 2 | 114µs | # spent 72µs (31+42) within Data::DPath::Context::BEGIN@159 which was called:
# once (31µs+42µs) by Data::DPath::Path::BEGIN@1.2 at line 159 # spent 72µs making 1 call to Data::DPath::Context::BEGIN@159
# spent 42µs making 1 call to warnings::unimport |
160 | |||||
161 | return | ||||
162 | map { $self->give_references ? $_ : $$_ } | ||||
163 | uniq | ||||
164 | map { defined $_ ? $_->ref : () } | ||||
165 | @{$self->current_points}; | ||||
166 | } | ||||
167 | |||||
168 | # filter current results by array index | ||||
169 | sub _filter_points_index { | ||||
170 | my ($self, $index, $points) = @_; | ||||
171 | |||||
172 | return $points ? [$points->[$index]] : []; | ||||
173 | } | ||||
174 | |||||
175 | # filter current results by condition | ||||
176 | sub _filter_points_eval | ||||
177 | { | ||||
178 | my ($self, $filter, $points) = @_; | ||||
179 | |||||
180 | return [] unless @$points; | ||||
181 | return $points unless defined $filter; | ||||
182 | |||||
183 | my $new_points; | ||||
184 | my $res; | ||||
185 | { | ||||
186 | package Data::DPath::Filters; | ||||
187 | |||||
188 | local our $idx = 0; | ||||
189 | $new_points = [ | ||||
190 | grep { | ||||
191 | local our $p = $_; | ||||
192 | local $_; | ||||
193 | my $pref = $p->ref; | ||||
194 | if ( defined $pref ) { | ||||
195 | $_ = $$pref; | ||||
196 | if ($Data::DPath::USE_SAFE) { | ||||
197 | # 'uninitialized' values are the norm | ||||
198 | # but "no warnings 'uninitialized'" does | ||||
199 | # not work in this restrictive Safe.pm config, so | ||||
200 | # we deactivate warnings completely by localizing $^W | ||||
201 | $res = $COMPARTMENT->reval('local $^W;'.$filter); | ||||
202 | } else { | ||||
203 | # 'uninitialized' values are the norm | ||||
204 | 2 | 465µs | 2 | 124µs | # spent 80µs (35+45) within Data::DPath::Filters::BEGIN@204 which was called:
# once (35µs+45µs) by Data::DPath::Path::BEGIN@1.2 at line 204 # spent 80µs making 1 call to Data::DPath::Filters::BEGIN@204
# spent 45µs making 1 call to warnings::unimport |
205 | $res = eval($filter); | ||||
206 | } | ||||
207 | print STDERR ($@, "\n") if $@; | ||||
208 | } else { | ||||
209 | $res = 0; | ||||
210 | } | ||||
211 | $idx++; | ||||
212 | $res; | ||||
213 | } @$points | ||||
214 | ]; | ||||
215 | } | ||||
216 | return $new_points; | ||||
217 | } | ||||
218 | |||||
219 | sub _filter_points { | ||||
220 | my ($self, $step, $points) = @_; | ||||
221 | |||||
222 | 2 | 103µs | 2 | 113µs | # spent 71µs (29+42) within Data::DPath::Context::BEGIN@222 which was called:
# once (29µs+42µs) by Data::DPath::Path::BEGIN@1.2 at line 222 # spent 71µs making 1 call to Data::DPath::Context::BEGIN@222
# spent 42µs making 1 call to strict::unimport |
223 | 2 | 1.20ms | 2 | 116µs | # spent 82µs (48+34) within Data::DPath::Context::BEGIN@223 which was called:
# once (48µs+34µs) by Data::DPath::Path::BEGIN@1.2 at line 223 # spent 82µs making 1 call to Data::DPath::Context::BEGIN@223
# spent 34µs making 1 call to warnings::unimport |
224 | |||||
225 | return [] unless @$points; | ||||
226 | |||||
227 | my $filter = $step->filter; | ||||
228 | return $points unless defined $filter; | ||||
229 | |||||
230 | $filter =~ s/^\[\s*(.*?)\s*\]$/$1/; # strip brackets and whitespace | ||||
231 | |||||
232 | if ($filter =~ /^-?\d+$/) | ||||
233 | { | ||||
234 | return $self->_filter_points_index($filter, $points); # simple array index | ||||
235 | } | ||||
236 | elsif ($filter =~ /\S/) | ||||
237 | { | ||||
238 | return $self->_filter_points_eval($filter, $points); # full condition | ||||
239 | } | ||||
240 | else | ||||
241 | { | ||||
242 | return $points; | ||||
243 | } | ||||
244 | } | ||||
245 | |||||
246 | # the root node | ||||
247 | # (only makes sense at first step, but currently not asserted) | ||||
248 | sub _select_root { | ||||
249 | my ($self, $step, $current_points, $new_points) = @_; | ||||
250 | |||||
251 | my $step_points = $self->_filter_points($step, $current_points); | ||||
252 | push @$new_points, @$step_points; | ||||
253 | } | ||||
254 | |||||
255 | |||||
256 | # // | ||||
257 | # anywhere in the tree | ||||
258 | sub _select_anywhere { | ||||
259 | my ($self, $step, $current_points, $lookahead, $new_points) = @_; | ||||
260 | |||||
261 | # speed optimization: only useful points added | ||||
262 | my $lookahead_key; | ||||
263 | if (defined $lookahead and $lookahead->kind eq KEY) { | ||||
264 | $lookahead_key = $lookahead->part; | ||||
265 | } | ||||
266 | |||||
267 | # '//' | ||||
268 | # all hash/array nodes of a data structure | ||||
269 | foreach my $point (@$current_points) { | ||||
270 | my $step_points = [_any([], [ $point ], $lookahead_key), $point]; | ||||
271 | push @$new_points, @{$self->_filter_points($step, $step_points)}; | ||||
272 | } | ||||
273 | } | ||||
274 | |||||
275 | # /key | ||||
276 | # the value of a key | ||||
277 | sub _select_key { | ||||
278 | my ($self, $step, $current_points, $new_points) = @_; | ||||
279 | |||||
280 | foreach my $point (@$current_points) { | ||||
281 | 2 | 648µs | 2 | 126µs | # spent 81µs (36+45) within Data::DPath::Context::BEGIN@281 which was called:
# once (36µs+45µs) by Data::DPath::Path::BEGIN@1.2 at line 281 # spent 81µs making 1 call to Data::DPath::Context::BEGIN@281
# spent 45µs making 1 call to warnings::unimport |
282 | next unless defined $point; | ||||
283 | my $pref = $point->ref; | ||||
284 | next unless ( | ||||
285 | # speed optimization: | ||||
286 | # first try faster ref, then reftype | ||||
287 | ref($$pref) eq HASH or | ||||
288 | reftype($$pref) eq HASH | ||||
289 | ); | ||||
290 | # take point as hash, skip undefs | ||||
291 | my $attrs = Attrs->new(key => $step->part); | ||||
292 | my $step_points = []; | ||||
293 | if (exists $$pref->{$step->part}) { | ||||
294 | $step_points = [ Point->new->ref(\($$pref->{$step->part}))->parent($point)->attrs($attrs) ]; | ||||
295 | } | ||||
296 | push @$new_points, @{$self->_filter_points($step, $step_points)}; | ||||
297 | } | ||||
298 | } | ||||
299 | |||||
300 | # '*' | ||||
301 | # all leaves of a data tree | ||||
302 | sub _select_anystep { | ||||
303 | my ($self, $step, $current_points, $new_points) = @_; | ||||
304 | |||||
305 | 2 | 2.98ms | 2 | 98µs | # spent 64µs (30+34) within Data::DPath::Context::BEGIN@305 which was called:
# once (30µs+34µs) by Data::DPath::Path::BEGIN@1.2 at line 305 # spent 64µs making 1 call to Data::DPath::Context::BEGIN@305
# spent 34µs making 1 call to warnings::unimport |
306 | foreach my $point (@$current_points) { | ||||
307 | # take point as array | ||||
308 | my $pref = $point->ref; | ||||
309 | my $ref = $$pref; | ||||
310 | my $step_points = []; | ||||
311 | # speed optimization: first try faster ref, then reftype | ||||
312 | if (ref($ref) eq HASH or reftype($ref) eq HASH) { | ||||
313 | $step_points = [ map { | ||||
314 | my $v_ref = \($ref->{$_}); | ||||
315 | my $attrs = Attrs->new(key => $_); | ||||
316 | Point->new->ref($v_ref)->parent($point)->attrs($attrs) | ||||
317 | } keys %$ref ]; | ||||
318 | } elsif (ref($ref) eq ARRAY or reftype($ref) eq ARRAY) { | ||||
319 | $step_points = [ map { | ||||
320 | Point->new->ref(\$_)->parent($point) | ||||
321 | } @$ref ]; | ||||
322 | } else { | ||||
323 | if (ref($pref) eq SCALAR or reftype($pref) eq SCALAR) { | ||||
324 | # TODO: without map, it's just one value | ||||
325 | $step_points = [ #map { | ||||
326 | Point->new->ref($pref)->parent($point) # XXX? why $_? What happens to $pref? | ||||
327 | ]; # } $ref ]; | ||||
328 | } | ||||
329 | } | ||||
330 | push @$new_points, @{ $self->_filter_points($step, $step_points) }; | ||||
331 | } | ||||
332 | } | ||||
333 | |||||
334 | # '.' | ||||
335 | # no step (neither up nor down), just allow filtering | ||||
336 | sub _select_nostep { | ||||
337 | my ($self, $step, $current_points, $new_points) = @_; | ||||
338 | |||||
339 | foreach my $point (@{$current_points}) { | ||||
340 | my $step_points = [$point]; | ||||
341 | push @$new_points, @{ $self->_filter_points($step, $step_points) }; | ||||
342 | } | ||||
343 | } | ||||
344 | |||||
345 | # '..' | ||||
346 | # the parent | ||||
347 | sub _select_parent { | ||||
348 | my ($self, $step, $current_points, $new_points) = @_; | ||||
349 | |||||
350 | foreach my $point (@{$current_points}) { | ||||
351 | next unless defined $point; | ||||
352 | my $step_points = [$point->parent]; | ||||
353 | push @$new_points, @{ $self->_filter_points($step, $step_points) }; | ||||
354 | } | ||||
355 | } | ||||
356 | |||||
357 | # '::ancestor' | ||||
358 | # all ancestors (parent, grandparent, etc.) of the current node | ||||
359 | sub _select_ancestor { | ||||
360 | my ($self, $step, $current_points, $new_points) = @_; | ||||
361 | |||||
362 | foreach my $point (@{$current_points}) { | ||||
363 | my $step_points = []; | ||||
364 | my $parent = $point; | ||||
365 | while ($parent = $parent->parent) { | ||||
366 | push @$step_points, $parent; # order matters | ||||
367 | } | ||||
368 | push @$new_points, @{ $self->_filter_points($step, $step_points) }; | ||||
369 | } | ||||
370 | } | ||||
371 | |||||
372 | # '::ancestor-or-self' | ||||
373 | # all ancestors (parent, grandparent, etc.) of the current node and the current node itself | ||||
374 | sub _select_ancestor_or_self { | ||||
375 | my ($self, $step, $current_points, $new_points) = @_; | ||||
376 | |||||
377 | foreach my $point (@{$current_points}) { | ||||
378 | my $step_points = [$point]; | ||||
379 | my $parent = $point; | ||||
380 | while ($parent = $parent->parent) { | ||||
381 | push @$step_points, $parent; # order matters | ||||
382 | } | ||||
383 | push @$new_points, @{ $self->_filter_points($step, $step_points) }; | ||||
384 | } | ||||
385 | } | ||||
386 | |||||
387 | sub ref { | ||||
388 | my ($self) = @_; | ||||
389 | $self->first_point->{ref}; | ||||
390 | } | ||||
391 | |||||
392 | sub deref { | ||||
393 | my ($self) = @_; | ||||
394 | ${$self->ref}; | ||||
395 | } | ||||
396 | |||||
397 | sub first_point { | ||||
398 | my ($self) = @_; | ||||
399 | $self->current_points->[0]; | ||||
400 | } | ||||
401 | |||||
402 | sub all_points { | ||||
403 | my ($self) = @_; | ||||
404 | iarray $self->current_points; | ||||
405 | } | ||||
406 | |||||
407 | sub _iter { | ||||
408 | my ($self) = @_; | ||||
409 | |||||
410 | my $iter = iarray $self->current_points; | ||||
411 | return imap { __PACKAGE__->new->current_points([ $_ ]) } $iter; | ||||
412 | } | ||||
413 | |||||
414 | sub isearch | ||||
415 | { | ||||
416 | my ($self, $path_str) = @_; | ||||
417 | $self->_search(Data::DPath::Path->new(path => $path_str))->_iter; | ||||
418 | } | ||||
419 | |||||
420 | sub _search | ||||
421 | { | ||||
422 | my ($self, $dpath) = @_; | ||||
423 | |||||
424 | 2 | 109µs | 2 | 172µs | # spent 108µs (43+65) within Data::DPath::Context::BEGIN@424 which was called:
# once (43µs+65µs) by Data::DPath::Path::BEGIN@1.2 at line 424 # spent 108µs making 1 call to Data::DPath::Context::BEGIN@424
# spent 65µs making 1 call to strict::unimport |
425 | 2 | 1.65ms | 2 | 130µs | # spent 81µs (31+50) within Data::DPath::Context::BEGIN@425 which was called:
# once (31µs+50µs) by Data::DPath::Path::BEGIN@1.2 at line 425 # spent 81µs making 1 call to Data::DPath::Context::BEGIN@425
# spent 50µs making 1 call to warnings::unimport |
426 | |||||
427 | my $current_points = $self->current_points; | ||||
428 | my $steps = $dpath->_steps; | ||||
429 | for (my $i = 0; $i < @$steps; $i++) { | ||||
430 | my $step = $steps->[$i]; | ||||
431 | my $lookahead = $steps->[$i+1]; | ||||
432 | my $new_points = []; | ||||
433 | |||||
434 | if ($step->kind eq ROOT) | ||||
435 | { | ||||
436 | $self->_select_root($step, $current_points, $new_points); | ||||
437 | } | ||||
438 | elsif ($step->kind eq ANYWHERE) | ||||
439 | { | ||||
440 | $self->_select_anywhere($step, $current_points, $lookahead, $new_points); | ||||
441 | } | ||||
442 | elsif ($step->kind eq KEY) | ||||
443 | { | ||||
444 | $self->_select_key($step, $current_points, $new_points); | ||||
445 | } | ||||
446 | elsif ($step->kind eq ANYSTEP) | ||||
447 | { | ||||
448 | $self->_select_anystep($step, $current_points, $new_points); | ||||
449 | } | ||||
450 | elsif ($step->kind eq NOSTEP) | ||||
451 | { | ||||
452 | $self->_select_nostep($step, $current_points, $new_points); | ||||
453 | } | ||||
454 | elsif ($step->kind eq PARENT) | ||||
455 | { | ||||
456 | $self->_select_parent($step, $current_points, $new_points); | ||||
457 | } | ||||
458 | elsif ($step->kind eq ANCESTOR) | ||||
459 | { | ||||
460 | $self->_select_ancestor($step, $current_points, $new_points); | ||||
461 | } | ||||
462 | elsif ($step->kind eq ANCESTOR_OR_SELF) | ||||
463 | { | ||||
464 | $self->_select_ancestor_or_self($step, $current_points, $new_points); | ||||
465 | } | ||||
466 | $current_points = $new_points; | ||||
467 | } | ||||
468 | $self->current_points( $current_points ); | ||||
469 | return $self; | ||||
470 | } | ||||
471 | |||||
472 | sub match { | ||||
473 | my ($self, $dpath) = @_; | ||||
474 | |||||
475 | $self->_search($dpath)->_all; | ||||
476 | } | ||||
477 | |||||
478 | 1 | 16µs | 1; | ||
479 | |||||
- - | |||||
482 | =pod | ||||
483 | |||||
484 | =encoding utf-8 | ||||
485 | |||||
486 | =head1 NAME | ||||
487 | |||||
488 | Data::DPath::Context - Abstraction for a current context that enables incremental searches | ||||
489 | |||||
490 | =head1 API METHODS | ||||
491 | |||||
492 | =head2 new ( %args ) | ||||
493 | |||||
494 | Constructor; creates instance. | ||||
495 | |||||
496 | Args: | ||||
497 | |||||
498 | =over 4 | ||||
499 | |||||
500 | =item give_references | ||||
501 | |||||
502 | Default 0. If set to true value then results are references to the | ||||
503 | matched points in the data structure. | ||||
504 | |||||
505 | =back | ||||
506 | |||||
507 | =head2 match( $dpath ) | ||||
508 | |||||
509 | Return all data that match the given DPath. | ||||
510 | |||||
511 | =head2 isearch( $path_str ) | ||||
512 | |||||
513 | Searches a path relative to current context and returns an iterator. | ||||
514 | See L<Iterator style|Data::DPath/"Iterator style"> for usage. | ||||
515 | |||||
516 | =head2 ref() | ||||
517 | |||||
518 | It returns the reference to the actual data from the current context's | ||||
519 | first element. This mostly makes sense on contexts returned by | ||||
520 | iterators as there is only one point there. | ||||
521 | |||||
522 | (Having the reference theoretically allows you to even change the data | ||||
523 | on this point. It's not yet clear what impact this has to currently | ||||
524 | active iterators, which B<should> still return the original data but | ||||
525 | that's not yet tested. So don't rely on that behaviour.) | ||||
526 | |||||
527 | =head2 deref() | ||||
528 | |||||
529 | This is one dereference step on top of F<ref()>. It gives you the | ||||
530 | actual data found. Most of the time you want this. | ||||
531 | |||||
532 | =head2 first_point | ||||
533 | |||||
534 | On a current context consisting on a set of points it returns the | ||||
535 | first point. This makes most sense with Iterator style API when the | ||||
536 | current iterator contains exactly one point. | ||||
537 | |||||
538 | =head2 all_points | ||||
539 | |||||
540 | On a current context consisting on a set of points it returns all | ||||
541 | those. This method is a functional complement to F<first_point>. | ||||
542 | |||||
543 | =head1 UTILITY SUBS/METHODS | ||||
544 | |||||
545 | =head2 _all | ||||
546 | |||||
547 | Returns all values covered by current context. | ||||
548 | |||||
549 | If C<give_references> is set to true value then results are references | ||||
550 | to the matched points in the data structure. | ||||
551 | |||||
552 | =head2 _search( $dpath ) | ||||
553 | |||||
554 | Return new context for a DPath relative to current context. | ||||
555 | |||||
556 | =head2 _filter_points | ||||
557 | |||||
558 | Evaluates the filter condition in brackets. It differenciates between | ||||
559 | simple integers, which are taken as array index, and all other | ||||
560 | conditions, which are taken as evaled perl expression in a grep like | ||||
561 | expression onto the set of points found by current step. | ||||
562 | |||||
563 | =head2 current_points | ||||
564 | |||||
565 | Attribute / accessor. | ||||
566 | |||||
567 | =head2 give_references | ||||
568 | |||||
569 | Attribute / accessor. | ||||
570 | |||||
571 | =head1 aliased classes | ||||
572 | |||||
573 | That's just to make Pod::Coverage happy which does not handle aliased | ||||
574 | modules. | ||||
575 | |||||
576 | =head2 Context | ||||
577 | |||||
578 | =head2 Point | ||||
579 | |||||
580 | =head2 Step | ||||
581 | |||||
582 | =head1 AUTHOR | ||||
583 | |||||
584 | Steffen Schwigon <ss5@renormalist.net> | ||||
585 | |||||
586 | =head1 COPYRIGHT AND LICENSE | ||||
587 | |||||
588 | This software is copyright (c) 2012 by Steffen Schwigon. | ||||
589 | |||||
590 | This is free software; you can redistribute it and/or modify it under | ||||
591 | the same terms as the Perl 5 programming language system itself. | ||||
592 | |||||
593 | =cut | ||||
594 | |||||
595 | |||||
596 | __END__ |