File Coverage

blib/lib/Tangence/Type.pm
Criterion Covered Total %
statement 393 403 97.5
branch 140 166 84.3
condition 29 45 64.4
subroutine 66 66 100.0
pod 1 1 100.0
total 629 681 92.3


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2013-2024 -- leonerd@leonerd.org.uk
5              
6 14     14   177 use v5.26;
  14         51  
7 14     14   113 use warnings;
  14         27  
  14         844  
8 14     14   796 use Object::Pad 0.800;
  14         14001  
  14         790  
9              
10             package Tangence::Type 0.33;
11 14     14   9830 class Tangence::Type :isa(Tangence::Meta::Type);
  14         41  
  14         1827  
12              
13             =head1 NAME
14              
15             C - represent a C value type
16              
17             =head1 DESCRIPTION
18              
19             Objects in this class represent individual types that are sent over the wire
20             in L messages. This is a subclass of L which
21             provides additional methods that may be useful in server or client
22             implementations.
23              
24             =cut
25              
26             =head1 CONSTRUCTOR
27              
28             =head2 make
29              
30             $type = Tangence::Type->make( $primitive_sig );
31              
32             Returns an instance to represent a primitive type of the given signature.
33              
34             $type = Tangence::Type->make( list => $member_type );
35              
36             $type = Tangence::Type->make( dict => $member_type );
37              
38             Returns an instance to represent a list or dict aggregation containing members
39             of the given type.
40              
41             =cut
42              
43             sub make
44             {
45             # Subtle trickery is at work here
46             # Invoke our own superclass constructor, but pretend to be some higher
47             # subclass that's appropriate
48              
49 1016     1016 1 1723 shift;
50 1016 100       3153 if( @_ == 1 ) {
    100          
    50          
51 797         1652 my ( $type ) = @_;
52 797         1423 my $class = "Tangence::Type::Primitive::$type";
53 797 50       7163 $class->can( "make" ) or die "TODO: Need $class";
54              
55 797         2497 return $class->SUPER::make( $type );
56             }
57             elsif( $_[0] eq "list" ) {
58 140         268 shift;
59 140         619 return Tangence::Type::List->SUPER::make( list => @_ );
60             }
61             elsif( $_[0] eq "dict" ) {
62 79         141 shift;
63 79         329 return Tangence::Type::Dict->SUPER::make( dict => @_ );
64             }
65             else {
66 0         0 die "TODO: Not sure how to make a Tangence::Type->make( @_ )";
67             }
68             }
69              
70             =head1 METHODS
71              
72             =head2 default_value
73              
74             $value = $type->default_value;
75              
76             Returns a value suitable to use as an initial value for object properties.
77              
78             =head2 pack_value
79              
80             $type->pack_value( $message, $value );
81              
82             Appends a value of this type to the end of a L.
83              
84             =head2 unpack_value
85              
86             $value = $type->unpack_value( $message );
87              
88             Removes a value of this type from the start of a L.
89              
90             =cut
91              
92             class Tangence::Type::List :isa(Tangence::Type)
93 14     14   7807 {
  14         27  
  14         1437  
94 14     14   1248 use Carp;
  14         29  
  14         1027  
95 14     14   80 use Tangence::Constants;
  14         25  
  14         16040  
96              
97             method default_value { [] }
98              
99 129     129   233 method pack_value ( $message, $value )
  129         297  
  129         196  
  129         212  
  129         185  
100             {
101 129 100       617 ref $value eq "ARRAY" or croak "Cannot pack a list from non-ARRAY reference";
102              
103 128         482 $message->_pack_leader( DATA_LIST, scalar @$value );
104              
105 128         530 my $member_type = $self->member_type;
106 128         544 $member_type->pack_value( $message, $_ ) for @$value;
107             }
108              
109 129     129   229 method unpack_value ( $message )
  129         290  
  129         212  
  129         179  
110             {
111 129         416 my ( $type, $num ) = $message->_unpack_leader();
112 129 100       501 $type == DATA_LIST or croak "Expected to unpack a list but did not find one";
113              
114 128         520 my $member_type = $self->member_type;
115 128         228 my @values;
116 128         370 foreach ( 1 .. $num ) {
117 171         670 push @values, $member_type->unpack_value( $message );
118             }
119              
120 126         829 return \@values;
121             }
122             }
123              
124             class Tangence::Type::Dict :isa(Tangence::Type)
125             {
126 14     14   1398 use Carp;
  14         27  
  14         1047  
127 14     14   87 use Tangence::Constants;
  14         26  
  14         16949  
128              
129             method default_value { {} }
130              
131 71     71   123 method pack_value ( $message, $value )
  71         212  
  71         126  
  71         118  
  71         103  
132             {
133 71 100       346 ref $value eq "HASH" or croak "Cannot pack a dict from non-HASH reference";
134              
135 70         262 my @keys = keys %$value;
136 70 100       223 @keys = sort @keys if $Tangence::Message::SORT_HASH_KEYS;
137              
138 70         242 $message->_pack_leader( DATA_DICT, scalar @keys );
139              
140 70         349 my $member_type = $self->member_type;
141 70         300 $message->pack_str( $_ ), $member_type->pack_value( $message, $value->{$_} ) for @keys;
142             }
143              
144 71     71   128 method unpack_value ( $message )
  71         152  
  71         114  
  71         97  
145             {
146 71         206 my ( $type, $num ) = $message->_unpack_leader();
147 71 100       296 $type == DATA_DICT or croak "Expected to unpack a dict but did not find one";
148              
149 70         326 my $member_type = $self->member_type;
150 70         114 my %values;
151 70         257 foreach ( 1 .. $num ) {
152 153         533 my $key = $message->unpack_str();
153 152         1276 $values{$key} = $member_type->unpack_value( $message );
154             }
155              
156 69         240 return \%values;
157             }
158             }
159              
160             class Tangence::Type::Primitive::bool :isa(Tangence::Type)
161             {
162 14     14   1201 use Carp;
  14         28  
  14         1088  
163 14     14   113 use Tangence::Constants;
  14         29  
  14         13450  
164              
165             method default_value { "" }
166              
167 102     102   176 method pack_value ( $message, $value )
  102         268  
  102         170  
  102         168  
  102         213  
168             {
169 102 100       352 $message->_pack_leader( DATA_NUMBER, $value ? DATANUM_BOOLTRUE : DATANUM_BOOLFALSE );
170             }
171              
172 103     103   180 method unpack_value ( $message )
  103         212  
  103         150  
  103         171  
173             {
174 103         266 my ( $type, $num ) = $message->_unpack_leader();
175              
176 103 100       760 $type == DATA_NUMBER or croak "Expected to unpack a number(bool) but did not find one";
177 101 100       403 $num == DATANUM_BOOLFALSE and return !!0;
178 29 50       148 $num == DATANUM_BOOLTRUE and return !!1;
179 0         0 croak "Expected to find a DATANUM_BOOL subtype but got $num";
180             }
181             }
182              
183             class Tangence::Type::Primitive::_integral :isa(Tangence::Type)
184             {
185 14     14   1349 use Carp;
  14         28  
  14         994  
186 14     14   83 use Tangence::Constants;
  14         27  
  14         3442  
187              
188 14     14   101 use constant SUBTYPE => undef;
  14         45  
  14         22841  
189              
190             method default_value { 0 }
191              
192             my %format = (
193             DATANUM_UINT8, [ "C", 1 ],
194             DATANUM_SINT8, [ "c", 1 ],
195             DATANUM_UINT16, [ "S>", 2 ],
196             DATANUM_SINT16, [ "s>", 2 ],
197             DATANUM_UINT32, [ "L>", 4 ],
198             DATANUM_SINT32, [ "l>", 4 ],
199             DATANUM_UINT64, [ "Q>", 8 ],
200             DATANUM_SINT64, [ "q>", 8 ],
201             );
202              
203             sub _best_int_type_for ( $n )
204 667     667   1000 {
  667         1022  
  667         865  
205 667 100       1451 if( $n < 0 ) {
206 5 100       23 return DATANUM_SINT8 if $n >= -0x80;
207 2 50       7 return DATANUM_SINT16 if $n >= -0x8000;
208 2 50       10 return DATANUM_SINT32 if $n >= -0x80000000;
209 0         0 return DATANUM_SINT64;
210             }
211              
212 662 100       2674 return DATANUM_UINT8 if $n <= 0xff;
213 17 100       115 return DATANUM_UINT16 if $n <= 0xffff;
214 3 50       21 return DATANUM_UINT32 if $n <= 0xffffffff;
215 0         0 return DATANUM_UINT64;
216             }
217              
218 723     723   1085 method pack_value ( $message, $value )
  723         1612  
  723         1077  
  723         1096  
  723         1050  
219             {
220 723 100       1927 defined $value or croak "cannot pack_int(undef)";
221 722 100       1907 ref $value and croak "$value is not a number";
222 720 100       1993 $value == $value or croak "cannot pack_int(NaN)";
223 718 100 66     3417 $value == "+Inf" || $value == "-Inf" and croak "cannot pack_int(Inf)";
224              
225 716   66     2948 my $subtype = $self->SUBTYPE || _best_int_type_for( $value );
226 716         2467 $message->_pack_leader( DATA_NUMBER, $subtype );
227              
228 716         3111 $message->_pack( pack( $format{$subtype}[0], $value ) );
229             }
230              
231 724     724   1062 method unpack_value ( $message )
  724         1507  
  724         1059  
  724         955  
232             {
233 724         1852 my ( $type, $num ) = $message->_unpack_leader();
234              
235 724 100       2845 $type == DATA_NUMBER or croak "Expected to unpack a number but did not find one";
236 719 50       2030 exists $format{$num} or croak "Expected an integer subtype but got $num";
237              
238 719 100       2389 if( my $subtype = $self->SUBTYPE ) {
239 51 50       182 $subtype == $num or croak "Expected integer subtype $subtype, got $num";
240             }
241              
242 719         2221 my ( $n ) = unpack( $format{$num}[0], $message->_unpack( $format{$num}[1] ) );
243              
244 719         2363 return $n;
245             }
246             }
247              
248             class Tangence::Type::Primitive::u8 :isa(Tangence::Type::Primitive::_integral)
249             {
250 14     14   1670 use constant SUBTYPE => Tangence::Constants::DATANUM_UINT8;
  14         27  
  14         3325  
251             }
252              
253             class Tangence::Type::Primitive::s8 :isa(Tangence::Type::Primitive::_integral)
254             {
255 14     14   1697 use constant SUBTYPE => Tangence::Constants::DATANUM_SINT8;
  14         29  
  14         3051  
256             }
257              
258             class Tangence::Type::Primitive::u16 :isa(Tangence::Type::Primitive::_integral)
259             {
260 14     14   1573 use constant SUBTYPE => Tangence::Constants::DATANUM_UINT16;
  14         29  
  14         3035  
261             }
262              
263             class Tangence::Type::Primitive::s16 :isa(Tangence::Type::Primitive::_integral)
264             {
265 14     14   1536 use constant SUBTYPE => Tangence::Constants::DATANUM_SINT16;
  14         47  
  14         2956  
266             }
267              
268             class Tangence::Type::Primitive::u32 :isa(Tangence::Type::Primitive::_integral)
269             {
270 14     14   1734 use constant SUBTYPE => Tangence::Constants::DATANUM_UINT32;
  14         27  
  14         2979  
271             }
272              
273             class Tangence::Type::Primitive::s32 :isa(Tangence::Type::Primitive::_integral)
274             {
275 14     14   1568 use constant SUBTYPE => Tangence::Constants::DATANUM_SINT32;
  14         35  
  14         3116  
276             }
277              
278             class Tangence::Type::Primitive::u64 :isa(Tangence::Type::Primitive::_integral)
279             {
280 14     14   1555 use constant SUBTYPE => Tangence::Constants::DATANUM_UINT64;
  14         266  
  14         3016  
281             }
282              
283             class Tangence::Type::Primitive::s64 :isa(Tangence::Type::Primitive::_integral)
284             {
285 14     14   1909 use constant SUBTYPE => Tangence::Constants::DATANUM_SINT64;
  14         106  
  14         3286  
286             }
287              
288             class Tangence::Type::Primitive::int :isa(Tangence::Type::Primitive::_integral)
289             {
290             # empty
291             }
292              
293             class Tangence::Type::Primitive::float :isa(Tangence::Type)
294             {
295 14     14   4729 use Carp;
  14         54  
  14         1178  
296 14     14   89 use Tangence::Constants;
  14         25  
  14         3863  
297              
298             my $TYPE_FLOAT16 = Tangence::Type->make( 'float16' );
299              
300 14     14   127 use constant SUBTYPE => undef;
  14         27  
  14         23959  
301              
302             method default_value { 0.0 }
303              
304             my %format = (
305             # pack, bytes, NaN
306             DATANUM_FLOAT32, [ "f>", 4, "\x7f\xc0\x00\x00" ],
307             DATANUM_FLOAT64, [ "d>", 8, "\x7f\xf8\x00\x00\x00\x00\x00\x00" ],
308             );
309              
310             sub _best_type_for ( $value )
311 7     7   10 {
  7         11  
  7         7  
312             # Unpack as 64bit float and see if it's within limits
313 7         25 my $float64BIN = pack "d>", $value;
314              
315             # float64 == 1 / 11 / 52
316 7         24 my $exp64 = ( unpack "L>", $float64BIN & "\x7f\xf0\x00\x00" ) >> (52-32);
317              
318             # Zero is smallest
319 7 50       19 return DATANUM_FLOAT16 if $exp64 == 0;
320              
321             # De-bias
322 7         14 $exp64 -= 1023;
323              
324             # Special values might as well be float16
325 7 100       44 return DATANUM_FLOAT16 if $exp64 == 1024;
326              
327             # Smaller types are OK if the exponent will fit and there's no loss of
328             # mantissa precision
329              
330 5 100 100     28 return DATANUM_FLOAT16 if abs($exp64) < 15 &&
331             ($float64BIN & "\x00\x00\x03\xff\xff\xff\xff\xff") eq "\x00"x8;
332              
333 3 100 66     23 return DATANUM_FLOAT32 if abs($exp64) < 127 &&
334             ($float64BIN & "\x00\x00\x00\x00\x1f\xff\xff\xff") eq "\x00"x8;
335              
336 2         9 return DATANUM_FLOAT64;
337             }
338              
339 15     15   60 method pack_value ( $message, $value )
  15         29  
  15         21  
  15         17  
  15         18  
340             {
341 15 50       51 defined $value or croak "cannot pack undef as float";
342 15 50       26 ref $value and croak "$value is not a number";
343              
344 15   66     61 my $subtype = $self->SUBTYPE || _best_type_for( $value );
345              
346 15 100       41 return $TYPE_FLOAT16->pack_value( $message, $value ) if $subtype == DATANUM_FLOAT16;
347              
348 11         30 $message->_pack_leader( DATA_NUMBER, $subtype );
349             $message->_pack( $value == $value ?
350 11 100       62 pack( $format{$subtype}[0], $value ) : $format{$subtype}[2]
351             );
352             }
353              
354 15     15   28 method unpack_value ( $message )
  15         42  
  15         24  
  15         17  
355             {
356 15         47 my ( $type, $num ) = $message->_unpack_leader( "peek" );
357              
358 15 50       34 $type == DATA_NUMBER or croak "Expected to unpack a number but did not find one";
359 15 50 66     51 exists $format{$num} or $num == DATANUM_FLOAT16 or
360             croak "Expected a float subtype but got $num";
361              
362 15 100       51 if( my $subtype = $self->SUBTYPE ) {
363 8 50       16 $subtype == $num or croak "Expected float subtype $subtype, got $num";
364             }
365              
366 15 100       104 return $TYPE_FLOAT16->unpack_value( $message ) if $num == DATANUM_FLOAT16;
367              
368 11         27 $message->_unpack_leader; # no-peek
369              
370 11         36 my ( $n ) = unpack( $format{$num}[0], $message->_unpack( $format{$num}[1] ) );
371              
372 11         28 return $n;
373             }
374             }
375              
376             class Tangence::Type::Primitive::float16 :isa(Tangence::Type::Primitive::float)
377             {
378 14     14   1300 use Carp;
  14         27  
  14         935  
379 14     14   77 use Tangence::Constants;
  14         40  
  14         3004  
380              
381 14     14   90 use constant SUBTYPE => DATANUM_FLOAT16;
  14         23  
  14         21065  
382              
383             # TODO: This code doesn't correctly cope with Inf, -Inf or NaN
384              
385 10     10   30 method pack_value ( $message, $value )
  10         23  
  10         14  
  10         11  
  10         13  
386             {
387 10 50       47 defined $value or croak "cannot pack undef as float";
388 10 50       25 ref $value and croak "$value is not a number";
389              
390 10         43 my $float32 = unpack( "N", pack "f>", $value );
391              
392             # float32 == 1 / 8 / 23
393 10         22 my $sign = ( $float32 & 0x80000000 ) >> 31;
394 10         15 my $exp = ( ( $float32 & 0x7f800000 ) >> 23 ) - 127;
395 10         14 my $mant32 = ( $float32 & 0x007fffff );
396              
397             # float16 == 1 / 5 / 10
398 10         11 my $mant16;
399              
400 10 100       32 if( $exp == 128 ) {
    100          
    100          
401             # special value - Inf or NaN
402 4         8 $exp = 16;
403 4 100       11 $mant16 = $mant32 ? (1 << 9) : 0;
404 4 100       10 $sign = 0 if $mant16;
405             }
406             elsif( $exp > 15 ) {
407             # Too large - become Inf
408 1         4 $exp = 16;
409 1         2 $mant16 = 0;
410             }
411             elsif( $exp > -15 ) {
412 3         6 $mant16 = $mant32 >> 13;
413             }
414             else {
415             # zero or subnormal - become zero
416 2         2 $exp = -15;
417 2         5 $mant16 = 0;
418             }
419              
420 10         24 my $float16 = $sign << 15 |
421             ( $exp + 15 ) << 10 |
422             $mant16;
423              
424 10         36 $message->_pack_leader( DATA_NUMBER, DATANUM_FLOAT16 );
425 10         56 $message->_pack( pack "n", $float16 );
426             }
427              
428 10     10   17 method unpack_value ( $message )
  10         30  
  10         13  
  10         14  
429             {
430 10         34 my ( $type, $num ) = $message->_unpack_leader;
431              
432 10 50       52 $type == DATA_NUMBER or croak "Expected to unpack a number but did not find one";
433 10 50       23 $num == DATANUM_FLOAT16 or croak "Expected to unpack a float16 but found $num";
434              
435 10         28 my $float16 = unpack "n", $message->_unpack( 2 );
436              
437             # float16 == 1 / 5 / 10
438 10         21 my $sign = ( $float16 & 0x8000 ) >> 15;
439 10         19 my $exp = ( ( $float16 & 0x7c00 ) >> 10 ) - 15;
440 10         12 my $mant16 = ( $float16 & 0x03ff );
441              
442             # float32 == 1 / 8 / 23
443 10         12 my $mant32;
444              
445 10 100       25 if( $exp == 16 ) {
    100          
446             # special value - Inf or NaN
447 5         13 $exp = 128;
448 5 100       16 $mant32 = $mant16 ? (1 << 22) : 0;
449             }
450             elsif( $exp > -15 ) {
451 3         8 $mant32 = $mant16 << 13;
452             }
453             else {
454             # zero
455 2         2 $exp = -127;
456 2         3 $mant32 = 0;
457             }
458              
459 10         24 my $float32 = $sign << 31 |
460             ( $exp + 127 ) << 23 |
461             $mant32;
462              
463 10         49 return unpack( "f>", pack "N", $float32 );
464             }
465             }
466              
467             class Tangence::Type::Primitive::float32 :isa(Tangence::Type::Primitive::float)
468             {
469 14     14   1242 use Tangence::Constants;
  14         26  
  14         3318  
470              
471 14     14   93 use constant SUBTYPE => DATANUM_FLOAT32;
  14         24  
  14         3457  
472             }
473              
474             class Tangence::Type::Primitive::float64 :isa(Tangence::Type::Primitive::float)
475             {
476 14     14   1356 use Tangence::Constants;
  14         33  
  14         3385  
477              
478 14     14   92 use constant SUBTYPE => DATANUM_FLOAT64;
  14         26  
  14         3071  
479             }
480              
481             class Tangence::Type::Primitive::str :isa(Tangence::Type)
482             {
483 14     14   1567 use Carp;
  14         53  
  14         1278  
484 14     14   9527 use Encode qw( encode_utf8 decode_utf8 );
  14         333898  
  14         1864  
485 14     14   127 use Tangence::Constants;
  14         46  
  14         16576  
486              
487             method default_value { "" }
488              
489 532     532   904 method pack_value ( $message, $value )
  532         1157  
  532         856  
  532         836  
  532         746  
490             {
491 532 100       1448 defined $value or croak "cannot pack_str(undef)";
492 531 100       1993 ref $value and croak "$value is not a string";
493 527         1953 my $octets = encode_utf8( $value );
494 527         1881 $message->_pack_leader( DATA_STRING, length($octets) );
495 527         1403 $message->_pack( $octets );
496             }
497              
498 530     530   877 method unpack_value ( $message )
  530         1104  
  530         784  
  530         701  
499             {
500 530         1388 my ( $type, $num ) = $message->_unpack_leader();
501              
502 530 100       1951 $type == DATA_STRING or croak "Expected to unpack a string but did not find one";
503 525         1293 my $octets = $message->_unpack( $num );
504 525         4889 return decode_utf8( $octets );
505             }
506             }
507              
508             class Tangence::Type::Primitive::obj :isa(Tangence::Type)
509             {
510 14     14   1360 use Carp;
  14         33  
  14         1327  
511 14     14   160 use Scalar::Util qw( blessed );
  14         29  
  14         911  
512 14     14   82 use Tangence::Constants;
  14         27  
  14         24111  
513              
514             method default_value { undef }
515              
516 61     61   93 method pack_value ( $message, $value )
  61         150  
  61         79  
  61         79  
  61         73  
517             {
518 61         134 my $stream = $message->stream;
519              
520 61 100 66     421 if( !defined $value ) {
    100 33        
    50          
521 38         50 $message->_pack_leader( DATA_OBJECT, 0 );
522             }
523             elsif( blessed $value and $value->isa( "Tangence::Object" ) ) {
524 21         180 my $id = $value->id;
525 21         60 my $preamble = "";
526              
527 21 50       89 $value->{destroyed} and croak "Cannot pack destroyed object $value";
528              
529 21 100       175 $message->packmeta_construct( $value ) unless $stream->peer_hasobj->{$id};
530              
531 21         101 $message->_pack_leader( DATA_OBJECT, 4 );
532 21         109 $message->_pack( pack( "N", $id ) );
533             }
534             elsif( blessed $value and $value->isa( "Tangence::ObjectProxy" ) ) {
535 2         8 $message->_pack_leader( DATA_OBJECT, 4 );
536 2         7 $message->_pack( pack( "N", $value->id ) );
537             }
538             else {
539 0         0 croak "Do not know how to pack a " . ref($value);
540             }
541             }
542              
543 61     61   115 method unpack_value ( $message )
  61         152  
  61         103  
  61         91  
544             {
545 61         221 my ( $type, $num ) = $message->_unpack_leader();
546              
547 61         195 my $stream = $message->stream;
548              
549 61 50       183 $type == DATA_OBJECT or croak "Expected to unpack an object but did not find one";
550 61 100       224 return undef unless $num;
551 23 50       73 if( $num == 4 ) {
552 23         87 my ( $id ) = unpack( "N", $message->_unpack( 4 ) );
553 23         174 return $stream->get_by_id( $id );
554             }
555             else {
556 0         0 croak "Unexpected number of bits to encode an OBJECT";
557             }
558             }
559             }
560              
561             class Tangence::Type::Primitive::any :isa(Tangence::Type)
562             {
563 14     14   1327 use Carp;
  14         30  
  14         1171  
564 14     14   92 use Scalar::Util qw( blessed );
  14         28  
  14         877  
565 14     14   81 use Tangence::Constants;
  14         27  
  14         3228  
566              
567 14     14   606 use Syntax::Keyword::Match;
  14         3518  
  14         137  
568              
569 14     14   1832 no if $] >= 5.035008, warnings => "experimental::builtin";
  14         32  
  14         1686  
