File Coverage

blib/lib/Data/Dumper/MessagePack.pm
Criterion Covered Total %
statement 91 222 40.9
branch 29 92 31.5
condition 3 18 16.6
subroutine 19 29 65.5
pod 0 2 0.0
total 142 363 39.1


line stmt bran cond sub pod time code
1             package Data::Dumper::MessagePack;
2             our $AUTHORITY = 'cpan:GETTY';
3             $Data::Dumper::MessagePack::VERSION = '0.002';
4             # ABSTRACT: Dump MessagePack
5 2     2   25914 use 5.008001;
  2         7  
6 2     2   9 use strict;
  2         4  
  2         56  
7 2     2   20 use warnings;
  2         4  
  2         62  
8 2     2   8 no warnings 'recursion';
  2         4  
  2         63  
9              
10 2     2   8 use Carp ();
  2         9  
  2         25  
11 2     2   8 use B ();
  2         3  
  2         32  
12 2     2   9 use Config;
  2         3  
  2         76  
13 2     2   715 use boolean;
  2         991  
  2         10  
14 2     2   6064 use Term::ANSIColor qw( color );
  2         18392  
  2         829  
15              
16             # Stolen from
17             # http://cpansearch.perl.org/src/GFUJI/Data-MessagePack-0.48/lib/Data/MessagePack/PP.pm
18              
19 2     2   23 use Exporter 'import';
  2         5  
  2         1032  
20             our @EXPORT = qw( ddmp );
21             our @EXPORT_OK = qw( mp_unpack );
22              
23 0         0 BEGIN {
24 2     2   4 my $unpack_int64_slow;
25             my $unpack_uint64_slow;
26              
27 2 50       3 if(!eval { pack 'Q', 1 }) { # don't have quad types
  2         8  
28             # emulates quad types with Math::BigInt.
29             # very slow but works well.
30             $unpack_int64_slow = sub {
31 0         0 require Math::BigInt;
32 0         0 my $high = unpack_uint32( $_[0], $_[1] );
33 0         0 my $low = unpack_uint32( $_[0], $_[1] + 4);
34              
35 0 0       0 if($high < 0xF0000000) { # positive
36 0         0 $high = Math::BigInt->new( $high );
37 0         0 $low = Math::BigInt->new( $low );
38 0         0 return +($high << 32 | $low)->bstr;
39             }
40             else { # negative
41 0         0 $high = Math::BigInt->new( ~$high );
42 0         0 $low = Math::BigInt->new( ~$low );
43 0         0 return +( -($high << 32 | $low + 1) )->bstr;
44             }
45 0         0 };
46             $unpack_uint64_slow = sub {
47 0         0 require Math::BigInt;
48 0         0 my $high = Math::BigInt->new( unpack_uint32( $_[0], $_[1]) );
49 0         0 my $low = Math::BigInt->new( unpack_uint32( $_[0], $_[1] + 4) );
50 0         0 return +($high << 32 | $low)->bstr;
51 0         0 };
52             }
53              
54 2     0   9 *unpack_uint16 = sub { return unpack 'n', substr( $_[0], $_[1], 2 ) };
  0         0  
55 2     0   6 *unpack_uint32 = sub { return unpack 'N', substr( $_[0], $_[1], 4 ) };
  0         0  
56              
57             # For ARM OABI
58 2         14 my $bo_is_me = unpack ( 'd', "\x00\x00\xf0\x3f\x00\x00\x00\x00") == 1;
59 2         3 my $unpack_double_oabi;
60              
61             # for pack and unpack compatibility
62 2 50       8 if ( $] < 5.010 ) {
63 0         0 my $bo_is_le = ( $Config{byteorder} =~ /^1234/ );
64              
65 0 0       0 if ($bo_is_me) {
66             $unpack_double_oabi = sub {
67 0         0 my @v = unpack( 'V2', substr( $_[0], $_[1], 8 ) );
68 0         0 return unpack( 'd', pack( 'N2', @v[0,1] ) );
69 0         0 };
70             }
71              
72             *unpack_int16 = sub {
73 0         0 my $v = unpack 'n', substr( $_[0], $_[1], 2 );
74 0 0       0 return $v ? $v - 0x10000 : 0;
75 0         0 };
76             *unpack_int32 = sub {
77 2     2   11 no warnings; # avoid for warning about Hexadecimal number
  2         2  
  2         1179  
78 0         0 my $v = unpack 'N', substr( $_[0], $_[1], 4 );
79 0 0       0 return $v ? $v - 0x100000000 : 0;
80 0         0 };
81              
82             # In reality, since 5.9.2 '>' is introduced. but 'n!' and 'N!'?
83 0 0       0 if($bo_is_le) {
84             *unpack_float = sub {
85 0         0 my @v = unpack( 'v2', substr( $_[0], $_[1], 4 ) );
86 0         0 return unpack( 'f', pack( 'n2', @v[1,0] ) );
87 0         0 };
88             *unpack_double = $unpack_double_oabi || sub {
89             my @v = unpack( 'V2', substr( $_[0], $_[1], 8 ) );
90             return unpack( 'd', pack( 'N2', @v[1,0] ) );
91 0   0     0 };
92              
93             *unpack_int64 = $unpack_int64_slow || sub {
94             my @v = unpack( 'V*', substr( $_[0], $_[1], 8 ) );
95             return unpack( 'q', pack( 'N2', @v[1,0] ) );
96 0   0     0 };
97             *unpack_uint64 = $unpack_uint64_slow || sub {
98             my @v = unpack( 'V*', substr( $_[0], $_[1], 8 ) );
99             return unpack( 'Q', pack( 'N2', @v[1,0] ) );
100 0   0     0 };
101             }
102             else { # big endian
103 0         0 *unpack_float = sub { return unpack( 'f', substr( $_[0], $_[1], 4 ) ); };
  0         0  
104 0   0     0 *unpack_double = $unpack_double_oabi || sub { return unpack( 'd', substr( $_[0], $_[1], 8 ) ); };
105 0   0     0 *unpack_int64 = $unpack_int64_slow || sub { unpack 'q', substr( $_[0], $_[1], 8 ); };
106 0   0     0 *unpack_uint64 = $unpack_uint64_slow || sub { unpack 'Q', substr( $_[0], $_[1], 8 ); };
107             }
108             }
109             else { # 5.10.0 or later
110 2 50       7 if ($bo_is_me) {
111             $unpack_double_oabi = sub {
112 0         0 my $first_word = substr($_[0], $_[1], 4);
113 0         0 my $second_word = substr($_[0], $_[1] + 4, 4);
114 0         0 my $d_bin = $second_word . $first_word;
115 0         0 return unpack( 'd>', $d_bin );
116 0         0 };
117             }
118              
119 2     0   5 *unpack_float = sub { return unpack( 'f>', substr( $_[0], $_[1], 4 ) ); };
  0         0  
120 2   50 1   17 *unpack_double = $unpack_double_oabi || sub { return unpack( 'd>', substr( $_[0], $_[1], 8 ) ); };
  1         11  
121 2     1   5 *unpack_int16 = sub { return unpack( 'n!', substr( $_[0], $_[1], 2 ) ); };
  1         7  
122 2     0   7 *unpack_int32 = sub { return unpack( 'N!', substr( $_[0], $_[1], 4 ) ); };
  0         0  
123              
124 2   50 0   14 *unpack_int64 = $unpack_int64_slow || sub { return unpack( 'q>', substr( $_[0], $_[1], 8 ) ); };
  0         0  
125 2   50 0   1550 *unpack_uint64 = $unpack_uint64_slow || sub { return unpack( 'Q>', substr( $_[0], $_[1], 8 ) ); };
  0         0  
126             }
127              
128             # fixin package symbols
129 2     2   9 no warnings 'once';
  2         4  
  2         76  
