File Coverage

blib/lib/Data/Dumper/MessagePack.pm
Criterion Covered Total %
statement 21 23 91.3
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 29 31 93.5


line stmt bran cond sub pod time code
1             package Data::Dumper::MessagePack;
2             our $AUTHORITY = 'cpan:GETTY';
3             $Data::Dumper::MessagePack::VERSION = '0.001';
4             # ABSTRACT: Dump MessagePack
5 1     1   649 use 5.008001;
  1         2  
6 1     1   3 use strict;
  1         2  
  1         19  
7 1     1   11 use warnings;
  1         2  
  1         27  
8 1     1   3 no warnings 'recursion';
  1         1  
  1         30  
9              
10 1     1   3 use Carp ();
  1         1  
  1         10  
11 1     1   3 use B ();
  1         1  
  1         11  
12 1     1   2 use Config;
  1         1  
  1         37  
13 1     1   381 use boolean;
  0            
  0            
14             use Term::ANSIColor qw( color );
15              
16             # Stolen from
17             # http://cpansearch.perl.org/src/GFUJI/Data-MessagePack-0.48/lib/Data/MessagePack/PP.pm
18              
19             use Exporter 'import';
20             our @EXPORT = qw( ddmp );
21             our @EXPORT_OK = qw( mp_unpack );
22              
23             BEGIN {
24             my $unpack_int64_slow;
25             my $unpack_uint64_slow;
26              
27             if(!eval { pack 'Q', 1 }) { # don't have quad types
28             # emulates quad types with Math::BigInt.
29             # very slow but works well.
30             $unpack_int64_slow = sub {
31             require Math::BigInt;
32             my $high = unpack_uint32( $_[0], $_[1] );
33             my $low = unpack_uint32( $_[0], $_[1] + 4);
34              
35             if($high < 0xF0000000) { # positive
36             $high = Math::BigInt->new( $high );
37             $low = Math::BigInt->new( $low );
38             return +($high << 32 | $low)->bstr;
39             }
40             else { # negative
41             $high = Math::BigInt->new( ~$high );
42             $low = Math::BigInt->new( ~$low );
43             return +( -($high << 32 | $low + 1) )->bstr;
44             }
45             };
46             $unpack_uint64_slow = sub {
47             require Math::BigInt;
48             my $high = Math::BigInt->new( unpack_uint32( $_[0], $_[1]) );
49             my $low = Math::BigInt->new( unpack_uint32( $_[0], $_[1] + 4) );
50             return +($high << 32 | $low)->bstr;
51             };
52             }
53              
54             *unpack_uint16 = sub { return unpack 'n', substr( $_[0], $_[1], 2 ) };
55             *unpack_uint32 = sub { return unpack 'N', substr( $_[0], $_[1], 4 ) };
56              
57             # For ARM OABI
58             my $bo_is_me = unpack ( 'd', "\x00\x00\xf0\x3f\x00\x00\x00\x00") == 1;
59             my $unpack_double_oabi;
60              
61             # for pack and unpack compatibility
62             if ( $] < 5.010 ) {
63             my $bo_is_le = ( $Config{byteorder} =~ /^1234/ );
64              
65             if ($bo_is_me) {
66             $unpack_double_oabi = sub {
67             my @v = unpack( 'V2', substr( $_[0], $_[1], 8 ) );
68             return unpack( 'd', pack( 'N2', @v[0,1] ) );
69             };
70             }
71              
72             *unpack_int16 = sub {
73             my $v = unpack 'n', substr( $_[0], $_[1], 2 );
74             return $v ? $v - 0x10000 : 0;
75             };
76             *unpack_int32 = sub {
77             no warnings; # avoid for warning about Hexadecimal number
78             my $v = unpack 'N', substr( $_[0], $_[1], 4 );
79             return $v ? $v - 0x100000000 : 0;
80             };
81              
82             # In reality, since 5.9.2 '>' is introduced. but 'n!' and 'N!'?
83             if($bo_is_le) {
84             *unpack_float = sub {
85             my @v = unpack( 'v2', substr( $_[0], $_[1], 4 ) );
86             return unpack( 'f', pack( 'n2', @v[1,0] ) );
87             };
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             };
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             };
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             };
101             }
102             else { # big endian
103             *unpack_float = sub { return unpack( 'f', substr( $_[0], $_[1], 4 ) ); };
104             *unpack_double = $unpack_double_oabi || sub { return unpack( 'd', substr( $_[0], $_[1], 8 ) ); };
105             *unpack_int64 = $unpack_int64_slow || sub { unpack 'q', substr( $_[0], $_[1], 8 ); };
106             *unpack_uint64 = $unpack_uint64_slow || sub { unpack 'Q', substr( $_[0], $_[1], 8 ); };
107             }
108             }
109             else { # 5.10.0 or later
110             if ($bo_is_me) {
111             $unpack_double_oabi = sub {
112             my $first_word = substr($_[0], $_[1], 4);
113             my $second_word = substr($_[0], $_[1] + 4, 4);
114             my $d_bin = $second_word . $first_word;
115             return unpack( 'd>', $d_bin );
116             };
117             }
118              
119             *unpack_float = sub { return unpack( 'f>', substr( $_[0], $_[1], 4 ) ); };
120             *unpack_double = $unpack_double_oabi || sub { return unpack( 'd>', substr( $_[0], $_[1], 8 ) ); };
121             *unpack_int16 = sub { return unpack( 'n!', substr( $_[0], $_[1], 2 ) ); };
122             *unpack_int32 = sub { return unpack( 'N!', substr( $_[0], $_[1], 4 ) ); };
123              
124             *unpack_int64 = $unpack_int64_slow || sub { return unpack( 'q>', substr( $_[0], $_[1], 8 ) ); };
125             *unpack_uint64 = $unpack_uint64_slow || sub { return unpack( 'Q>', substr( $_[0], $_[1], 8 ) ); };
126             }
127              
128             # fixin package symbols
129             no warnings 'once';
130             }
131              
132             sub _unexpected {
133             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             Carp::confess("Insufficient bytes (pos=$p, type=@_)");
145             }
146              
147             sub mp_unpack {
148             $p = 0; # init
149             my $data = _unpack( $_[0] );
150             if($p < length($_[0])) {
151             Carp::croak("Data::Dumper::MessagePack->mp_unpack: extra bytes");
152             }
153             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             my($value_ref, $byte, $x16, $x32, $x_fixbits) = @_;
219             if ( $byte == $x16 ) {
220             $p += 2;
221             $p <= length(${$value_ref}) or _insufficient('x/16');
222             return CORE::unpack 'n', substr( ${$value_ref}, $p - 2, 2 );
223             }
224             elsif ( $byte == $x32 ) {
225             $p += 4;
226             $p <= length(${$value_ref}) or _insufficient('x/32');
227             return CORE::unpack 'N', substr( ${$value_ref}, $p - 4, 4 );
228             }
229             else { # fix raw
230             return $byte & ~$x_fixbits;
231             }
232             }
233              
234             sub _unpack {
235             my ( $value ) = @_;
236             $p < length($value) or _insufficient('header byte');
237             # get a header byte
238             my $byte = ord( substr $value, $p, 1 );
239             $p++;
240              
241             # +/- fixnum, nil, true, false
242             return [ $detailed[$byte], $byte2value[$byte] ]
243             if $typemap[$byte] & $T_DIRECT;
244              
245             if ( $typemap[$byte] & $T_RAW ) {
246             my $size = _fetch_size(\$value, $byte, 0xda, 0xdb, 0xa0);
247             my $s = substr( $value, $p, $size );
248             length($s) == $size or _insufficient('raw');
249             $p += $size;
250             utf8::decode($s) if $_utf8;
251             return [ $detailed[$byte], $s ];
252             }
253             elsif ( $typemap[$byte] & $T_ARRAY ) {
254             my $size = _fetch_size(\$value, $byte, 0xdc, 0xdd, 0x90);
255             my @array;
256             push @array, _unpack( $value ) while --$size >= 0;
257             return [ $detailed[$byte], \@array ];
258             }
259             elsif ( $typemap[$byte] & $T_MAP ) {
260             my $size = _fetch_size(\$value, $byte, 0xde, 0xdf, 0x80);
261             my @map;
262             while(--$size >= 0) {
263             no warnings; # for undef key case
264             my $key = _unpack( $value );
265             my $val = _unpack( $value );
266             push @map, $key, $val;
267             }
268             return [ $detailed[$byte], \@map ];
269             }
270              
271             elsif ( $byte == 0xcc ) {
272             $p++;
273             $p <= length($value) or _insufficient('uint8');
274             my $number = CORE::unpack( 'C', substr( $value, $p - 1, 1 ) );
275             return [ uint8 => $number ];
276             }
277             elsif ( $byte == 0xcd ) {
278             $p += 2;
279             $p <= length($value) or _insufficient('uint16');
280             return [ uint16 => unpack_uint16( $value, $p - 2 ) ];
281             }
282             elsif ( $byte == 0xce ) {
283             $p += 4;
284             $p <= length($value) or _insufficient('uint32');
285             return [ uint32 => unpack_uint32( $value, $p - 4 ) ];
286             }
287             elsif ( $byte == 0xcf ) {
288             $p += 8;
289             $p <= length($value) or _insufficient('uint64');
290             return [ uint64 => unpack_uint64( $value, $p - 8 ) ];
291             }
292             elsif ( $byte == 0xd3 ) {
293             $p += 8;
294             $p <= length($value) or _insufficient('int64');
295             return [ int64 => unpack_int64( $value, $p - 8 ) ];
296             }
297             elsif ( $byte == 0xd2 ) {
298             $p += 4;
299             $p <= length($value) or _insufficient('int32');
300             return [ int32 => unpack_int32( $value, $p - 4 ) ];
301             }
302             elsif ( $byte == 0xd1 ) {
303             $p += 2;
304             $p <= length($value) or _insufficient('int16');
305             return [ int16 => unpack_int16( $value, $p - 2 ) ];
306             }
307             elsif ( $byte == 0xd0 ) {
308             $p++;
309             $p <= length($value) or _insufficient('int8');
310             my $number = CORE::unpack('c', substr( $value, $p - 1, 1 ) );
311             return [ int8 => $number ];
312             }
313             elsif ( $byte == 0xcb ) {
314             $p += 8;
315             $p <= length($value) or _insufficient('double');
316             return [ float64 => unpack_double( $value, $p - 8 ) ];
317             }
318             elsif ( $byte == 0xca ) {
319             $p += 4;
320             $p <= length($value) or _insufficient('float');
321             return [ float32 => unpack_float( $value, $p - 4 ) ];
322             }
323             else {
324             _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             my ( $depth, $data, $no_prespace ) = @_;
355             my $type = $data->[0];
356             my $value = $data->[1];
357             unless ($no_prespace) {
358             print " " x ($depth * 2);
359             }
360             print color($type_name_color);
361             print $type." ";
362             print color($type_color{$type});
363             if ($array_types{$type}) {
364             print "(".(scalar @{$value}).") [\n";
365             for (@{$value}) {
366             _ddmp($depth + 1, $_);
367             }
368             print color($type_color{$type});
369             print " " x ($depth * 2);
370             print "]";
371             } elsif ($map_types{$type}) {
372             print "(".((scalar @{$value}) / 2).") {\n";
373             my @values = @{$value};
374             while (@values) {
375             my $key = shift @values;
376             my $value = shift @values;
377             _ddmp($depth + 1, $key);
378             print color($type_color{$type});
379             print " " x (($depth + 1) * 2);
380             print "=> ";
381             _ddmp($depth + 1, $value, 1);
382             }
383             print color($type_color{$type});
384             print " " x ($depth * 2);
385             print "}";
386             } elsif ($type eq 'nil') {
387             print "nil";
388             } elsif ($type eq 'true') {
389             print "true";
390             } elsif ($type eq 'false') {
391             print "false";
392             } else {
393             print $value;
394             }
395             print "\n".color('reset');
396             }
397              
398             sub ddmp {
399             my ( $bytes ) = @_;
400             my $data = mp_unpack($bytes);
401             _ddmp(0, $data);
402             }
403              
404             1;
405              
406             __END__