570 14     14   103 use constant HAVE_IS_BOOL => defined &builtin::is_bool;
  14         28  
  14         9876  
571              
572             my $TYPE_BOOL = Tangence::Type->make( 'bool' );
573             my $TYPE_INT = Tangence::Type->make( 'int' );
574             my $TYPE_FLOAT = Tangence::Type->make( 'float' );
575             my $TYPE_STR = Tangence::Type->make( 'str' );
576             my $TYPE_OBJ = Tangence::Type->make( 'obj' );
577             my $TYPE_ANY = Tangence::Type->make( 'any' );
578              
579             my $TYPE_LIST_ANY = Tangence::Type->make( list => $TYPE_ANY );
580             my $TYPE_DICT_ANY = Tangence::Type->make( dict => $TYPE_ANY );
581              
582             method default_value { undef }
583              
584 209     209   379 method pack_value ( $message, $value )
  209         455  
  209         348  
  209         336  
  209         261  
585             {
586 209 100 33     2168 if( !defined $value ) {
    100 66        
    50          
    100          
    100          
    50          
587 38         51 $TYPE_OBJ->pack_value( $message, undef );
588             }
589             elsif( !ref $value ) {
590 14     14   112 no warnings 'numeric';
  14         46  
  14         34562  
591              
592 21         37 my $is_numeric = do {
593 21         47 my $tmp = $value;
594              
595             # use X^X operator to distinguish actual numbers from strings
596             # If $tmp contains any non-ASCII bytes the it's definitely not a
597             # decimal representation of a number
598 21 100       224 $tmp =~ m/^[[:ascii:]]+$/ and ( $value ^ $value ) eq "0"
599             };
600              
601 21 100 100     257 if( HAVE_IS_BOOL && builtin::is_bool($value) ) {
    100 66        
    100          
602 1         6 $TYPE_BOOL->pack_value( $message, $value );
603             }
604             # test for integers, but exclude NaN
605             elsif( int($value) eq $value and $value == $value ) {
606 1         8 $TYPE_INT->pack_value( $message, $value );
607             }
608             elsif( $message->stream->_ver_can_num_float and $is_numeric ) {
609 2         25 $TYPE_FLOAT->pack_value( $message, $value );
610             }
611             else {
612 17         115 $TYPE_STR->pack_value( $message, $value );
613             }
614             }
615             elsif( blessed $value and $value->isa( "Tangence::Object" ) || $value->isa( "Tangence::ObjectProxy" ) ) {
616 0         0 $TYPE_OBJ->pack_value( $message, $value );
617             }
618 150         688 elsif( my $struct = eval { Tangence::Struct->for_perlname( ref $value ) } ) {
619 140         521 $message->pack_record( $value, $struct );
620             }
621             elsif( ref $value eq "ARRAY" ) {
622 5         29 $TYPE_LIST_ANY->pack_value( $message, $value );
623             }
624             elsif( ref $value eq "HASH" ) {
625 5         33 $TYPE_DICT_ANY->pack_value( $message, $value );
626             }
627             else {
628 0         0 croak "Do not know how to pack a " . ref($value);
629             }
630             }
631              
632 209     209   336 method unpack_value ( $message )
  209         2286  
  209         293  
  209         318  
