Codec-CBOR
view release on metacpan or search on metacpan
## `encode($data)`
Encodes a Perl data structure into a CBOR byte string.
- Strings: Encoded as Major Type 3 (UTF-8) if they are not references.
- Byte strings: Encoded as Major Type 2 if provided as a scalar reference (e.g., `\$binary_data`).
- Tags: Registered class handlers can produce tagged values (Major Type 6).
## `decode($input)`
Decodes a single CBOR object from a byte string or a filehandle. Returns the decoded Perl data structure.
## `decode_sequence($input)`
Decodes a sequence of concatenated CBOR objects. Returns an array (in list context) or an arrayref.
## `add_tag_handler($tag, $callback)`
Registers a callback to handle a specific CBOR tag during decoding.
```perl
lib/Codec/CBOR.pm view on Meta::CPAN
if ( $major == 1 ) { return -1 - $self->_decode_value( $info, $fh ); }
if ( $major == 2 ) { # Byte string
my $len = $self->_decode_value( $info, $fh );
read( $fh, my $buf, $len );
return $buf;
}
if ( $major == 3 ) { # UTF-8 string
my $len = $self->_decode_value( $info, $fh );
read( $fh, my $buf, $len );
my $decoded = $buf;
return $decoded if utf8::decode($decoded);
# Fallback for invalid UTF-8: return raw bytes
return $buf;
}
if ( $major == 4 ) { # Array
my $len = $self->_decode_value( $info, $fh );
my @arr;
push @arr, $self->_decode_item($fh) for 1 .. $len;
return \@arr;
}
lib/Codec/CBOR.pod view on Meta::CPAN
=item * Strings: Encoded as Major Type 3 (UTF-8) if they are not references.
=item * Byte strings: Encoded as Major Type 2 if provided as a scalar reference (e.g., C<\$binary_data>).
=item * Tags: Registered class handlers can produce tagged values (Major Type 6).
=back
=head2 C<decode($input)>
Decodes a single CBOR object from a byte string or a filehandle. Returns the decoded Perl data structure.
=head2 C<decode_sequence($input)>
Decodes a sequence of concatenated CBOR objects. Returns an array (in list context) or an arrayref.
=head2 C<add_tag_handler($tag, $callback)>
Registers a callback to handle a specific CBOR tag during decoding.
$codec->add_tag_handler(42 => sub ($data) {
t/01_basic.t view on Meta::CPAN
my @cases = (
undef, 0, 1, 23, 24, 255, 256, 65535, 65536, 4294967295, 4294967296, -1, -24, -255, -65536, 'hello',
'world with spaces',
"unicode \x{1f600}",
[ 1, 2, 3 ],
{ a => 1, b => 2 },
{ inner => [ { x => 1 } ], y => 'z' }
);
for my $case (@cases) {
my $encoded = $codec->encode($case);
my $decoded = $codec->decode($encoded);
is $decoded, $case, 'Roundtrip for ' . ( defined $case ? ( ref $case // $case ) : 'undef' );
}
};
subtest 'DAG-CBOR Determinism' => sub {
my $h1 = { a => 1, b => 2, c => 3 };
my $h2 = { c => 3, a => 1, b => 2 };
is $codec->encode($h1), $codec->encode($h2), 'Hash encoding is deterministic';
};
subtest 'Sequence Decoding' => sub {
my $data = $codec->encode( { a => 1 } ) . $codec->encode( { b => 2 } );
my @items = $codec->decode_sequence($data);
t/01_basic.t view on Meta::CPAN
};
subtest 'Tag 42 (CID)' => sub {
{
# Mock a CID object
package Mock::CID;
sub new { bless { raw => 'foobar' }, shift }
sub raw { shift->{raw} }
}
my $cid = Mock::CID->new();
my $encoded = $codec->encode($cid);
my $decoded = $codec->decode($encoded);
is ref $decoded, 'HASH', 'Decoded Tag 42 into hash (default handler)';
# Default handler strips the leading 00 if present
is $decoded->{cid_raw}, 'foobar', 'Extracted cid_raw matches';
};
#
done_testing;
t/02_fixtures.t view on Meta::CPAN
{ name => 'dagpb_1link', hex => 'a1654c696e6b7381a16448617368d82a58230012207521fe19c374a97759226dc5c0c8e674e73950e81b211f7dd3b6b30883a08a51' }
);
my $garbage_00
= '8679075f593b44722664215c2961322b62222ee298ba0955235125776c6c7d21402622493834676426343b2c33425f2e674274356d6c0a6d6f6d642c292e4a3b386b45425d4e627b5935744834452278787a6b793c747b7c243a553b656b5a74704a6f2d6f6c4d606a79494a35574e6e7d4c3d4022464b4c772...
# Garbage fixtures (Round-trip check)
subtest 'Garbage Round-trip' => sub {
my @garbage = ( { name => 'garbage-00', hex => $garbage_00 } );
for my $f (@garbage) {
my $bin = pack( 'H*', $f->{hex} );
my $decoded = $codec->decode($bin);
ok $decoded, 'Decoded ' . $f->{name};
}
};
# Basic & Floats
subtest 'Basic and Float Fixtures' => sub {
for my $f ( @basic_fixtures, @float_fixtures ) {
my $encoded = $codec->encode( $f->{data} );
is( unpack( 'H*', $encoded ), $f->{hex}, 'Encoding matches for ' . $f->{name} );
my $decoded = $codec->decode( pack( 'H*', $f->{hex} ) );
if ( builtin::blessed( $f->{data} ) && $f->{data}->isa('Codec::CBOR::Boolean') ) {
ok $decoded->isa('Codec::CBOR::Boolean'), 'Decoded is a Boolean object';
is $decoded, $f->{data}, 'Boolean value matches';
}
else {
is normalize($decoded), ( $f->{name} =~ /float-[-\d]/ ? float( normalize( $f->{data} ), tolerance => 0.01 ) : normalize( $f->{data} ) ),
'Decoding matches for ' . $f->{name};
}
}
};
# CIDs
subtest 'CID Fixtures' => sub {
for my $f (@cid_fixtures) {
my $bin = pack( 'H*', $f->{hex} );
my $decoded = $codec->decode($bin);
is ref $decoded, 'HASH', "Decoded $f->{name} into hash";
ok exists $decoded->{cid_raw}, 'Has cid_raw key';
}
};
# DAG-PB
subtest 'DAG-PB Fixtures' => sub {
for my $f (@dagpb_fixtures) {
my $bin = pack( 'H*', $f->{hex} );
my $decoded = $codec->decode($bin);
ok $decoded, 'Decoded ' . $f->{name};
is normalize($decoded), normalize( $f->{data} ), 'Decoded data matches expected for ' . $f->{name} if defined $f->{data};
}
};
#
done_testing;
( run in 0.997 second using v1.01-cache-2.11-cpan-ecdf5575e8d )