File Coverage

blib/lib/MsgPack/Encoder.pm
Criterion Covered Total %
statement 194 194 100.0
branch 11 20 55.0
condition n/a
subroutine 54 54 100.0
pod 2 42 4.7
total 261 310 84.1


line stmt bran cond sub pod time code
1             package MsgPack::Encoder;
2             our $AUTHORITY = 'cpan:YANICK';
3             # ABSTRACT: Encode a structure into a MessagePack binary string
4             $MsgPack::Encoder::VERSION = '2.0.3';
5              
6 4     4   138240 use strict;
  4         20  
  4         116  
7 4     4   17 use warnings;
  4         8  
  4         109  
8              
9 4     4   873 use Moose;
  4         755005  
  4         25  
10 4     4   27413 use MooseX::NonMoose;
  4         3683  
  4         14  
11              
12 4     4   228493 use Exporter::Tiny;
  4         8  
  4         38  
13              
14             extends 'Exporter::Tiny';
15              
16 4     4   1596 use experimental 'postderef', 'signatures';
  4         5717  
  4         29  
17              
18 4     4   783 use overload '""' => \&encoded;
  4         8  
  4         32  
19              
20 4     4   1207 use Types::Standard qw/ Str ArrayRef Ref Int Any InstanceOf Undef HashRef Num StrictNum /;
  4         116117  
  4         45  
21 4     4   6185 use Type::Tiny;
  4         10  
  4         81  
22              
23 4     4   1656 use MsgPack::Type::Ext;
  4         11  
  4         15363  
