--- CBOR-XS/XS.pm 2013/11/20 16:29:02 1.21 +++ CBOR-XS/XS.pm 2013/11/22 15:28:38 1.22 @@ -629,13 +629,19 @@ =head1 TAG HANDLING AND EXTENSIONS -This section describes how this module handles specific tagged values and -extensions. If a tag is not mentioned here, then the default handling -applies (creating a CBOR::XS::Tagged object on decoding, and only encoding -the tag when explicitly requested). +This section describes how this module handles specific tagged values +and extensions. If a tag is not mentioned here and no additional filters +are provided for it, then the default handling applies (creating a +CBOR::XS::Tagged object on decoding, and only encoding the tag when +explicitly requested). Future versions of this module reserve the right to special case -additional tags (such as bigfloat or base64url). +additional tags (such as base64url). + +=head2 ENFORCED TAGS + +These tags are always handled when decoding, and their handling cannot be +overriden by the user. =over 4 @@ -669,6 +675,94 @@ =back +=head2 OPTIONAL TAGS + +These tags have default filters provided when decoding. Their handling can +be overriden by changing the C<%CBOR::XS::FILTER> entry for the tag, or by +providing a custom C function when decoding. + +When they result in decoding into a specific Perl class, the module +usually provides a corresponding C method as well. + +When any of these need to load additional modules that are not part of the +perl core distribution (e.g. L), it is (currently) up to the user to +provide these modules. The decoding usually fails with an exception if the +required module cannot be loaded. + +=over 4 + +=item 2, 3 (positive/negative bignum) + +These tags are decoded into L objects. The corresponding +C method encodes "small" bigints into normal CBOR +integers, and others into positive/negative CBOR bignums. + +=item 4, 5 (decimal fraction/bigfloat) + +Both decimal fractions and bigfloats are decoded into L +objects. The corresponding C method I +encodes into a decimal fraction. + +CBOR cannot represent bigfloats with I large exponents - conversion +of such big float objects is undefined. + +Also, NaN and infinities are not encoded properly. + +=item 21, 22, 23 (expected later JSON conversion) + +CBOR::XS is not a CBOR-to-JSON converter, and will simply ignore these +tags. + +=item 32 (URI) + +These objects decode into L objects. The corresponding +C method again results in a CBOR URI value. + +=back + +=cut + +our %FILTER = ( + # 0 # rfc4287 datetime, utf-8 + # 1 # unix timestamp, any + + 2 => sub { # pos bigint + require Math::BigInt; + Math::BigInt->new ("0x" . unpack "H*", pop) + }, + + 3 => sub { # neg bigint + require Math::BigInt; + -Math::BigInt->new ("0x" . unpack "H*", pop) + }, + + 4 => sub { # decimal fraction, array + require Math::BigFloat; + Math::BigFloat->new ($_[1][1] . "E" . $_[1][0]) + }, + + 5 => sub { # bigfloat, array + require Math::BigFloat; + scalar Math::BigFloat->new ($_[1][1])->blsft ($_[1][0], 2) + }, + + 21 => sub { pop }, # expected conversion to base64url encoding + 22 => sub { pop }, # expected conversion to base64 encoding + 23 => sub { pop }, # expected conversion to base16 encoding + + # 24 # embedded cbor, byte string + + 32 => sub { + require URI; + URI->new (pop) + }, + + # 33 # base64url rfc4648, utf-8 + # 34 # base64 rfc46484, utf-8 + # 35 # regex pcre/ecma262, utf-8 + # 36 # mime message rfc2045, utf-8 +); + =head1 CBOR and JSON @@ -760,6 +854,72 @@ =cut +our %FILTER = ( + # 0 # rfc4287 datetime, utf-8 + # 1 # unix timestamp, any + + 2 => sub { # pos bigint + require Math::BigInt; + Math::BigInt->new ("0x" . unpack "H*", pop) + }, + + 3 => sub { # neg bigint + require Math::BigInt; + -Math::BigInt->new ("0x" . unpack "H*", pop) + }, + + 4 => sub { # decimal fraction, array + require Math::BigFloat; + Math::BigFloat->new ($_[1][1] . "E" . $_[1][0]) + }, + + 5 => sub { # bigfloat, array + require Math::BigFloat; + scalar Math::BigFloat->new ($_[1][1])->blsft ($_[1][0], 2) + }, + + 21 => sub { pop }, # expected conversion to base64url encoding + 22 => sub { pop }, # expected conversion to base64 encoding + 23 => sub { pop }, # expected conversion to base16 encoding + + # 24 # embedded cbor, byte string + + 32 => sub { + require URI; + URI->new (pop) + }, + + # 33 # base64url rfc4648, utf-8 + # 34 # base64 rfc46484, utf-8 + # 35 # regex pcre/ecma262, utf-8 + # 36 # mime message rfc2045, utf-8 +); + +sub CBOR::XS::default_filter { + &{ $FILTER{$_[0]} or return } +} + +sub URI::TO_CBOR { + my $uri = $_[0]->as_string; + utf8::upgrade $uri; + CBOR::XS::tag 32, $uri +} + +sub Math::BigInt::TO_CBOR { + if ($_[0] >= -2147483648 && $_[0] <= 2147483647) { + $_[0]->numify + } else { + my $hex = substr $_[0]->as_hex, 2; + $hex = "0$hex" if 1 & length $hex; # sigh + CBOR::XS::tag $_[0] >= 0 ? 2 : 3, pack "H*", $hex + } +} + +sub Math::BigFloat::TO_CBOR { + my ($m, $e) = $_[0]->parts; + CBOR::XS::tag 4, [$e->numify, $m] +} + XSLoader::load "CBOR::XS", $VERSION; =head1 SEE ALSO