File Coverage

blib/lib/Data/Dump.pm
Criterion Covered Total %
statement 330 337 97.9
branch 182 200 91.0
condition 74 87 85.0
subroutine 18 18 100.0
pod 4 8 50.0
total 608 650 93.5


line stmt bran cond sub pod time code
1             package Data::Dump;
2              
3 15     15   131129 use strict;
  15         116  
  15         580  
4 15     15   83 use vars qw(@EXPORT @EXPORT_OK $VERSION $DEBUG);
  15         27  
  15         1051  
5 15     15   8766 use subs qq(dump);
  15         418  
  15         74  
6              
7             require Exporter;
8             *import = \&Exporter::import;
9             @EXPORT = qw(dd ddx);
10             @EXPORT_OK = qw(dump pp dumpf quote);
11              
12             $VERSION = "1.25";
13             $DEBUG = 0;
14              
15 15     15   20334 use overload ();
  15         16750  
  15         533  
16 15     15   101 use vars qw(%seen %refcnt @dump @fixup %require $TRY_BASE64 @FILTERS $INDENT $LINEWIDTH);
  15         28  
  15         25228  
17              
18             $TRY_BASE64 = 50 unless defined $TRY_BASE64;
19             $INDENT = " " unless defined $INDENT;
20             $LINEWIDTH = 60 unless defined $LINEWIDTH;
21              
22             sub dump
23             {
24 111     111   7362 local %seen;
25 111         182 local %refcnt;
26 111         170 local %require;
27 111         171 local @fixup;
28              
29 111 100       1349 require Data::Dump::FilterContext if @FILTERS;
30              
31 111         198 my $name = "a";
32 111         176 my @dump;
33              
34 111         486 for my $v (@_) {
35 159         427 my $val = _dump($v, $name, [], tied($v));
36 159         442 push(@dump, [$name, $val]);
37             } continue {
38 159         335 $name++;
39             }
40              
41 111         188 my $out = "";
42 111 100       264 if (%require) {
43 4         20 for (sort keys %require) {
44 4         15 $out .= "require $_;\n";
45             }
46             }
47 111 100       241 if (%refcnt) {
48             # output all those with refcounts first
49 10         27 for (@dump) {
50 27         46 my $name = $_->[0];
51 27 100       95 if ($refcnt{$name}) {
52 10         55 $out .= "my \$$name = $_->[1];\n";
53 10         43 undef $_->[1];
54             }
55             }
56 10         25 for (@fixup) {
57 17         141 $out .= "$_;\n";
58             }
59             }
60              
61 111         210 my $paren = (@dump != 1);
62 111 100       234 $out .= "(" if $paren;
63             $out .= format_list($paren, undef,
64 111 100       245 map {defined($_->[1]) ? $_->[1] : "\$".$_->[0]}
  159         639  
65             @dump
66             );
67 111 100       292 $out .= ")" if $paren;
68              
69 111 100 100     420 if (%refcnt || %require) {
70 13         29 $out .= ";\n";
71 13         487 $out =~ s/^/$INDENT/gm;
72 13         101 $out = "do {\n$out}";
73             }
74              
75 111 50       261 print STDERR "$out\n" unless defined wantarray;
76 111         1884 $out;
77             }
78              
79             *pp = \&dump;
80              
81             sub dd {
82 1     1 1 1201 print dump(@_), "\n";
83             }
84              
85             sub ddx {
86 2     2 1 43 my(undef, $file, $line) = caller;
87 2         26 $file =~ s,.*[\\/],,;
88 2         12 my $out = "$file:$line: " . dump(@_) . "\n";
89 2         270 $out =~ s/^/# /gm;
90 2         120 print $out;
91             }
92              
93             sub dumpf {
94 8     8 1 999 require Data::Dump::Filtered;
95 8         33 goto &Data::Dump::Filtered::dump_filtered;
96             }
97              
98             sub _dump
99             {
100 1185     1185   1851 my $ref = ref $_[0];
101 1185 100       2118 my $rval = $ref ? $_[0] : \$_[0];
102 1185         1525 shift;
103              
104 1185         2038 my($name, $idx, $dont_remember, $pclass, $pidx) = @_;
105              
106 1185         1561 my($class, $type, $id);
107 1185         2250 my $strval = overload::StrVal($rval);
108             # Parse $strval without using regexps, in order not to clobber $1, $2,...
109 1185 100       5271 if ((my $i = rindex($strval, "=")) >= 0) {
110 26         65 $class = substr($strval, 0, $i);
111 26         52 $strval = substr($strval, $i+1);
112             }
113 1185 50       2316 if ((my $i = index($strval, "(0x")) >= 0) {
114 1185         1867 $type = substr($strval, 0, $i);
115 1185         1756 $id = substr($strval, $i + 2, -1);
116             }
117             else {
118 0         0 die "Can't parse " . overload::StrVal($rval);
119             }
120 1185 50 33     2253 if ($] < 5.008 && $type eq "SCALAR") {
121 0 0       0 $type = "REF" if $ref eq "REF";
122             }
123 1185 50       1841 warn "\$$name(@$idx) $class $type $id ($ref)" if $DEBUG;
124              
125 1185         2213 my $out;
126             my $comment;
127 1185         0 my $hide_keys;
128 1185 100       2070 if (@FILTERS) {
129 11         16 my $pself = "";
130 11 100       26 $pself = fullname("self", [@$idx[$pidx..(@$idx - 1)]]) if $pclass;
131 11         38 my $ctx = Data::Dump::FilterContext->new($rval, $class, $type, $ref, $pclass, $pidx, $idx);
132 11         23 my @bless;
133 11         18 for my $filter (@FILTERS) {
134 11 100       26 if (my $f = $filter->($ctx, $rval)) {
135 6 100       38 if (my $v = $f->{object}) {
136 1         11 local @FILTERS;
137 1         33 $out = _dump($v, $name, $idx, 1);
138 1         2 $dont_remember++;
139             }
140 6 100       14 if (defined(my $c = $f->{bless})) {
141 1         2 push(@bless, $c);
142             }
143 6 100       14 if (my $c = $f->{comment}) {
144 1         2 $comment = $c;
145             }
146 6 100       13 if (defined(my $c = $f->{dump})) {
147 1         1 $out = $c;
148 1         3 $dont_remember++;
149             }
150 6 100       26 if (my $h = $f->{hide_keys}) {
151 2 50       7 if (ref($h) eq "ARRAY") {
152             $hide_keys = sub {
153 2     2   4 for my $k (@$h) {
154 2 100       7 return 1 if $k eq $_[0];
155             }
156 1         4 return 0;
157 2         11 };
158             }
159             }
160             }
161             }
162 11 100 66     43 push(@bless, "") if defined($out) && !@bless;
163 11 100       35 if (@bless) {
164 3         4 $class = shift(@bless);
165 3 50       28 warn "More than one filter callback tried to bless object" if @bless;
166             }
167             }
168              
169 1185 100       1928 unless ($dont_remember) {
170 1170 100       2389 if (my $s = $seen{$id}) {
171 19         39 my($sname, $sidx) = @$s;
172 19         32 $refcnt{$sname}++;
173 19   100     110 my $sref = fullname($sname, $sidx,
174             ($ref && $type eq "SCALAR"));
175 19 50       40 warn "SEEN: [\$$name(@$idx)] => [\$$sname(@$sidx)] ($ref,$sref)" if $DEBUG;
176 19 100       72 return $sref unless $sname eq $name;
177 4         7 $refcnt{$name}++;
178 4         10 push(@fixup, fullname($name,$idx)." = $sref");
179 4 100 66     19 return "do{my \$fix}" if @$idx && $idx->[-1] eq '$';
180 3         12 return "'fix'";
181             }
182 1151         3592 $seen{$id} = [$name, $idx];
183             }
184              
185 1166 100       2095 if ($class) {
186 27         55 $pclass = $class;
187 27         41 $pidx = @$idx;
188             }
189              
190 1166 100 100     3610 if (defined $out) {
    100 100        
    100          
    100          
    100          
    100          
    50          
191             # keep it
192             }
193             elsif ($type eq "SCALAR" || $type eq "REF" || $type eq "REGEXP") {
194 1042 100       1551 if ($ref) {
195 32 100 100     92 if ($class && $class eq "Regexp") {
196 9         17 my $v = "$rval";
197              
198 9         12 my $mod = "";
199 9 50       52 if ($v =~ /^\(\?\^?([msix-]*):([\x00-\xFF]*)\)\z/) {
200 9         27 $mod = $1;
201 9         16 $v = $2;
202 9         13 $mod =~ s/-.*//;
203             }
204              
205 9         14 my $sep = '/';
206 9         15 my $sep_count = ($v =~ tr/\///);
207 9 100       21 if ($sep_count) {
208             # see if we can find a better one
209 4         7 for ('|', ',', ':', '#') {
210 10         471 my $c = eval "\$v =~ tr/\Q$_\E//";
211             #print "SEP $_ $c $sep_count\n";
212 10 100       37 if ($c < $sep_count) {
213 3         4 $sep = $_;
214 3         5 $sep_count = $c;
215 3 50       8 last if $sep_count == 0;
216             }
217             }
218             }
219 9         70 $v =~ s/\Q$sep\E/\\$sep/g;
220              
221 9         24 $out = "qr$sep$v$sep$mod";
222 9         14 undef($class);
223             }
224             else {
225 23 100       77 delete $seen{$id} if $type eq "SCALAR"; # will be seen again shortly
226 23         367 my $val = _dump($$rval, $name, [@$idx, "\$"], 0, $pclass, $pidx);
227 23 100       89 $out = $class ? "do{\\(my \$o = $val)}" : "\\$val";
228             }
229             } else {
230 1010 100   1   2488 if (!defined $$rval) {
  1 100       684  
  1 100       15  
  1         14  
231 3         8 $out = "undef";
232             }
233             elsif ($$rval =~ /^-?(?:nan|inf)/i) {
234 4         7 $out = str($$rval);
235             }
236 15     15   131 elsif (do {no warnings 'numeric'; $$rval + 0 eq $$rval}) {
  15         59  
  15         42619  
  1003         32539  
237 692         1013 $out = $$rval;
238             }
239             else {
240 311         613 $out = str($$rval);
241             }
242 1010 100 100     2037 if ($class && !@$idx) {
243             # Top is an object, not a reference to one as perl needs
244 1         4 $refcnt{$name}++;
245 1         12 my $obj = fullname($name, $idx);
246 1         3 my $cl = quote($class);
247 1         4 push(@fixup, "bless \\$obj, $cl");
248             }
249             }
250             }
251             elsif ($type eq "GLOB") {
252 38 100       64 if ($ref) {
253 5         14 delete $seen{$id};
254 5         52 my $val = _dump($$rval, $name, [@$idx, "*"], 0, $pclass, $pidx);
255 5         12 $out = "\\$val";
256 5 100       22 if ($out =~ /^\\\*Symbol::/) {
257 3         8 $require{Symbol}++;
258 3         6 $out = "Symbol::gensym()";
259             }
260             } else {
261 33         97 my $val = "$$rval";
262 33         77 $out = "$$rval";
263              
264 33         60 for my $k (qw(SCALAR ARRAY HASH)) {
265 99         173 my $gval = *$$rval{$k};
266 99 100       200 next unless defined $gval;
267 40 100 100     132 next if $k eq "SCALAR" && ! defined $$gval; # always there
268 12         18 my $f = scalar @fixup;
269 12         20 push(@fixup, "RESERVED"); # overwritten after _dump() below
270 12         108 $gval = _dump($gval, $name, [@$idx, "*{$k}"], 0, $pclass, $pidx);
271 12         31 $refcnt{$name}++;
272 12         30 my $gname = fullname($name, $idx);
273 12         71 $fixup[$f] = "$gname = $gval"; #XXX indent $gval
274             }
275             }
276             }
277             elsif ($type eq "ARRAY") {
278 18         33 my @vals;
279 18         50 my $tied = tied_str(tied(@$rval));
280 18         69 my $i = 0;
281 18         44 for my $v (@$rval) {
282 291         906 push(@vals, _dump($v, $name, [@$idx, "[$i]"], $tied, $pclass, $pidx));
283 291         514 $i++;
284             }
285 18         66 $out = "[" . format_list(1, $tied, @vals) . "]";
286             }
287             elsif ($type eq "HASH") {
288 54         130 my(@keys, @vals);
289 54         145 my $tied = tied_str(tied(%$rval));
290              
291             # statistics to determine variation in key lengths
292 54         100 my $kstat_max = 0;
293 54         80 my $kstat_sum = 0;
294 54         69 my $kstat_sum2 = 0;
295              
296 54         356 my @orig_keys = keys %$rval;
297 54 100       134 if ($hide_keys) {
298 1         4 @orig_keys = grep !$hide_keys->($_), @orig_keys;
299             }
300 54         92 my $text_keys = 0;
301 54         107 for (@orig_keys) {
302 41 100       271 $text_keys++, last unless /^[-+]?(?:0|[1-9]\d*)(?:\.\d+)?\z/;
303             }
304              
305 54 100       125 if ($text_keys) {
306 34         197 @orig_keys = sort { lc($a) cmp lc($b) } @orig_keys;
  4113         5392  
307             }
308             else {
309 20         44 @orig_keys = sort { $a <=> $b } @orig_keys;
  6         15  
310             }
311              
312 54         93 my $quote;
313 54         98 for my $key (@orig_keys) {
314 276 100       740 next if $key =~ /^-?[a-zA-Z_]\w*\z/;
315 10 100       33 next if $key =~ /^-?[1-9]\d{0,8}\z/;
316 6         13 $quote++;
317 6         28 last;
318             }
319              
320 54         103 for my $key (@orig_keys) {
321 694         1373 my $val = \$rval->{$key}; # capture value before we modify $key
322 694 100       1370 $key = quote($key) if $quote;
323 694 100       1268 $kstat_max = length($key) if length($key) > $kstat_max;
324 694         841 $kstat_sum += length($key);
325 694         904 $kstat_sum2 += length($key)*length($key);
326              
327 694         1075 push(@keys, $key);
328 694         2771 push(@vals, _dump($$val, $name, [@$idx, "{$key}"], $tied, $pclass, $pidx));
329             }
330 54         108 my $nl = "";
331 54         88 my $klen_pad = 0;
332 54         510 my $tmp = "@keys @vals";
333 54 100 66     337 if (length($tmp) > $LINEWIDTH || $tmp =~ /\n/ || $tied) {
      100        
334 15         26 $nl = "\n";
335              
336             # Determine what padding to add
337 15 100       77 if ($kstat_max < 4) {
    50          
338 5         9 $klen_pad = $kstat_max;
339             }
340             elsif (@keys >= 2) {
341 10         27 my $n = @keys;
342 10         34 my $avg = $kstat_sum/$n;
343 10         42 my $stddev = sqrt(($kstat_sum2 - $n * $avg * $avg) / ($n - 1));
344              
345             # I am not actually very happy with this heuristics
346 10 100       33 if ($stddev / $kstat_max < 0.25) {
347 5         10 $klen_pad = $kstat_max;
348             }
349 10 50       25 if ($DEBUG) {
350 0         0 push(@keys, "__S");
351 0         0 push(@vals, sprintf("%.2f (%d/%.1f/%.1f)",
352             $stddev / $kstat_max,
353             $kstat_max, $avg, $stddev));
354             }
355             }
356             }
357 54         104 $out = "{$nl";
358 54 100       125 $out .= "$INDENT# $tied$nl" if $tied;
359 54         120 while (@keys) {
360 694         1017 my $key = shift @keys;
361 694         956 my $val = shift @vals;
362 694 100       1309 my $vpad = $INDENT . (" " x ($klen_pad ? $klen_pad + 4 : 0));
363 694         1201 $val =~ s/\n/\n$vpad/gm;
364 694 100       980 my $kpad = $nl ? $INDENT : " ";
365 694 100 100     1946 $key .= " " x ($klen_pad - length($key)) if $nl && $klen_pad > length($key);
366 694         1760 $out .= "$kpad$key => $val,$nl";
367             }
368 54 100       200 $out =~ s/,$/ / unless $nl;
369 54         208 $out .= "}";
370             }
371             elsif ($type eq "CODE") {
372 1         2 $out = 'sub { ... }';
373             }
374             elsif ($type eq "VSTRING") {
375 11 100       42 $out = sprintf +($ref ? '\v%vd' : 'v%vd'), $$rval;
376             }
377             else {
378 0         0 warn "Can't handle $type data";
379 0         0 $out = "'#$type#'";
380             }
381              
382 1166 100 100     2062 if ($class && $ref) {
383 14         62 $out = "bless($out, " . quote($class) . ")";
384             }
385 1166 100       1830 if ($comment) {
386 1         5 $comment =~ s/^/# /gm;
387 1 50       4 $comment .= "\n" unless $comment =~ /\n\z/;
388 1         5 $comment =~ s/^#[ \t]+\n/\n/;
389 1         3 $out = "$comment$out";
390             }
391 1166         3016 return $out;
392             }
393              
394             sub tied_str {
395 72     72 0 114 my $tied = shift;
396 72 100       140 if ($tied) {
397 2 50       7 if (my $tied_ref = ref($tied)) {
398 2         5 $tied = "tied $tied_ref";
399             }
400             else {
401 0         0 $tied = "tied";
402             }
403             }
404 72         115 return $tied;
405             }
406              
407             sub fullname
408             {
409 41     41 0 108 my($name, $idx, $ref) = @_;
410 41         83 substr($name, 0, 0) = "\$";
411              
412 41         91 my @i = @$idx; # need copy in order to not modify @$idx
413 41 100 100     146 if ($ref && @i && $i[0] eq "\$") {
      100        
414 2         3 shift(@i); # remove one deref
415 2         4 $ref = 0;
416             }
417 41   100     179 while (@i && $i[0] eq "\$") {
418 5         8 shift @i;
419 5         14 $name = "\$$name";
420             }
421              
422 41         58 my $last_was_index;
423 41         77 for my $i (@i) {
424 35 100 100     186 if ($i eq "*" || $i eq "\$") {
    100          
425 7         10 $last_was_index = 0;
426 7         17 $name = "$i\{$name}";
427             } elsif ($i =~ s/^\*//) {
428 2         5 $name .= $i;
429 2         6 $last_was_index++;
430             } else {
431 26 100       70 $name .= "->" unless $last_was_index++;
432 26         47 $name .= $i;
433             }
434             }
435 41 100       90 $name = "\\$name" if $ref;
436 41         109 $name;
437             }
438              
439             sub format_list
440             {
441 129     129 0 216 my $paren = shift;
442 129         171 my $comment = shift;
443 129 100       255 my $indent_lim = $paren ? 0 : 1;
444 129 100       314 if (@_ > 3) {
445             # can we use range operator to shorten the list?
446 12         27 my $i = 0;
447 12         35 while ($i < @_) {
448 40         52 my $j = $i + 1;
449 40         56 my $v = $_[$i];
450 40         76 while ($j < @_) {
451             # XXX allow string increment too?
452 316 100 100     1376 if ($v eq "0" || $v =~ /^-?[1-9]\d{0,9}\z/) {
    100          
453 37         56 $v++;
454             }
455             elsif ($v =~ /^"([A-Za-z]{1,3}\d*)"\z/) {
456 266         505 $v = $1;
457 266         307 $v++;
458 266         370 $v = qq("$v");
459             }
460             else {
461 13         20 last;
462             }
463 303 100       523 last if $_[$j] ne $v;
464 289         432 $j++;
465             }
466 40 100       94 if ($j - $i > 3) {
467 9         35 splice(@_, $i, $j - $i, "$_[$i] .. $_[$j-1]");
468             }
469 40         99 $i++;
470             }
471             }
472 129         360 my $tmp = "@_";
473 129 100 33     637 if ($comment || (@_ > $indent_lim && (length($tmp) > $LINEWIDTH || $tmp =~ /\n/))) {
      66        
      66        
474 1         3 my @elem = @_;
475 1         4 for (@elem) { s/^/$INDENT/gm; }
  1         27  
476 1 50       26 return "\n" . ($comment ? "$INDENT# $comment\n" : "") .
477             join(",\n", @elem, "");
478             } else {
479 128         456 return join(", ", @_);
480             }
481             }
482              
483             sub str {
484 315 100   315 0 551 if (length($_[0]) > 20) {
485 14         28 for ($_[0]) {
486             # Check for repeated string
487 14 100       55 if (/^(.)\1\1\1/s) {
488             # seems to be a repeating sequence, let's check if it really is
489             # without backtracking
490 4 50       51350 unless (/[^\Q$1\E]/) {
491 4         21 my $base = quote($1);
492 4         8 my $repeat = length;
493 4         20 return "($base x $repeat)"
494             }
495             }
496             # Length protection because the RE engine will blow the stack [RT#33520]
497 10 100 66     118 if (length($_) < 16 * 1024 && /^(.{2,5}?)\1*\z/s) {
498 2         8 my $base = quote($1);
499 2         9 my $repeat = length($_)/length($1);
500 2         20 return "($base x $repeat)";
501             }
502             }
503             }
504              
505 309         498 local $_ = "e;
506              
507 309 100 100     704 if (length($_) > 40 && !/\\x\{/ && length($_) > (length($_[0]) * 2)) {
      100        
508             # too much binary data, better to represent as a hex/base64 string
509              
510             # Base64 is more compact than hex when string is longer than
511             # 17 bytes (not counting any require statement needed).
512             # But on the other hand, hex is much more readable.
513 3 100 66     33 if ($TRY_BASE64 && length($_[0]) > $TRY_BASE64 &&
      66        
      100        
      66        
514             (defined &utf8::is_utf8 && !utf8::is_utf8($_[0])) &&
515 1         10 eval { require MIME::Base64 })
516             {
517 1         4 $require{"MIME::Base64"}++;
518 1         9 return "MIME::Base64::decode(\"" .
519             MIME::Base64::encode($_[0],"") .
520             "\")";
521             }
522 2         23 return "pack(\"H*\",\"" . unpack("H*", $_[0]) . "\")";
523             }
524              
525 306         544 return $_;
526             }
527              
528             my %esc = (
529             "\a" => "\\a",
530             "\b" => "\\b",
531             "\t" => "\\t",
532             "\n" => "\\n",
533             "\f" => "\\f",
534             "\r" => "\\r",
535             "\e" => "\\e",
536             );
537              
538             # put a string value in double quotes
539             sub quote {
540 774     774 1 1425 local($_) = $_[0];
541             # If there are many '"' we might want to use qq() instead
542 774         1614 s/([\\\"\@\$])/\\$1/g;
543 774 100       2749 return qq("$_") unless /[^\040-\176]/; # fast exit
544              
545 16         93 s/([\a\b\t\n\f\r\e])/$esc{$1}/g;
546              
547             # no need for 3 digits in escape for these
548 16         89 s/([\0-\037])(?!\d)/sprintf('\\%o',ord($1))/eg;
  1103         2595  
549              
550 16         72 s/([\0-\037\177-\377])/sprintf('\\x%02X',ord($1))/eg;
  393         986  
551 16         85 s/([^\040-\176])/sprintf('\\x{%X}',ord($1))/eg;
  105         294  
552              
553 16         163 return qq("$_");
554             }
555              
556             1;
557              
558             __END__