24              
25             our @EXPORT = qw/
26             msgpack
27             msgpack_nil
28             msgpack_bool
29             msgpack_positive_fixnum
30             msgpack_negative_fixnum
31             msgpack_uint8
32             msgpack_uint16
33             msgpack_uint32
34             msgpack_uint64
35              
36             msgpack_int8
37             msgpack_int16
38             msgpack_int32
39             msgpack_int64
40              
41             msgpack_bin8
42             msgpack_bin16
43             msgpack_bin32
44             msgpack_float32
45             msgpack_float64
46              
47             msgpack_fixstr
48             msgpack_str8
49             msgpack_str16
50             msgpack_str32
51              
52             msgpack_fixarray
53             msgpack_array16
54             msgpack_array32
55              
56             msgpack_fixmap
57             msgpack_map16
58             msgpack_map32
59              
60             msgpack_fixext1
61             msgpack_fixext2
62             msgpack_fixext4
63             msgpack_fixext8
64             msgpack_fixext16
65              
66             msgpack_ext8
67             msgpack_ext16
68             msgpack_ext32
69             /;
70              
71             my $UInt64 = Type::Tiny->new(
72             parent => Int,
73             name => 'UInt64',
74             constraint => sub { $_ >= 0 },
75             );
76              
77             my $UInt32 = Type::Tiny->new(
78             parent => $UInt64,
79             name => 'UInt32',
80             constraint => sub { $_ < 2**32 },
81             );
82              
83             my $UInt16 = Type::Tiny->new(
84             parent => $UInt32,
85             name => 'UInt16',
86             constraint => sub { $_ < 2**16 },
87             );
88              
89             my $UInt8 = Type::Tiny->new(
90             parent => $UInt16,
91             name => 'UInt16',
92             constraint => sub { $_ < 2**8 },
93             );
94              
95             my $PositiveFixInt = Type::Tiny->new(
96             parent => $UInt8,
97             name => 'PositiveFixint',
98             constraint => sub { $_ < 2**7 },
99             );
100              
101             my $Int64 = Type::Tiny->new(
102             parent => Int,
103             name => 'Int64',
104             );
105              
106             my $Int32 = Type::Tiny->new(
107             parent => $Int64,
108             name => 'Int32',
109             constraint => sub { abs($_) < 2**31 },
110             );
111              
112             my $Int16 = Type::Tiny->new(
113             parent => $Int32,
114             name => 'Int16',
115             constraint => sub { abs($_) < 2**15 },
116             );
117              
118             my $Int8 = Type::Tiny->new(
119             parent => $Int16,
120             name => 'Int8',
121             constraint => sub { abs($_) < 2**7 },
122             );
123              
124             my $NegativeFixInt = Type::Tiny->new(
125             parent => $Int8,
126             name => 'NegativeFixint',
127             constraint => sub { $_ < 0 and $_ > -2**5 },
128             );
129              
130             my $Str32 = Type::Tiny->new(
131             parent => Str,
132             name => 'Str32',
133             );
134              
135             my $Str16 = Type::Tiny->new(
136             parent => $Str32,
137             name => 'Str16',
138             constraint => sub { length $_ < 2**16 }
139             );
140              
141             my $Str8 = Type::Tiny->new(
142             parent => $Str16,
143             name => 'Str8',
144             constraint => sub { length $_ < 2**8 }
145             );
146              
147             my $FixStr = Type::Tiny->new(
148             parent => $Str8,
149             name => 'FixStr',
150             constraint => sub { length $_ <= 31 }
151             );
152              
153             my $Array32 = Type::Tiny->new(
154             parent => ArrayRef,
155             name => 'Array32',
156             );
157              
158             my $Array16 = Type::Tiny->new(
159             parent => $Array32,
160             name => 'Array16',
161             constraint => sub { @$_ < 2**16 },
162             );
163              
164             my $FixArray = Type::Tiny->new(
165             parent => ArrayRef,
166             name => 'FixArray',
167             constraint => sub { @$_ < 16 },
168             );
169              
170             my $Nil = Type::Tiny->new(
171             parent => Undef,
172             name => 'Nil',
173             );
174              
175             my $Map32 = Type::Tiny->new(
176             parent => HashRef,
177             name => 'Map32',
178             );
179              
180             my $Map16 = Type::Tiny->new(
181             parent => $Map32,
182             name => 'Map16',
183             constraint => sub { keys %$_ < 2**16 },
184             );
185              
186             my $FixMap = Type::Tiny->new(
187             parent => $Map16,
188             name => 'FixMap',
189             constraint => sub { keys %$_ < 16 }
190             );
191              
192             my $Boolean = Type::Tiny->new(
193             parent => InstanceOf['MsgPack::Type::Boolean'],
194             name => 'Boolean'
195             );
196              
197             my $FixExt1 = Type::Tiny->new(
198             parent => InstanceOf['MsgPack::Type::Ext'],
199             name => 'FixExt1',
200             constraint => sub { $_->fix and $_->size == 1 },
201             );
202             my $FixExt2 = Type::Tiny->new(
203             parent => InstanceOf['MsgPack::Type::Ext'],
204             name => 'FixExt2',
205             constraint => sub { $_->fix and $_->size == 2 },
206             );
207             my $FixExt4 = Type::Tiny->new(
208             parent => InstanceOf['MsgPack::Type::Ext'],
209             name => 'FixExt4',
210             constraint => sub { $_->fix and $_->size == 4 },
211             );
212             my $FixExt8 = Type::Tiny->new(
213             parent => InstanceOf['MsgPack::Type::Ext'],
214             name => 'FixExt8',
215             constraint => sub { $_->fix and $_->size == 8 },
216             );
217             my $FixExt16 = Type::Tiny->new(
218             parent => InstanceOf['MsgPack::Type::Ext'],
219             name => 'FixExt16',
220             constraint => sub { $_->fix and $_->size == 16 },
221             );
222              
223             my $Ext8 = Type::Tiny->new(
224             parent => InstanceOf['MsgPack::Type::Ext'],
225             name => 'Ext8',
226             constraint => sub { !$_->fix and $_->size == 8 },
227             );
228             my $Ext16 = Type::Tiny->new(
229             parent => InstanceOf['MsgPack::Type::Ext'],
230             name => 'Ext16',
231             constraint => sub { !$_->fix and $_->size == 16 },
232             );
233             my $Ext32 = Type::Tiny->new(
234             parent => InstanceOf['MsgPack::Type::Ext'],
235             name => 'Ext32',
236             constraint => sub { !$_->fix and $_->size == 32 },
237             );
238              
239             my $Float64 = Type::Tiny->new(
240             parent => StrictNum,
241             name => 'Float64',
242             );
243              
244 133     133   183 sub _packed($value) { bless \$value, 'MessagePacked' }
  133         185  
  133         182  
  133         563  
