File Coverage

blib/lib/Data/Hexify.pm
Criterion Covered Total %
statement 86 90 95.5
branch 24 34 70.5
condition 6 6 100.0
subroutine 7 7 100.0
pod 0 1 0.0
total 123 138 89.1


line stmt bran cond sub pod time code
1             # Data-Hexify.pm -- Perl extension for hexdumping arbitrary data
2             # RCS Info : $Id: Data-Hexify.pm,v 1.6 2004/11/05 09:17:14 jv Exp $
3             # Author : Johan Vromans
4             # Created On : Sat Jun 19 12:31:21 2004
5             # Last Modified By: Johan Vromans
6             # Last Modified On: Fri Nov 5 10:17:11 2004
7             # Update Count : 37
8             # Status : Unknown, Use with caution!
9              
10             package Data::Hexify;
11              
12 8     8   231749 use 5.006;
  8         27  
  8         296  
13 8     8   45 use strict;
  8         13  
  8         358  
14 8     8   57 use warnings;
  8         17  
  8         1126  
15              
16             ################ Exporter Section ################
17              
18             require Exporter;
19             our @ISA = qw(Exporter);
20             our @EXPORT = qw(Hexify);
21             our %EXPORT_TAGS = ( all => [ @EXPORT ] );
22             our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
23              
24             ################ Preamble ################
25              
26             our $VERSION = '1.00';
27              
28 8     8   963 use Carp;
  8         12  
  8         912  