130             }
131              
132             sub _unexpected {
133 0     0   0 Carp::confess("Unexpected " . sprintf(shift, @_) . " found");
134             }
135              
136             #
137             # UNPACK
138             #
139              
140             our $_utf8 = 1;
141             my $p; # position variables for speed.
142              
143             sub _insufficient {
144 0     0   0 Carp::confess("Insufficient bytes (pos=$p, type=@_)");
145             }
146              
147             sub mp_unpack {
148 7     7 0 55080 $p = 0; # init
149 7         25 my $data = _unpack( $_[0] );
150 7 50       22 if($p < length($_[0])) {
151 0         0 Carp::croak("Data::Dumper::MessagePack->mp_unpack: extra bytes");
152             }
153 7         46 return $data;
154             }
155              
156             my $T_RAW = 0x01;
157             my $T_ARRAY = 0x02;
158             my $T_MAP = 0x04;
159             my $T_DIRECT = 0x08; # direct mapping (e.g. 0xc0 <-> nil)
160              
161             my @detailed = ( (0x00) x 256 );
162             my @typemap = ( (0x00) x 256 );
163              
164             $detailed[$_] = 'fixarray' for
165             0x90 .. 0x9f
166             ;
167             $detailed[0xdc] = 'array16';
168             $detailed[0xdd] = 'array32';
169             $detailed[$_] = 'fixmap' for
170             0x80 .. 0x8f
171             ;
172             $detailed[0xde] = 'map16';
173             $detailed[0xdf] = 'map32';
174             $detailed[$_] = 'fixstr' for
175             0xa0 .. 0xbf
176             ;
177             $detailed[0xda] = 'str16';
178             $detailed[0xdb] = 'str32';
179             $typemap[$_] |= $T_ARRAY for
180             0x90 .. 0x9f, # fix array
181             0xdc, # array16
182             0xdd, # array32
183             ;
184             $typemap[$_] |= $T_MAP for
185             0x80 .. 0x8f, # fix map
186             0xde, # map16
187             0xdf, # map32
188             ;
189             $typemap[$_] |= $T_RAW for
190             0xa0 .. 0xbf, # fix raw
191             0xda, # raw16
192             0xdb, # raw32
193             ;
194              
195             $detailed[0xc3] = 'true';
196             $detailed[0xc2] = 'false';
197             $detailed[0xc0] = 'nil';
198             $detailed[$_] = 'positive fixint' for
199             0x00 .. 0x7f
200             ;
201             $detailed[$_] = 'negative fixint' for
202             0xe0 .. 0xff
203             ;
204             my @byte2value;
205             foreach my $pair(
206             [0xc3, true],
207             [0xc2, false],
208             [0xc0, undef],
209              
210             (map { [ $_, $_ ] } 0x00 .. 0x7f), # positive fixnum
211             (map { [ $_, $_ - 0x100 ] } 0xe0 .. 0xff), # negative fixnum
212             ) {
213             $typemap[ $pair->[0] ] |= $T_DIRECT;
214             $byte2value[ $pair->[0] ] = $pair->[1];
215             }
216              
217             sub _fetch_size {
218 7     7   11 my($value_ref, $byte, $x16, $x32, $x_fixbits) = @_;
219 7 50       17 if ( $byte == $x16 ) {
    50          
220 0         0 $p += 2;
221 0 0       0 $p <= length(${$value_ref}) or _insufficient('x/16');
  0         0  
222 0         0 return CORE::unpack 'n', substr( ${$value_ref}, $p - 2, 2 );
  0         0  
223             }
224             elsif ( $byte == $x32 ) {
225 0         0 $p += 4;
226 0 0       0 $p <= length(${$value_ref}) or _insufficient('x/32');
  0         0  
227 0         0 return CORE::unpack 'N', substr( ${$value_ref}, $p - 4, 4 );
  0         0  
228             }
229             else { # fix raw
230 7         14 return $byte & ~$x_fixbits;
231             }
232             }
233              
234             sub _unpack {
235 13     13   22 my ( $value ) = @_;
236 13 50       33 $p < length($value) or _insufficient('header byte');
237             # get a header byte
238 13         24 my $byte = ord( substr $value, $p, 1 );
239 13         17 $p++;
240              
241             # +/- fixnum, nil, true, false
242 13 100       47 return [ $detailed[$byte], $byte2value[$byte] ]
243             if $typemap[$byte] & $T_DIRECT;
244              
245 9 100       54 if ( $typemap[$byte] & $T_RAW ) {
    100          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    100          
    50          
    50          
    0          
246 5         11 my $size = _fetch_size(\$value, $byte, 0xda, 0xdb, 0xa0);
247 5         6 my $s = substr( $value, $p, $size );
248 5 50       12 length($s) == $size or _insufficient('raw');
249 5         6 $p += $size;
250 5 50       14 utf8::decode($s) if $_utf8;
251 5         19 return [ $detailed[$byte], $s ];
252             }
253             elsif ( $typemap[$byte] & $T_ARRAY ) {
254 1         3 my $size = _fetch_size(\$value, $byte, 0xdc, 0xdd, 0x90);
255 1         2 my @array;
256 1         6 push @array, _unpack( $value ) while --$size >= 0;
257 1         4 return [ $detailed[$byte], \@array ];
258             }
259             elsif ( $typemap[$byte] & $T_MAP ) {
260 1         6 my $size = _fetch_size(\$value, $byte, 0xde, 0xdf, 0x80);
261 1         3 my @map;
262 1         4 while(--$size >= 0) {
263 2     2   10 no warnings; # for undef key case
  2         4  
  2         2223  
264 1         9 my $key = _unpack( $value );
265 1         4 my $val = _unpack( $value );
266 1         4 push @map, $key, $val;
267             }
268 1         3 return [ $detailed[$byte], \@map ];
269             }
270              
271             elsif ( $byte == 0xcc ) {
272 0         0 $p++;
273 0 0       0 $p <= length($value) or _insufficient('uint8');
274 0         0 my $number = CORE::unpack( 'C', substr( $value, $p - 1, 1 ) );
275 0         0 return [ uint8 => $number ];
276             }
277             elsif ( $byte == 0xcd ) {
278 0         0 $p += 2;
279 0 0       0 $p <= length($value) or _insufficient('uint16');
280 0         0 return [ uint16 => unpack_uint16( $value, $p - 2 ) ];
281             }
282             elsif ( $byte == 0xce ) {
283 0         0 $p += 4;
284 0 0       0 $p <= length($value) or _insufficient('uint32');
285 0         0 return [ uint32 => unpack_uint32( $value, $p - 4 ) ];
286             }
287             elsif ( $byte == 0xcf ) {
288 0         0 $p += 8;
289 0 0       0 $p <= length($value) or _insufficient('uint64');
290 0         0 return [ uint64 => unpack_uint64( $value, $p - 8 ) ];
291             }
292             elsif ( $byte == 0xd3 ) {
293 0         0 $p += 8;
294 0 0       0 $p <= length($value) or _insufficient('int64');
295 0         0 return [ int64 => unpack_int64( $value, $p - 8 ) ];
296             }
297             elsif ( $byte == 0xd2 ) {
298 0         0 $p += 4;
299 0 0       0 $p <= length($value) or _insufficient('int32');
300 0         0 return [ int32 => unpack_int32( $value, $p - 4 ) ];
301             }
302             elsif ( $byte == 0xd1 ) {
303 1         3 $p += 2;
304 1 50       5 $p <= length($value) or _insufficient('int16');
305 1         6 return [ int16 => unpack_int16( $value, $p - 2 ) ];
306             }
307             elsif ( $byte == 0xd0 ) {
308 0         0 $p++;
309 0 0       0 $p <= length($value) or _insufficient('int8');
310 0         0 my $number = CORE::unpack('c', substr( $value, $p - 1, 1 ) );
311 0         0 return [ int8 => $number ];
312             }
313             elsif ( $byte == 0xcb ) {
314 1         3 $p += 8;
315 1 50       6 $p <= length($value) or _insufficient('double');
316 1         6 return [ float64 => unpack_double( $value, $p - 8 ) ];
317             }
318             elsif ( $byte == 0xca ) {
319 0           $p += 4;
320 0 0         $p <= length($value) or _insufficient('float');
321 0           return [ float32 => unpack_float( $value, $p - 4 ) ];
322             }
323             else {
324 0           _unexpected("byte 0x%02x", $byte);
325             }
326             }
327              
328             our %array_types = map { $_, 1 } qw(
329             fixarray array16 array32
330             );
331              
332             our %map_types = map { $_, 1 } qw(
333             fixmap map16 map32
334             );
335              
336             our %type_color;
337              
338             $type_color{$_} = "blue" for (keys %map_types, keys %array_types);
339             $type_color{$_} = "yellow" for qw(
340             fixstr str16 str32
341             );
342             $type_color{$_} = "red" for qw(
343             uint8 uint16 uint32 uint64
344             int8 int16 int32 int64
345             float32 float64
346             ),"positive fixint","negative fixint";
347             $type_color{$_} = "magenta" for qw(
348             true false nil
349             );
350              
351             our $type_name_color = "green";
352              
353             sub _ddmp {
354 0     0     my ( $depth, $data, $no_prespace ) = @_;
355 0           my $type = $data->[0];
356 0           my $value = $data->[1];
357 0 0         unless ($no_prespace) {
358 0           print " " x ($depth * 2);
359             }
360 0           print color($type_name_color);
361 0           print $type." ";
362 0           print color($type_color{$type});
363 0 0         if ($array_types{$type}) {
    0          
    0          
    0          
    0          
364 0           print "(".(scalar @{$value}).") [\n";
  0            
365 0           for (@{$value}) {
  0            
366 0           _ddmp($depth + 1, $_);
367             }
368 0           print color($type_color{$type});
369 0           print " " x ($depth * 2);
370 0           print "]";
371             } elsif ($map_types{$type}) {
372 0           print "(".((scalar @{$value}) / 2).") {\n";
  0            
373 0           my @values = @{$value};
  0            
374 0           while (@values) {
375 0           my $key = shift @values;
376 0           my $value = shift @values;
377 0           _ddmp($depth + 1, $key);
378 0           print color($type_color{$type});
379 0           print " " x (($depth + 1) * 2);
380 0           print "=> ";
381 0           _ddmp($depth + 1, $value, 1);
382             }
383 0           print color($type_color{$type});
384 0           print " " x ($depth * 2);
385 0           print "}";
386             } elsif ($type eq 'nil') {
387 0           print "nil";
388             } elsif ($type eq 'true') {
389 0           print "true";
390             } elsif ($type eq 'false') {
391 0           print "false";
392             } else {
393 0           print $value;
394             }
395 0           print "\n".color('reset');
396             }
397              
398             sub ddmp {
399 0     0 0   my ( $bytes ) = @_;
400 0           my $data = mp_unpack($bytes);
401 0           _ddmp(0, $data);
402             }
403              
404             1;
405              
406             __END__