Codec-CBOR

 view release on metacpan or  search on metacpan

lib/Codec/CBOR.pm  view on Meta::CPAN

            my $raw = $item->raw;
            return pack( 'C', 0xd8 ) . pack( 'C', 42 ) . $self->_encode_bytes( "\x00" . $raw );
        }
        die 'Codec::CBOR: Cannot encode ' . $ref;
    }

    method _encode_int ($val) {
        return $self->_encode_header( 0, $val ) if $val >= 0;
        $self->_encode_header( 1, -1 - $val );
    }

    method _encode_header ( $major, $val ) {
        return pack( 'C', ( $major << 5 ) | $val ) if $val < 24;
        return pack( 'CC', ( $major << 5 ) | 24, $val ) if $val < 256;
        return pack( 'Cn', ( $major << 5 ) | 25, $val ) if $val < 65536;
        return pack( 'CN', ( $major << 5 ) | 26, $val ) if $val < 4294967296;
        pack( 'CQ>', ( $major << 5 ) | 27, $val );
    }

    method _encode_utf8 ($str) {
        my $encoded = $str;
        utf8::encode($encoded) if utf8::is_utf8($encoded);
        $self->_encode_header( 3, length($encoded) ) . $encoded;
    }
    method _encode_bytes ($bytes) { $self->_encode_header( 2, length($bytes) ) . $bytes }

    method _encode_array ($arr) {
        my $out = $self->_encode_header( 4, scalar @$arr );
        $out .= $self->_encode_item($_) for @$arr;
        $out;
    }

    method _encode_hash ($hash) {    # DAG-CBOR deterministic sort: length first, then lexical
        my @keys = sort { length($a) <=> length($b) || $a cmp $b } keys %$hash;
        my $out  = $self->_encode_header( 5, scalar @keys );
        for my $k (@keys) {
            $out .= $self->_encode_utf8($k);
            $out .= $self->_encode_item( $hash->{$k} );
        }
        $out;
    }

    method _decode_item ($fh) {
        return undef unless defined $fh;
        return undef if eof($fh);
        read( $fh, my $byte, 1 ) or return undef;
        my $b     = ord($byte);
        my $major = $b >> 5;
        my $info  = $b & 0x1f;
        if ( $major == 0 ) { return $self->_decode_value( $info, $fh ); }
        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;
        }
        if ( $major == 5 ) {    # Map
            my $len = $self->_decode_value( $info, $fh );
            my %hash;
            for ( 1 .. $len ) {
                my $k = $self->_decode_item($fh);
                my $v = $self->_decode_item($fh);
                $hash{$k} = $v if defined $k;
            }
            return \%hash;
        }
        if ( $major == 6 ) {    # Tag
            my $tag = $self->_decode_value( $info, $fh );
            my $val = $self->_decode_item($fh);
            return $tag_handlers{$tag}->($val) if exists $tag_handlers{$tag};
            return $val;
        }
        if ( $major == 7 ) {    # Simple / Float
            return Codec::CBOR::Boolean->new(0) if $info == 20;
            return Codec::CBOR::Boolean->new(1) if $info == 21;
            return undef                        if $info == 22;
            if ( $info == 25 ) { read( $fh, my $b, 2 ); return unpack( 'f>', $b ); }
            if ( $info == 26 ) { read( $fh, my $b, 4 ); return unpack( 'f>', $b ); }
            if ( $info == 27 ) { read( $fh, my $b, 8 ); return unpack( 'd>', $b ); }
            return $self->_decode_value( $info, $fh );
        }
        die 'Codec::CBOR: Unsupported major type ' . $major;
    }

    method _decode_value ( $info, $fh ) {
        return $info if $info < 24;
        if ( $info == 24 ) { read( $fh, my $b, 1 ); return unpack( 'C',  $b ); }
        if ( $info == 25 ) { read( $fh, my $b, 2 ); return unpack( 'n',  $b ); }
        if ( $info == 26 ) { read( $fh, my $b, 4 ); return unpack( 'N',  $b ); }
        if ( $info == 27 ) { read( $fh, my $b, 8 ); return unpack( 'Q>', $b ); }
        die 'Codec::CBOR: Indefinite length or invalid info ' . $info;
    }
};
#
1;



( run in 0.330 second using v1.01-cache-2.11-cpan-7f9471e7e0a )