Filename | /usr/share/perl5/Hash/Merge.pm |
Statements | Executed 913 statements in 3.50ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
96 | 1 | 1 | 1.10ms | 1.53ms | merge | Hash::Merge::
97 | 2 | 1 | 238µs | 238µs | _get_obj | Hash::Merge::
1 | 1 | 1 | 18µs | 31µs | BEGIN@3 | Hash::Merge::
1 | 1 | 1 | 15µs | 17µs | specify_behavior | Hash::Merge::
1 | 1 | 1 | 10µs | 51µs | BEGIN@8 | Hash::Merge::
1 | 1 | 1 | 10µs | 20µs | BEGIN@4 | Hash::Merge::
1 | 1 | 1 | 10µs | 49µs | BEGIN@5 | Hash::Merge::
1 | 1 | 1 | 10µs | 59µs | BEGIN@7 | Hash::Merge::
1 | 1 | 1 | 6µs | 6µs | new | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:23] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:24] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:25] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:284] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:28] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:291] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:298] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:29] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:30] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:33] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:34] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:35] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:41] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:42] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:43] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:46] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:47] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:48] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:51] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:52] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:53] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:59] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:60] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:61] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:64] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:65] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:66] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:69] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:70] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:71] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:77] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:78] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:79] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:82] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:83] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:84] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:87] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:88] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | __ANON__[:89] | Hash::Merge::
0 | 0 | 0 | 0s | 0s | _hashify | Hash::Merge::
0 | 0 | 0 | 0s | 0s | _merge_hashes | Hash::Merge::
0 | 0 | 0 | 0s | 0s | _my_clone | Hash::Merge::
0 | 0 | 0 | 0s | 0s | get_behavior | Hash::Merge::
0 | 0 | 0 | 0s | 0s | get_clone_behavior | Hash::Merge::
0 | 0 | 0 | 0s | 0s | set_behavior | Hash::Merge::
0 | 0 | 0 | 0s | 0s | set_clone_behavior | Hash::Merge::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package Hash::Merge; | ||||
2 | |||||
3 | 2 | 48µs | 2 | 43µs | # spent 31µs (18+12) within Hash::Merge::BEGIN@3 which was called:
# once (18µs+12µs) by DBIx::Class::ResultSet::_merge_attr at line 3 # spent 31µs making 1 call to Hash::Merge::BEGIN@3
# spent 12µs making 1 call to strict::import |
4 | 2 | 39µs | 2 | 30µs | # spent 20µs (10+10) within Hash::Merge::BEGIN@4 which was called:
# once (10µs+10µs) by DBIx::Class::ResultSet::_merge_attr at line 4 # spent 20µs making 1 call to Hash::Merge::BEGIN@4
# spent 10µs making 1 call to warnings::import |
5 | 2 | 75µs | 2 | 88µs | # spent 49µs (10+39) within Hash::Merge::BEGIN@5 which was called:
# once (10µs+39µs) by DBIx::Class::ResultSet::_merge_attr at line 5 # spent 49µs making 1 call to Hash::Merge::BEGIN@5
# spent 39µs making 1 call to Exporter::import |
6 | |||||
7 | 2 | 87µs | 2 | 109µs | # spent 59µs (10+50) within Hash::Merge::BEGIN@7 which was called:
# once (10µs+50µs) by DBIx::Class::ResultSet::_merge_attr at line 7 # spent 59µs making 1 call to Hash::Merge::BEGIN@7
# spent 50µs making 1 call to base::import |
8 | 2 | 1.85ms | 2 | 92µs | # spent 51µs (10+41) within Hash::Merge::BEGIN@8 which was called:
# once (10µs+41µs) by DBIx::Class::ResultSet::_merge_attr at line 8 # spent 51µs making 1 call to Hash::Merge::BEGIN@8
# spent 41µs making 1 call to vars::import |
9 | |||||
10 | 1 | 200ns | my ( $GLOBAL, $clone ); | ||
11 | |||||
12 | 1 | 500ns | $VERSION = '0.200'; | ||
13 | 1 | 1µs | @EXPORT_OK = qw( merge _hashify _merge_hashes ); | ||
14 | 1 | 2µs | %EXPORT_TAGS = ( 'custom' => [qw( _hashify _merge_hashes )] ); | ||
15 | |||||
16 | 1 | 700ns | $GLOBAL = {}; | ||
17 | 1 | 1µs | bless $GLOBAL, __PACKAGE__; | ||
18 | 1 | 100ns | $context = $GLOBAL; # $context is a variable for merge and _merge_hashes. used by functions to respect calling context | ||
19 | |||||
20 | $GLOBAL->{'behaviors'} = { | ||||
21 | 'LEFT_PRECEDENT' => { | ||||
22 | 'SCALAR' => { | ||||
23 | 'SCALAR' => sub { $_[0] }, | ||||
24 | 'ARRAY' => sub { $_[0] }, | ||||
25 | 'HASH' => sub { $_[0] }, | ||||
26 | }, | ||||
27 | 'ARRAY' => { | ||||
28 | 'SCALAR' => sub { [ @{ $_[0] }, $_[1] ] }, | ||||
29 | 'ARRAY' => sub { [ @{ $_[0] }, @{ $_[1] } ] }, | ||||
30 | 'HASH' => sub { [ @{ $_[0] }, values %{ $_[1] } ] }, | ||||
31 | }, | ||||
32 | 'HASH' => { | ||||
33 | 'SCALAR' => sub { $_[0] }, | ||||
34 | 'ARRAY' => sub { $_[0] }, | ||||
35 | 'HASH' => sub { _merge_hashes( $_[0], $_[1] ) }, | ||||
36 | }, | ||||
37 | }, | ||||
38 | |||||
39 | 'RIGHT_PRECEDENT' => { | ||||
40 | 'SCALAR' => { | ||||
41 | 'SCALAR' => sub { $_[1] }, | ||||
42 | 'ARRAY' => sub { [ $_[0], @{ $_[1] } ] }, | ||||
43 | 'HASH' => sub { $_[1] }, | ||||
44 | }, | ||||
45 | 'ARRAY' => { | ||||
46 | 'SCALAR' => sub { $_[1] }, | ||||
47 | 'ARRAY' => sub { [ @{ $_[0] }, @{ $_[1] } ] }, | ||||
48 | 'HASH' => sub { $_[1] }, | ||||
49 | }, | ||||
50 | 'HASH' => { | ||||
51 | 'SCALAR' => sub { $_[1] }, | ||||
52 | 'ARRAY' => sub { [ values %{ $_[0] }, @{ $_[1] } ] }, | ||||
53 | 'HASH' => sub { _merge_hashes( $_[0], $_[1] ) }, | ||||
54 | }, | ||||
55 | }, | ||||
56 | |||||
57 | 'STORAGE_PRECEDENT' => { | ||||
58 | 'SCALAR' => { | ||||
59 | 'SCALAR' => sub { $_[0] }, | ||||
60 | 'ARRAY' => sub { [ $_[0], @{ $_[1] } ] }, | ||||
61 | 'HASH' => sub { $_[1] }, | ||||
62 | }, | ||||
63 | 'ARRAY' => { | ||||
64 | 'SCALAR' => sub { [ @{ $_[0] }, $_[1] ] }, | ||||
65 | 'ARRAY' => sub { [ @{ $_[0] }, @{ $_[1] } ] }, | ||||
66 | 'HASH' => sub { $_[1] }, | ||||
67 | }, | ||||
68 | 'HASH' => { | ||||
69 | 'SCALAR' => sub { $_[0] }, | ||||
70 | 'ARRAY' => sub { $_[0] }, | ||||
71 | 'HASH' => sub { _merge_hashes( $_[0], $_[1] ) }, | ||||
72 | }, | ||||
73 | }, | ||||
74 | |||||
75 | 'RETAINMENT_PRECEDENT' => { | ||||
76 | 'SCALAR' => { | ||||
77 | 'SCALAR' => sub { [ $_[0], $_[1] ] }, | ||||
78 | 'ARRAY' => sub { [ $_[0], @{ $_[1] } ] }, | ||||
79 | 'HASH' => sub { _merge_hashes( _hashify( $_[0] ), $_[1] ) }, | ||||
80 | }, | ||||
81 | 'ARRAY' => { | ||||
82 | 'SCALAR' => sub { [ @{ $_[0] }, $_[1] ] }, | ||||
83 | 'ARRAY' => sub { [ @{ $_[0] }, @{ $_[1] } ] }, | ||||
84 | 'HASH' => sub { _merge_hashes( _hashify( $_[0] ), $_[1] ) }, | ||||
85 | }, | ||||
86 | 'HASH' => { | ||||
87 | 'SCALAR' => sub { _merge_hashes( $_[0], _hashify( $_[1] ) ) }, | ||||
88 | 'ARRAY' => sub { _merge_hashes( $_[0], _hashify( $_[1] ) ) }, | ||||
89 | 'HASH' => sub { _merge_hashes( $_[0], $_[1] ) }, | ||||
90 | }, | ||||
91 | }, | ||||
92 | 1 | 49µs | }; | ||
93 | |||||
94 | 1 | 500ns | $GLOBAL->{'behavior'} = 'LEFT_PRECEDENT'; | ||
95 | 1 | 600ns | $GLOBAL->{'matrix'} = $GLOBAL->{behaviors}{ $GLOBAL->{'behavior'} }; | ||
96 | 1 | 300ns | $GLOBAL->{'clone'} = 1; | ||
97 | |||||
98 | sub _get_obj { | ||||
99 | 97 | 72µs | if ( my $type = ref $_[0] ) { | ||
100 | 97 | 316µs | return shift() if $type eq __PACKAGE__ || eval { $_[0]->isa(__PACKAGE__) }; | ||
101 | } | ||||
102 | |||||
103 | return $context; | ||||
104 | } | ||||
105 | |||||
106 | # spent 6µs within Hash::Merge::new which was called:
# once (6µs+0s) by DBIx::Class::ResultSet::_merge_attr at line 3889 of DBIx/Class/ResultSet.pm | ||||
107 | 1 | 500ns | my $pkg = shift; | ||
108 | 1 | 300ns | $pkg = ref $pkg || $pkg; | ||
109 | 1 | 600ns | my $beh = shift || $context->{'behavior'}; | ||
110 | |||||
111 | 1 | 500ns | croak "Behavior '$beh' does not exist" if !exists $context->{'behaviors'}{$beh}; | ||
112 | |||||
113 | 1 | 8µs | return bless { | ||
114 | 'behavior' => $beh, | ||||
115 | 'matrix' => $context->{'behaviors'}{$beh}, | ||||
116 | }, $pkg; | ||||
117 | } | ||||
118 | |||||
119 | sub set_behavior { | ||||
120 | my $self = &_get_obj; # '&' + no args modifies current @_ | ||||
121 | my $value = uc(shift); | ||||
122 | if ( !exists $self->{'behaviors'}{$value} and !exists $GLOBAL->{'behaviors'}{$value} ) { | ||||
123 | carp 'Behavior must be one of : ' . join( ', ', keys %{ $self->{'behaviors'} }, keys %{ $GLOBAL->{'behaviors'}{$value} } ); | ||||
124 | return; | ||||
125 | } | ||||
126 | my $oldvalue = $self->{'behavior'}; | ||||
127 | $self->{'behavior'} = $value; | ||||
128 | $self->{'matrix'} = $self->{'behaviors'}{$value} || $GLOBAL->{'behaviors'}{$value}; | ||||
129 | return $oldvalue; # Use classic POSIX pattern for get/set: set returns previous value | ||||
130 | } | ||||
131 | |||||
132 | sub get_behavior { | ||||
133 | my $self = &_get_obj; # '&' + no args modifies current @_ | ||||
134 | return $self->{'behavior'}; | ||||
135 | } | ||||
136 | |||||
137 | # spent 17µs (15+2) within Hash::Merge::specify_behavior which was called:
# once (15µs+2µs) by DBIx::Class::ResultSet::_merge_attr at line 3963 of DBIx/Class/ResultSet.pm | ||||
138 | 1 | 700ns | 1 | 2µs | my $self = &_get_obj; # '&' + no args modifies current @_ # spent 2µs making 1 call to Hash::Merge::_get_obj |
139 | 1 | 500ns | my ( $matrix, $name ) = @_; | ||
140 | 1 | 100ns | $name ||= 'user defined'; | ||
141 | 1 | 700ns | if ( exists $self->{'behaviors'}{$name} ) { | ||
142 | carp "Behavior '$name' was already defined. Please take another name"; | ||||
143 | return; | ||||
144 | } | ||||
145 | |||||
146 | 1 | 600ns | my @required = qw( SCALAR ARRAY HASH ); | ||
147 | |||||
148 | 1 | 800ns | foreach my $left (@required) { | ||
149 | 3 | 1µs | foreach my $right (@required) { | ||
150 | 9 | 4µs | if ( !exists $matrix->{$left}->{$right} ) { | ||
151 | carp "Behavior does not specify action for '$left' merging with '$right'"; | ||||
152 | return; | ||||
153 | } | ||||
154 | } | ||||
155 | } | ||||
156 | |||||
157 | 1 | 400ns | $self->{'behavior'} = $name; | ||
158 | 1 | 4µs | $self->{'behaviors'}{$name} = $self->{'matrix'} = $matrix; | ||
159 | } | ||||
160 | |||||
161 | sub set_clone_behavior { | ||||
162 | my $self = &_get_obj; # '&' + no args modifies current @_ | ||||
163 | my $oldvalue = $self->{'clone'}; | ||||
164 | $self->{'clone'} = shift() ? 1 : 0; | ||||
165 | return $oldvalue; | ||||
166 | } | ||||
167 | |||||
168 | sub get_clone_behavior { | ||||
169 | my $self = &_get_obj; # '&' + no args modifies current @_ | ||||
170 | return $self->{'clone'}; | ||||
171 | } | ||||
172 | |||||
173 | # spent 1.53ms (1.10+423µs) within Hash::Merge::merge which was called 96 times, avg 16µs/call:
# 96 times (1.10ms+423µs) by DBIx::Class::ResultSet::_merge_attr at line 3967 of DBIx/Class/ResultSet.pm, avg 16µs/call | ||||
174 | 96 | 136µs | 96 | 236µs | my $self = &_get_obj; # '&' + no args modifies current @_ # spent 236µs making 96 calls to Hash::Merge::_get_obj, avg 2µs/call |
175 | |||||
176 | 96 | 35µs | my ( $left, $right ) = @_; | ||
177 | |||||
178 | # For the general use of this module, we want to create duplicates | ||||
179 | # of all data that is merged. This behavior can be shut off, but | ||||
180 | # can create havoc if references are used heavily. | ||||
181 | |||||
182 | 96 | 119µs | my $lefttype = | ||
183 | ref $left eq 'HASH' ? 'HASH' | ||||
184 | : ref $left eq 'ARRAY' ? 'ARRAY' | ||||
185 | : 'SCALAR'; | ||||
186 | |||||
187 | 96 | 66µs | my $righttype = | ||
188 | ref $right eq 'HASH' ? 'HASH' | ||||
189 | : ref $right eq 'ARRAY' ? 'ARRAY' | ||||
190 | : 'SCALAR'; | ||||
191 | |||||
192 | 96 | 43µs | if ( $self->{'clone'} ) { | ||
193 | $left = _my_clone( $left, 1 ); | ||||
194 | $right = _my_clone( $right, 1 ); | ||||
195 | } | ||||
196 | |||||
197 | 96 | 53µs | local $context = $self; | ||
198 | 96 | 464µs | 96 | 188µs | return $self->{'matrix'}->{$lefttype}{$righttype}->( $left, $right ); # spent 178µs making 91 calls to DBIx::Class::ResultSet::__ANON__[DBIx/Class/ResultSet.pm:3926], avg 2µs/call
# spent 6µs making 4 calls to DBIx::Class::ResultSet::__ANON__[DBIx/Class/ResultSet.pm:3913], avg 1µs/call
# spent 4µs making 1 call to DBIx::Class::ResultSet::__ANON__[DBIx/Class/ResultSet.pm:3919] |
199 | } | ||||
200 | |||||
201 | # This does a straight merge of hashes, delegating the merge-specific | ||||
202 | # work to 'merge' | ||||
203 | |||||
204 | sub _merge_hashes { | ||||
205 | my $self = &_get_obj; # '&' + no args modifies current @_ | ||||
206 | |||||
207 | my ( $left, $right ) = ( shift, shift ); | ||||
208 | if ( ref $left ne 'HASH' || ref $right ne 'HASH' ) { | ||||
209 | carp 'Arguments for _merge_hashes must be hash references'; | ||||
210 | return; | ||||
211 | } | ||||
212 | |||||
213 | my %newhash; | ||||
214 | foreach my $leftkey ( keys %$left ) { | ||||
215 | if ( exists $right->{$leftkey} ) { | ||||
216 | $newhash{$leftkey} = $self->merge( $left->{$leftkey}, $right->{$leftkey} ); | ||||
217 | } | ||||
218 | else { | ||||
219 | $newhash{$leftkey} = $self->{clone} ? $self->_my_clone( $left->{$leftkey} ) : $left->{$leftkey}; | ||||
220 | } | ||||
221 | } | ||||
222 | |||||
223 | foreach my $rightkey ( keys %$right ) { | ||||
224 | if ( !exists $left->{$rightkey} ) { | ||||
225 | $newhash{$rightkey} = $self->{clone} ? $self->_my_clone( $right->{$rightkey} ) : $right->{$rightkey}; | ||||
226 | } | ||||
227 | } | ||||
228 | |||||
229 | return \%newhash; | ||||
230 | } | ||||
231 | |||||
232 | # Given a scalar or an array, creates a new hash where for each item in | ||||
233 | # the passed scalar or array, the key is equal to the value. Returns | ||||
234 | # this new hash | ||||
235 | |||||
236 | sub _hashify { | ||||
237 | my $self = &_get_obj; # '&' + no args modifies current @_ | ||||
238 | my $arg = shift; | ||||
239 | if ( ref $arg eq 'HASH' ) { | ||||
240 | carp 'Arguement for _hashify must not be a HASH ref'; | ||||
241 | return; | ||||
242 | } | ||||
243 | |||||
244 | my %newhash; | ||||
245 | if ( ref $arg eq 'ARRAY' ) { | ||||
246 | foreach my $item (@$arg) { | ||||
247 | my $suffix = 2; | ||||
248 | my $name = $item; | ||||
249 | while ( exists $newhash{$name} ) { | ||||
250 | $name = $item . $suffix++; | ||||
251 | } | ||||
252 | $newhash{$name} = $item; | ||||
253 | } | ||||
254 | } | ||||
255 | else { | ||||
256 | $newhash{$arg} = $arg; | ||||
257 | } | ||||
258 | return \%newhash; | ||||
259 | } | ||||
260 | |||||
261 | # This adds some checks to the clone process, to deal with problems that | ||||
262 | # the current distro of ActiveState perl has (specifically, it uses 0.09 | ||||
263 | # of Clone, which does not support the cloning of scalars). This simply | ||||
264 | # wraps around clone as to prevent a scalar from being cloned via a | ||||
265 | # Clone 0.09 process. This might mean that CODEREFs and anything else | ||||
266 | # not a HASH or ARRAY won't be cloned. | ||||
267 | |||||
268 | # $clone is global, which should point to coderef | ||||
269 | |||||
270 | sub _my_clone { | ||||
271 | my $self = &_get_obj; # '&' + no args modifies current @_ | ||||
272 | my ( $arg, $depth ) = @_; | ||||
273 | |||||
274 | if ( $self->{clone} && !$clone ) { | ||||
275 | if ( eval { require Clone; 1 } ) { | ||||
276 | $clone = sub { | ||||
277 | if ( !( $Clone::VERSION || 0 ) > 0.09 | ||||
278 | && ref $_[0] ne 'HASH' | ||||
279 | && ref $_[0] ne 'ARRAY' ) { | ||||
280 | my $var = shift; # Forced clone | ||||
281 | return $var; | ||||
282 | } | ||||
283 | Clone::clone( shift, $depth ); | ||||
284 | }; | ||||
285 | } | ||||
286 | elsif ( eval { require Storable; 1 } ) { | ||||
287 | $clone = sub { | ||||
288 | my $var = shift; # Forced clone | ||||
289 | return $var if !ref($var); | ||||
290 | Storable::dclone($var); | ||||
291 | }; | ||||
292 | } | ||||
293 | elsif ( eval { require Clone::PP; 1 } ) { | ||||
294 | $clone = sub { | ||||
295 | my $var = shift; # Forced clone | ||||
296 | return $var if !ref($var); | ||||
297 | Clone::PP::clone( $var, $depth ); | ||||
298 | }; | ||||
299 | } | ||||
300 | else { | ||||
301 | croak "Can't load Clone, Storable, or Clone::PP for cloning purpose"; | ||||
302 | } | ||||
303 | } | ||||
304 | |||||
305 | if ( $self->{'clone'} ) { | ||||
306 | return $clone->($arg); | ||||
307 | } | ||||
308 | else { | ||||
309 | return $arg; | ||||
310 | } | ||||
311 | } | ||||
312 | |||||
313 | 1 | 13µs | 1; | ||
314 | |||||
315 | __END__ |