245              
246 1     1 0 4303 sub msgpack_float32($number) { pack 'Cf', 0xca, $number }
  1         2  
  1         1  
  1         13  
247 3     3 0 4453 sub msgpack_float64($number) { pack 'Cd', 0xcb, $number }
  3         6  
  3         4  
  3         24  
248              
249 2     2 0 7949 sub msgpack_bin8($binary) { ${ encode_bin8($binary) } }
  2         4  
  2         3  
  2         4  
  2         9  
250 2     2 0 2746 sub msgpack_bin16($binary) { ${ encode_bin16($binary) } }
  2         5  
  2         4  
  2         4  
  2         9  
251 2     2 0 2618 sub msgpack_bin32($binary) { ${ encode_bin32($binary) } }
  2         5  
  2         4  
  2         4  
  2         7  
252              
253 2     2 0 3095 sub msgpack_nil { pack 'C', 0xc0 }
254              
255 10     10 0 2316 sub msgpack_bool($bool) { chr 0xc2 + $bool }
  10         17  
  10         11  
  10         44  
256              
257 71     71 0 3355 sub msgpack_positive_fixnum($num) { chr $num }
  71         100  
  71         86  
  71         183  
258 3     3 0 2654 sub msgpack_negative_fixnum($num) { chr 0xe0 - $num }
  3         6  
  3         3  
  3         15  
259              
260 2     2 0 2567 sub msgpack_uint8($num) { pack 'C*', 0xcc, $num }
  2         4  
  2         3  
  2         11  
261 2     2 0 2905 sub msgpack_uint16($num) { pack 'Cn*', 0xcd, $num }
  2         4  
  2         4  
  2         13  
262 2     2 0 2792 sub msgpack_uint32($num) { pack 'CN*', 0xce, $num }
  2         3  
  2         3  
  2         11  
263 3     3 0 2563 sub msgpack_uint64($num) { pack 'CN*', 0xcf, int($num/(2**32)), $num%(2**32) }
  3         5  
  3         5  
  3         18  
264              
265 2     2 0 2507 sub msgpack_int8($num) { pack 'Cc*', 0xd0, $num }
  2         4  
  2         3  
  2         11  
266 3     3 0 2326 sub msgpack_int16($num) { pack 'Cs*', 0xd1, $num }
  3         5  
  3         3  
  3         19  
267 2     2 0 2327 sub msgpack_int32($num) { pack 'Cl*', 0xd2, $num }
  2         3  
  2         2  
  2         10  
268 2     2 0 2317 sub msgpack_int64($num) { pack 'Cq*', 0xd3, $num }
  2         4  
  2         3  
  2         11  
