no
warnings
'numeric'
;
no
warnings
'portable'
;
use
constant
MAX_UINT64
=> 0xFFFF_FFFF_FFFF_FFFF;
use
constant
MAX_SINT64
=> 0x7FFF_FFFF_FFFF_FFFF;
use
constant
MIN_SINT64
=>-0x8000_0000_0000_0000;
sub
decode_varint {
my
$v
= 0;
my
$shift
= 0;
my
$l
=
length
(
$_
[0]);
while
(1) {
die
BROKEN_MESSAGE()
if
$_
[1] >=
$l
;
my
$b
=
ord
(
substr
(
$_
[0],
$_
[1]++, 1));
$v
+= ((
$b
& 0x7F) <<
$shift
);
$shift
+= 7;
last
if
(
$b
& 0x80)==0;
die
if
$shift
> 63;
}
return
$v
;
}
sub
encode_int {
if
(
$_
[1]>=0) {
encode_varint(
$_
[0],
$_
[1]);
}
else
{
encode_varint(
$_
[0], (MAX_UINT64+
$_
[1])+1);
}
}
sub
decode_int {
my
$v
= decode_varint(
@_
);
if
(
$v
>MAX_SINT64()) {
return
(
$v
-MAX_UINT64())-1;
}
else
{
return
$v
;
}
}
sub
encode_sint {
if
(
$_
[1]>=MAX_SINT64()) {
encode_varint(
$_
[0], Math::BigInt->new(
$_
[1])<<1);
}
elsif
(
$_
[1]<=MIN_SINT64) {
encode_varint(
$_
[0], ((-Math::BigInt->new(
$_
[1]))<<1)-1);
}
elsif
(
$_
[1]>=0) {
encode_varint(
$_
[0],
$_
[1]<<1);
}
else
{
encode_varint(
$_
[0], ((-
$_
[1])<<1)-1);
}
}
sub
encode_fixed64 {
$_
[0] .=
pack
(
'V'
,
$_
[1] & 0xFFFF_FFFF);
$_
[0] .=
pack
(
'V'
,
$_
[1] >> 32);
}
sub
decode_fixed64 {
die
BROKEN_MESSAGE()
if
$_
[1]+8 >
length
(
$_
[0]);
my
$a
=
unpack
(
'V'
,
substr
(
$_
[0],
$_
[1], 4));
my
$b
=
unpack
(
'V'
,
substr
(
$_
[0],
$_
[1]+4, 4));
$_
[1] += 8;
return
$a
| (
$b
<<32);
}
sub
encode_sfixed64 {
my
$v
= (
$_
[1]<0) ? (MAX_UINT64()+
$_
[1])+1 :
$_
[1];
$_
[0] .=
pack
(
'V'
,
$v
& 0xFFFF_FFFF);
$_
[0] .=
pack
(
'V'
,
$v
>> 32);
}
sub
decode_sfixed64 {
die
BROKEN_MESSAGE()
if
$_
[1]+8 >
length
(
$_
[0]);
my
$a
=
unpack
(
'V'
,
substr
(
$_
[0],
$_
[1], 4));
my
$b
=
unpack
(
'V'
,
substr
(
$_
[0],
$_
[1]+4, 4));
$_
[1] += 8;
$a
|=
$b
<<32;
if
(
$a
>MAX_SINT64()) {
return
(
$a
-MAX_UINT64())-1;
}
else
{
return
$a
;
}
}
1;