File Coverage

blib/lib/Data/Dump.pm
Criterion Covered Total %
statement 325 333 97.6
branch 180 198 90.9
condition 70 84 83.3
subroutine 17 17 100.0
pod 4 8 50.0
total 596 640 93.1


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