File Coverage

blib/lib/Tangence/Type.pm
Criterion Covered Total %
statement 391 413 94.6
branch 139 166 83.7
condition 25 42 59.5
subroutine 67 72 93.0
pod 1 1 100.0
total 623 694 89.7


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