29              
30             my $usage = "Usage: Hexify( [ , ])\n";
31              
32             ################ Code ################
33              
34             sub Hexify {
35              
36 8     8   11567 use bytes;
  8         83  
  8         34  
37              
38             # First argument: data or reference to the data.
39 30     30 0 197 my $data = shift;
40 30 50       92 my $dr = ref($data) ? $data : \$data;
41              
42 30         42 my $start = 0; # first byte to dump
43 30         52 my $lastplusone = length($$dr); # first byte not to dump
44 30         48 my $align = 1; # align
45 30         38 my $chunk = 16; # bytes per line
46 30         42 my $first = $start; # number of 1st byte
47 30         34 my $dups = 0; # output identical lines
48 30         34 my $group = 1; # group per # bytes
49              
50 86     86   120 my $show = sub { my $t = shift;
51 86         133 $t =~ tr /\000-\037\177-\377/./;
52 86         463 $t;
53 30         126 };
54              
55             # Check for second argument.
56 30 100       94 if ( @_ ) {
57              
58             # Second argument: options hash or hashref.
59 27         324 my %atts = ( align => $align,
60             chunk => $chunk,
61             showdata => $show,
62             start => $start,
63             length => $lastplusone - $start,
64             duplicates => $dups,
65             first => undef,
66             group => 1,
67             );
68              
69 27 50       78 if ( @_ == 1 ) { # hash ref
    0          
70 27         39 my $a = shift;
71 27 50       108 croak($usage) unless ref($a) eq 'HASH';
72 27         261 %atts = ( %atts, %$a );
73             }
74             elsif ( @_ % 2 ) { # odd
75 0         0 croak($usage);
76             }
77             else { # assume hash
78 0         0 %atts = ( %atts, @_ );
79             }
80              
81 27         67 my $length;
82 27         65 $start = delete($atts{start});
83 27         45 $length = delete($atts{length});
84 27         46 $align = delete($atts{align});
85 27         45 $chunk = delete($atts{chunk});
86 27         50 $show = delete($atts{showdata});
87 27         47 $dups = delete($atts{duplicates});
88 27         45 $group = delete($atts{group});
89 27 100       67 $first = defined($atts{first}) ? $atts{first} : $start;
90 27         37 delete($atts{first});
91              
92 27 50       94 if ( %atts ) {
93 0         0 croak("Hexify: unrecognized options: ".
94             join(" ", sort(keys(%atts))));
95             }
96              
97             # Sanity
98 27 50       64 $start = 0 if $start < 0;
99 27         37 $lastplusone = $start + $length;
100 27 50       61 $lastplusone = length($$dr)
101             if $lastplusone > length($$dr);
102 27 50       75 $chunk = 16 if $chunk <= 0;
103 27 50       85 if ( $chunk % $group ) {
104 0         0 croak("Hexify: chunk ($chunk) must be a multiple of group ($group)");
105             }
106             }
107 30         47 $group *= 2;
108              
109             #my $fmt = " %04x: %-" . (3 * $chunk - 1) . "s %-" . $chunk . "s\n";
110 30         145 my $fmt = " %04x: %-" . (2*$chunk + $chunk/($group/2) - 1) . "s %-" . $chunk . "s\n";
111 30         185 my $ret = "";
112              
113 30 100 100     159 if ( $align && (my $r = $first % $chunk) ) {
114             # This piece of code can be merged into the main loop.
115             # However, this piece is only executed infrequently.
116 9         23 my $lead = " " x $r;
117 9         14 my $firstn = $chunk - $r;
118 9         13 $first -= $r;
119 9         12 my $n = $lastplusone - $start;
120 9 100       24 $n = $firstn if $n > $firstn;
121 9         20 my $ss = substr($$dr, $start, $n);
122 9         263 (my $hex = $lead . $lead . unpack("H*",$ss)) =~ s/(.{$group})(?!$)/$1 /g;
123 9         28 $ret .= sprintf($fmt, $first, $hex,
124             $lead . $show->($ss));
125 9         14 $start += $n;
126 9         20 $first += $chunk;
127             }
128              
129 30         40 my $same = "";
130 30         35 my $didsame = 0;
131 30         42 my $dupline = " |\n";
132              
133 30         71 while ( $start < $lastplusone ) {
134 96         118 my $n = $lastplusone - $start;
135 96 100       196 $n = $chunk if $n > $chunk;
136 96         155 my $ss = substr($$dr, $start, $n);
137              
138 96 100       201 if ( !$dups ) {
139 90 100 100     271 if ( $ss eq $same && ($start + $n) < $lastplusone ) {
140 16 100       67 if ( !$didsame ) {
141 3         6 $ret .= $dupline;
142 3         3 $same = $ss;
143 3         5 $didsame = 1;
144             }
145 16         18 next;
146             }
147             else {
148 74         87 $same = "";
149 74         184 $didsame = 0;
150             }
151             }
152 80         92 $same = $ss;
153              
154 80         1781 (my $hex = unpack("H*", $ss)) =~ s/(.{$group})(?!$)/$1 /g;
155 80         197 $ret .= sprintf($fmt, $first, $hex, $show->($ss));
156             }
157             continue {
158 96         170 $start += $chunk;
159 96         229 $first += $chunk;
160             }
161              
162 30         243 $ret;
163             }
164              
165             ################ Selftest ################
166              
167             unless ( caller ) {
168              
169             package main;
170             my $data = pack("C*", 0..255);
171             my $res = "";
172             $res .= Data::Hexify::Hexify(\$data,
173             length => 48);
174             $res .= Data::Hexify::Hexify(\$data,
175             start => 14, length => 48);
176             $res .= Data::Hexify::Hexify(\$data,
177             start => 3, length => 4);
178             $res .= Data::Hexify::Hexify(\$data,
179             start => 3, length => 4, first => 7);
180             my $exp = <<'EOD';
181             0000: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ................
182             0010: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f ................
183             0020: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f !"#$%&'()*+,-./
184             0000: 0e 0f ..
185             0010: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f ................
186             0020: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f !"#$%&'()*+,-./
187             0030: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 0123456789:;<=
188             0000: 03 04 05 06 ....
189             0000: 03 04 05 06 ....
190             EOD
191              
192             die("Selftest error:\n".
193             "Got:\n$res".
194             "Expected:\n$exp") unless $res eq $exp;
195             }
196              
197             ################ End of Selftest ################
198              
199             __END__