633             {
634 209         666 my $type = $message->_peek_leader_type();
635              
636             match( $type : == ) {
637             case( DATA_NUMBER ) {
638 4         17 my ( undef, $num ) = $message->_unpack_leader( "peek" );
639 4 100 66     46 if( $num >= DATANUM_BOOLFALSE and $num <= DATANUM_BOOLTRUE ) {
    100 66        
    50 33        
640 1         6 return $TYPE_BOOL->unpack_value( $message );
641             }
642             elsif( $num >= DATANUM_UINT8 and $num <= DATANUM_SINT64 ) {
643 1         8 return $TYPE_INT->unpack_value( $message );
644             }
645             elsif( $num >= DATANUM_FLOAT16 and $num <= DATANUM_FLOAT64 ) {
646 2         13 return $TYPE_FLOAT->unpack_value( $message );
647             }
648             else {
649 0         0 croak "Do not know how to unpack DATA_NUMBER subtype $num";
650             }
651             }
652             case( DATA_STRING ) {
653 17         84 return $TYPE_STR->unpack_value( $message );
654             }
655             case( DATA_OBJECT ) {
656 38         90 return $TYPE_OBJ->unpack_value( $message );
657             }
658             case( DATA_LIST ) {
659 5         47 return $TYPE_LIST_ANY->unpack_value( $message );
660             }
661             case( DATA_DICT ) {
662 5         28 return $TYPE_DICT_ANY->unpack_value( $message );
663             }
664             case( DATA_RECORD ) {
665 140         392 return $message->unpack_record( undef );
666             }
667 209 100       943 default {
    100          
    100          
    100          
    100          
    50          
668 0           croak "Do not know how to unpack record of type $type";
669             }
670             }
671             }
672             }
673              
674             =head1 AUTHOR
675              
676             Paul Evans
677              
678             =cut
679              
680             0x55AA;