269              
270              
271             sub msgpack_fixext1 {
272 3 100   3 0 4722 my $ext = @_ == 1 ? shift
273             : MsgPack::Type::Ext->new( fix => 1, size => 1, type => $_[0], data => $_[1] );
274 3         1189 chr( 0xd4 ) . chr( $ext->type ) . $ext->padded_data;
275             }
276             sub msgpack_fixext2 {
277 1 50   1 0 2399 my $ext = @_ == 1 ? shift
278             : MsgPack::Type::Ext->new( fix => 1, size => 2, type => $_[0], data => $_[1] );
279 1         1072 chr( 0xd5 ) . chr( $ext->type ) . $ext->padded_data;
280             }
281             sub msgpack_fixext4 {
282 1 50   1 0 2368 my $ext = @_ == 1 ? shift
283             : MsgPack::Type::Ext->new( fix => 1, size => 4, type => $_[0], data => $_[1] );
284 1         1099 chr( 0xd6 ) . chr( $ext->type ) . $ext->padded_data;
285             }
286             sub msgpack_fixext8 {
287 1 50   1 0 2424 my $ext = @_ == 1 ? shift
288             : MsgPack::Type::Ext->new( fix => 1, size => 8, type => $_[0], data => $_[1] );
289 1         1074 chr( 0xd7 ) . chr( $ext->type ) . $ext->padded_data;
290             }
291             sub msgpack_fixext16 {
292 1 50   1 0 2498 my $ext = @_ == 1 ? shift
293             : MsgPack::Type::Ext->new( fix => 1, size => 16, type => $_[0], data => $_[1] );
294 1         1070 chr( 0xd8 ) . chr( $ext->type ) . $ext->padded_data;
295             }
296             sub msgpack_ext8 {
297 1 50   1 0 4069 my $ext = @_ == 1 ? shift
298             : MsgPack::Type::Ext->new( fix => 0, type => $_[0], data => $_[1] );
299 1         865 chr( 0xc7 ) . chr( $ext->size ), chr( $ext->type ) . $ext->padded_data;
300             }
301             sub msgpack_ext16 {
302 1 50   1 0 2424 my $ext = @_ == 1 ? shift
303             : MsgPack::Type::Ext->new( fix => 0, type => $_[0], data => $_[1] );
304 1         877 pack( 'CS>C', 0xc8, $ext->size, $ext->type ) . $ext->padded_data;
305             }
306             sub msgpack_ext32 {
307 1 50   1 0 2479 my $ext = @_ == 1 ? shift
308             : MsgPack::Type::Ext->new( fix => 0, type => $_[0], data => $_[1] );
309 1         867 pack( 'CL>C', 0xc9, $ext->size, $ext->type ) . $ext->padded_data;
310             }
311              
312             sub msgpack_str8 {
313 3     3 0 2590 my $string = shift;
314 3         23 chr( 0xd9 ) . chr( length $string ) . $string;
315             }
316              
317             sub msgpack_str16 {
318 2     2 0 2744 my $string = shift;
319 2         22 pack( 'CS>', 0xda, length $string ). $string;
320             }
321              
322             sub msgpack_str32 {
323 1     1 0 2704 my $string = shift;
324 1         13 pack( 'CL>', 0xdb, length $string ). $string;
325             }
326              
327             sub msgpack_fixstr {
328 13     13 0 3680 my $string = shift;
329 13         74 chr( 0xa0 + length $string ) . $string;
330             }
331              
332             my $MessagePack;
333              
334             sub msgpack_fixarray {
335 16     16 0 4145 my @inner = @{ shift @_ };
  16         50  
336              
337 16         40 my $size = @inner;
338              
339 16         57 join '', chr( 0x90 + $size ), map { $$_ } map { $MessagePack->assert_coerce($_) } @inner;
  45         466  
  45         819  
340             }
341              
342             sub msgpack_array16 {
343 2     2 0 3579 my @inner = @{ shift @_ };
  2         9  
344              
345 2         5 my $size = @inner;
346              
347             join '', pack( 'CS>', 0xdc, $size ),
348 2         15 map { $$_ } map { $MessagePack->assert_coerce($_) } @inner;
  19         123  
  19         348  
349             }
350              
351             sub msgpack_array32 {
352 1     1 0 3572 my @inner = @{ shift @_ };
  1         4  
353              
354 1         3 my $size = @inner;
355              
356             join '', pack( 'CL>', 0xdd, $size ),
357 1         6 map { $$_ } map { $MessagePack->assert_coerce($_) } @inner;
  1         45  
  1         9  
358             }
359              
360             sub msgpack_fixmap {
361 3     3 0 5422 my @inner = %{ shift @_ };
  3         12  
362              
363 3         11 my $size = @inner/2;
364              
365 3         11 join '', chr( 0x80 + $size ), map { $$_ } map { $MessagePack->assert_coerce($_) } @inner;
  14         93  
  14         228  
366             }
367              
368             sub msgpack_map16 {
369 1     1 0 3266 my @inner = %{ shift @_ };
  1         4  
370              
371 1         4 my $size = @inner/2;
372              
373             join '', pack( 'CS>', 0xde, $size ),
374 1         6 map { $$_ } map { $MessagePack->assert_coerce($_) } @inner;
  2         29  
  2         32  
375             }
376              
377             sub msgpack_map32 {
378 1     1 0 3261 my @inner = %{ shift @_ };
  1         4  
379              
380 1         4 my $size = @inner/2;
381              
382             join '', pack( 'CL>', 0xdf, $size ),
383 1         7 map { $$_ } map { $MessagePack->assert_coerce($_) } @inner;
  2         31  
  2         43  
384             }
385              
386              
387              
388              
389             $MessagePack = Type::Tiny->new(
390             parent => InstanceOf['MessagePacked'],
391             name => 'MessagePack',
392             )->plus_coercions(
393             $Boolean => sub { _packed msgpack_bool $_ },
394             $PositiveFixInt => sub { _packed msgpack_positive_fixnum $_ },
395             $NegativeFixInt => sub { _packed msgpack_negative_fixnum $_ },
396             $UInt8 => sub { _packed msgpack_uint8 $_ },
397             $UInt16 => sub { _packed msgpack_uint16 $_ },
398             $UInt32 => sub { _packed msgpack_uint32 $_ },
399             $UInt64 => sub { _packed msgpack_uint64 $_ },
400             $Int8 => sub { _packed msgpack_int8 $_ },
401             $Int16 => sub { _packed msgpack_int16 $_ },
402             $Int32 => sub { _packed msgpack_int32 $_ },
403             $Int64 => sub { _packed msgpack_int64 $_ },
404             $Float64 => sub { _packed msgpack_float64 $_ },
405             $FixStr ,=> sub { _packed msgpack_fixstr $_ },
406             $Str8 ,=> sub { _packed msgpack_str8 $_ },
407             $Str16 ,=> sub { _packed msgpack_str16 $_ },
408             $Str32 ,=> sub { _packed msgpack_str32 $_ },
409             $FixArray => sub { _packed msgpack_fixarray $_ },
410             $Array16 => sub { _packed msgpack_array16 $_ },
411             $Array32 => sub { _packed msgpack_array32 $_ },
412             $Nil => \&encode_nil,
413             $FixMap => sub { _packed msgpack_fixmap $_ },
414             $Map16 => sub { _packed msgpack_map16 $_ },
415             $Map32 => sub { _packed msgpack_map32 $_ },
416             $FixExt1 => sub { _packed msgpack_fixext1 $_ },
417             $FixExt2 => sub { _packed msgpack_fixext2 $_ },
418             $FixExt4 => sub { _packed msgpack_fixext4 $_ },
419             $FixExt8 => sub { _packed msgpack_fixext8 $_ },
420             $FixExt16 => sub { _packed msgpack_fixext16 $_ },
421             $Ext8 => sub { _packed msgpack_ext8 $_ },
422             $Ext16 => sub { _packed msgpack_ext16 $_ },
423             $Ext32 => sub { _packed msgpack_ext32 $_ },
424             );
425              
426             has struct => (
427             isa => $MessagePack,
428             is => 'ro',
429             required => 1,
430             coerce => 1,
431             );
432              
433             sub BUILDARGS {
434 27     27 1 76954 shift;
435 27 50       158 return { @_ == 1 ? ( struct => $_ ) : @_ };
436             }
437              
438             sub encoded {
439 27     27 1 6597 my $self = shift;
440 27         650 my $x = $MessagePack->assert_coerce($self->struct);
441 27         1932 return $$x;
442             }
443              
444              
445              
446              
447             sub encode_nil {
448 3     3 0 923 _packed chr 0xc0;
449             }
450              
451 2     2 0 4 sub encode_bin8($binary) {
  2         5  
  2         3  
452 2         12 _packed join '', chr( 0xc4 ), chr length($binary), $binary;
453             }
454              
455 4     4   9 sub _variable_length($number,$width=1) {
  4         7  
  4         8  
  4         7  
456 4         8 my $final = '';
457 4         14 while($width--){
458 12         30 $final = chr( $number % 2**8 ) . $final;
459 12         26 $number /= 2**8;
460             }
461 4 50       13 die "number too big for type\n" if $number >= 1;
462 4         19 $final;
463             }
464              
465 2     2 0 4 sub encode_bin16($binary) {
  2         5  
  2         4  
466 2         8 _packed join '', chr( 0xc5 ), _variable_length( length($binary), 2 ), $binary;
467             }
468              
469 2     2 0 3 sub encode_bin32($binary) {
  2         4  
  2         4  
470 2         9 _packed join '', chr( 0xc6 ), _variable_length( length($binary), 4 ), $binary;
471             }
472              
473              
474 17     17 0 5477 sub msgpack($data) {
  17         33  
  17         35  
475 17         98 $MessagePack->assert_coerce($data)->$*;
476             }
477              
478             1;
479              
480             __END__
481              
482             =pod
483              
484             =encoding UTF-8
485              
486             =head1 NAME
487              
488             MsgPack::Encoder - Encode a structure into a MessagePack binary string
489              
490             =head1 VERSION
491              
492             version 2.0.3
493              
494             =head1 SYNOPSIS
495              
496             use MsgPack::Encoder;
497              
498             my $binary = MsgPack::Encoder->new( struct => [ "hello world" ] )->encoded;
499              
500             # using the msgpack_* functions
501              
502             my $binary = msgpack [ "hello world" ];
503             # or
504             my $specific = msgpack_array16 [ "hello", "world" ];
505              
506             use MsgPack::Decoder;
507              
508             my $struct = MsgPack::Decoder->new->read_all($binary);
509              
510             =head1 DESCRIPTION
511              
512             C<MsgPack::Encoder> objects encapsulate a Perl data structure, and provide
513             its MessagePack serialization.
514              
515             In addition of the L<MsgPack::Encoder> class, the module exports
516             C<msgpack_*> helper functions that would convert data structures to their
517             MessagePack representations.
518              
519             =head1 EXPORTED FUNCTIONS
520              
521             =head2 Explicit conversion
522              
523             $packed = msgpack_nil();
524              
525             $packed = msgpack_bool($boolean);
526              
527             $packed = msgpack_positive_fixnum($num);
528             $packed = msgpack_negative_fixnum($num);
529              
530             $packed = msgpack_uint8($int);
531             $packed = msgpack_uint16($int);
532             $packed = msgpack_uint32($int);
533             $packed = msgpack_uint64($int);
534              
535             $packed = msgpack_int8($int);
536             $packed = msgpack_int16($int);
537             $packed = msgpack_int32($int);
538             $packed = msgpack_int64($int);
539              
540             $packed = msgpack_bin8($binary);
541             $packed = msgpack_bin16($binary);
542             $packed = msgpack_bin32($binary);
543              
544             $packed = msgpack_float32($float);
545             $packed = msgpack_float64($float);
546              
547             $packed = msgpack_fixstr($string);
548             $packed = msgpack_str8($string);
549             $packed = msgpack_str16($string);
550             $packed = msgpack_str32($string);
551              
552             $packed = msgpack_fixarray(\@array);
553             $packed = msgpack_array16(\@array);
554             $packed = msgpack_array32(\@array);
555              
556             $packed = msgpack_fixmap(\%hash);
557             $packed = msgpack_map16(\%hash);
558             $packed = msgpack_map32(\%hash);
559              
560             $packed = msgpack_fixext1($type => $data);
561             $packed = msgpack_fixext2($type => $data);
562             $packed = msgpack_fixext4($type => $data);
563             $packed = msgpack_fixext8($type => $data);
564             $packed = msgpack_fixext16($type => $data);
565              
566             $packed = msgpack_ext8($type => $data);
567             $packed = msgpack_ext16($type => $data);
568             $packed = msgpack_ext32($type => $data);
569              
570             =head2 Coerced conversion
571              
572             $packed = msgpack( $data )
573              
574             Which is equivalent to
575              
576             $packed = MsgPack::Encoder->new(struct=>$data);
577              
578             =head1 OBJECT OVERLOADING
579              
580             =head2 Stringification
581              
582             The stringification of a C<MsgPack::Encoder> object is its MessagePack encoding.
583              
584             print MsgPack::Encoder->new( struct => $foo );
585              
586             # equivalent to
587              
588             print MsgPack::Encoder->new( struct => $foo )->encoded;
589              
590             =head1 METHODS
591              
592             =head2 new( struct => $perl_struct )
593              
594             The constructor accepts a single argument, C<struct>, which is the perl structure (or simple scalar)
595             to encode.
596              
597             =head2 encoded
598              
599             Returns the MessagePack representation of the structure.
600              
601             =head1 AUTHOR
602              
603             Yanick Champoux <yanick@cpan.org>
604              
605             =head1 COPYRIGHT AND LICENSE
606              
607             This software is copyright (c) 2019, 2017, 2016, 2015 by Yanick Champoux.
608              
609             This is free software; you can redistribute it and/or modify it under
610             the same terms as the Perl 5 programming language system itself.
611              
612             =cut