added perl support

This commit is contained in:
Tokuhiro Matsuno 2009-04-15 12:55:41 +09:00
parent 5bd53f018f
commit d449b1d20d
16 changed files with 7734 additions and 0 deletions

34
perl/Makefile.PL Normal file
View File

@ -0,0 +1,34 @@
use inc::Module::Install;
name 'Data-MessagePack';
all_from 'lib/Data/MessagePack.pm';
perl_version '5.008005';
license 'perl';
can_cc or die "This module requires a C compiler";
my $ccflags = '-I../ ';
makemaker_args(
OBJECT => '$(O_FILES)',
LIBS => [''],
CCFLAGS => $ccflags,
clean => {
FILES => q{
*.stackdump
*.gcov *.gcda *.gcno
*.out
nytprof
cover_db
},
},
);
tests 't/*.t';
author_tests('xt');
auto_set_repository;
build_requires 'Test::More';
use_test_base;
auto_include;
WriteAll;

39
perl/MessagePack.c Normal file
View File

@ -0,0 +1,39 @@
#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#ifdef __cplusplus
};
#endif
XS(xs_pack);
XS(xs_unpack);
XS(xs_unpacker_new);
XS(xs_unpacker_execute);
XS(xs_unpacker_execute_limit);
XS(xs_unpacker_is_finished);
XS(xs_unpacker_data);
XS(xs_unpacker_reset);
XS(boot_Data__MessagePack) {
dXSARGS;
HV * stash;
newXS("Data::MessagePack::pack", xs_pack, __FILE__);
newXS("Data::MessagePack::unpack", xs_unpack, __FILE__);
stash = gv_stashpvn("Data::MessagePack", strlen("Data::MessagePack"), TRUE);
newCONSTSUB(stash, "true", &PL_sv_yes);
newCONSTSUB(stash, "false", &PL_sv_no);
newXS("Data::MessagePack::Unpacker::new", xs_unpacker_new, __FILE__);
newXS("Data::MessagePack::Unpacker::execute", xs_unpacker_execute, __FILE__);
newXS("Data::MessagePack::Unpacker::execute_limit", xs_unpacker_execute_limit, __FILE__);
newXS("Data::MessagePack::Unpacker::is_finished", xs_unpacker_is_finished, __FILE__);
newXS("Data::MessagePack::Unpacker::data", xs_unpacker_data, __FILE__);
newXS("Data::MessagePack::Unpacker::reset", xs_unpacker_reset, __FILE__);
}

16
perl/benchmark/p1.pl Normal file
View File

@ -0,0 +1,16 @@
use strict;
use warnings;
use Data::MessagePack;
use JSON::XS;
use Benchmark ':all';
my $a = [0..2**24];
print "-- serialize\n";
cmpthese(
-1 => {
json => sub { JSON::XS::encode_json($a) },
mp => sub { Data::MessagePack->pack($a) },
}
);

View File

@ -0,0 +1,33 @@
package Data::MessagePack;
use strict;
use warnings;
use XSLoader;
our $VERSION = 0.01;
XSLoader::load(__PACKAGE__, $VERSION);
1;
__END__
=head1 NAME
Data::MessagePack - messagepack
=head1 SYNOPSIS
my $packed = Data::MessagePack->pack($dat);
my $unpacked = Data::MessagePack->unpack($dat);
=head1 DESCRIPTION
Data::MessagePack is a binary packer for perl.
=head1 AUTHORS
Tokuhiro Matsuno
=head1 SEE ALSO
L<http://msgpack.sourceforge.jp/>

View File

@ -0,0 +1,52 @@
=head1 NAME
Data::MessagePack::Unpacker - messagepack streaming deserializer
=head1 SYNOPSIS
use Data::Dumper;
my $up = Data::MessagePack::Unpacker->new;
my $ret = $up->execute($v, 0);
if ($ret != length($v)) {
fail "extra bytes";
}
return Dumper($up->data);
=head1 DESCRIPTION
This is an streaming deserializer for messagepack.
=head1 METHODS
=over 4
=item my $up = Data::MessagePack::Unpacker->new()
create new stream deserializer
=item $up->execute()
=item $up->execute_limit()
=item $up->is_finished()
is this deserializer finished?
=item $up->data()
returns deserialized object.
=item $up->reset()
reset the stream deserializer, without memory zone.
=back
=head1 AUTHORS
Tokuhiro Matsuno
=head1 SEE ALSO
L<Data::MessagePack>

122
perl/pack.c Normal file
View File

@ -0,0 +1,122 @@
#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#ifdef __cplusplus
};
#endif
#include "msgpack/pack_define.h"
#define msgpack_pack_inline_func(name) \
static inline void msgpack_pack ## name
#define msgpack_pack_inline_func_cint(name) \
static inline void msgpack_pack ## name
#define msgpack_pack_user SV*
#define msgpack_pack_append_buffer(user, buf, len) \
sv_catpvn(user, (const char*)(buf), len);
#include "msgpack/pack_template.h"
#define _PACK_WRAPPER(t) msgpack_pack_##t
#define PACK_WRAPPER(t) _PACK_WRAPPER(t)
// move to pack.c
static void _msgpack_pack_sv(SV* buf, SV* val) {
if (val==NULL) {
msgpack_pack_nil(buf);
return;
}
switch (SvTYPE(val)) {
case SVt_NULL:
msgpack_pack_nil(buf);
break;
case SVt_IV:
if (SvIOK_UV(val)) {
msgpack_pack_uint32(buf, SvUV(val));
} else {
PACK_WRAPPER(IVTYPE)(buf, SvIV(val));
}
break;
case SVt_PVNV:
{
STRLEN len = 0;
char *pv = SvPV(val, len);
if (len == 1 && *pv == '1') {
msgpack_pack_true(buf);
} else if (len == 0 && *pv==0) {
msgpack_pack_false(buf);
} else {
msgpack_pack_nil(buf);
}
}
break;
case SVt_PV:
{
STRLEN len;
char * cval = SvPV(val, len);
msgpack_pack_raw(buf, len);
msgpack_pack_raw_body(buf, cval, len);
}
break;
case SVt_NV:
PACK_WRAPPER(NVTYPE)(buf, SvNV(val));
break;
case SVt_PVAV:
{
AV* ary = (AV*)val;
int len = av_len(ary) + 1;
int i;
msgpack_pack_array(buf, len);
for (i=0; i<len; i++) {
SV** svp = av_fetch(ary, i, 0);
if (svp) {
_msgpack_pack_sv(buf, *svp);
} else {
msgpack_pack_nil(buf);
}
}
}
break;
case SVt_PVHV:
{
HV* hval = (HV*)val;
int count = hv_iterinit(hval);
HE* he;
msgpack_pack_map(buf, count);
while (he = hv_iternext(hval)) {
_msgpack_pack_sv(buf, hv_iterkeysv(he));
_msgpack_pack_sv(buf, HeVAL(he));
}
}
break;
case SVt_RV:
_msgpack_pack_sv(buf, SvRV(val));
break;
default:
sv_dump(val);
Perl_croak(aTHX_ "msgpack for perl doesn't supported this type: %d\n", SvTYPE(val));
}
}
XS(xs_pack) {
dXSARGS;
PERL_UNUSED_VAR(items);
SV* buf = newSVpv("", 0);
SV* val = ST(1);
_msgpack_pack_sv(buf, val);
ST(0) = buf;
XSRETURN(1);
}

6984
perl/ppport.h Normal file

File diff suppressed because it is too large Load Diff

6
perl/t/00_compile.t Normal file
View File

@ -0,0 +1,6 @@
use strict;
use warnings;
use Test::More tests => 1;
use_ok 'Data::MessagePack';

61
perl/t/01_pack.t Normal file
View File

@ -0,0 +1,61 @@
use t::Util;
use Test::More;
use Data::MessagePack;
sub packit {
local $_ = unpack("H*", Data::MessagePack->pack($_[0]));
s/(..)/$1 /g;
s/ $//;
$_;
}
sub pis ($$) {
is packit($_[0]), $_[1], 'dump ' . $_[1];
}
my @dat = (
0, '00',
1, '01',
127, '7f',
128, 'cc 80',
255, 'cc ff',
256, 'cd 01 00',
65535, 'cd ff ff',
65536, 'ce 00 01 00 00',
-1, 'ff',
-32, 'e0',
-33, 'd0 df',
-128, 'd0 80',
-129, 'd1 ff 7f',
-32768, 'd1 80 00',
-32769, 'd2 ff ff 7f ff',
1.0, 'cb 3f f0 00 00 00 00 00 00',
"", 'a0',
"a", 'a1 61',
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 'bf 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61',
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 'da 00 20 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61',
undef, 'c0',
Data::MessagePack::true(), 'c3',
Data::MessagePack::false(), 'c2',
[], '90',
[+[]], '91 90',
[[], undef], '92 90 c0',
{'a', 0}, '81 a1 61 00',
8388608, 'ce 00 80 00 00',
[undef, false, true], '93 c0 c2 c3',
["", "a", "bc", "def"], '94 a0 a1 61 a2 62 63 a3 64 65 66',
[[], [[undef]]], '92 90 91 91 c0',
[undef, false, true], '93 c0 c2 c3',
[[0, 64, 127], [-32, -16, -1]], '92 93 00 40 7f 93 e0 f0 ff',
[0, -128, -1, 0, -32768, -1, 0, -2147483648, -1], '99 00 d0 80 ff 00 d1 80 00 ff 00 d2 80 00 00 00 ff',
2147483648, 'ce 80 00 00 00',
-2147483648, 'd2 80 00 00 00',
);
@dat = (2147483648, 'ce 80 00 00 00');
plan tests => 1*(scalar(@dat)/2);
for (my $i=0; $i<scalar(@dat); ) {
pis $dat[$i++], $dat[$i++];
}

24
perl/t/02_unpack.t Normal file
View File

@ -0,0 +1,24 @@
use Test::More;
use Data::MessagePack;
use t::Util;
no warnings 'uninitialized'; # i need this. i need this.
sub unpackit {
my $v = $_[0];
$v =~ s/ //g;
$v = pack 'H*', $v;
return Data::MessagePack->unpack($v);
}
sub pis ($$) {
is_deeply unpackit($_[0]), $_[1], 'dump ' . $_[0];
}
my @dat = do 't/data.pl';
plan tests => 1*(scalar(@dat)/2);
for (my $i=0; $i<scalar(@dat); ) {
pis $dat[$i++], $dat[$i++];
}

32
perl/t/03_stream_unpack.t Normal file
View File

@ -0,0 +1,32 @@
use t::Util;
use Test::More;
use Data::MessagePack;
no warnings 'uninitialized'; # i need this. i need this.
my $up = Data::MessagePack::Unpacker->new;
sub unpackit {
my $v = $_[0];
$v =~ s/ //g;
$v = pack 'H*', $v;
$up->reset;
my $ret = $up->execute($v, 0);
if ($ret != length($v)) {
fail "extra bytes";
}
return $up->data;
}
sub pis ($$) {
is_deeply unpackit($_[0]), $_[1], 'dump ' . $_[0];
}
my @dat = do 't/data.pl';
plan tests => 1*(scalar(@dat)/2) + 1;
isa_ok $up, 'Data::MessagePack::Unpacker';
for (my $i=0; $i<scalar(@dat); ) {
pis $dat[$i++], $dat[$i++];
}

24
perl/t/04_invert.t Normal file
View File

@ -0,0 +1,24 @@
use Test::More;
use Data::MessagePack;
use t::Util;
no warnings 'uninitialized'; # i need this. i need this.
sub invert {
return Data::MessagePack->unpack(
Data::MessagePack->pack($_[0]),
);
}
sub pis ($) {
is_deeply invert($_[0]), $_[0], 'dump ' . $_[0];
}
my @dat = do 't/data.pl';
plan tests => 1*(scalar(@dat)/2);
for (my $i=0; $i<scalar(@dat); ) {
$i++;
pis $dat[$i++];
}

20
perl/t/Util.pm Normal file
View File

@ -0,0 +1,20 @@
package t::Util;
use strict;
use warnings;
sub import {
my $pkg = caller(0);
strict->import;
warnings->import;
no strict 'refs';
*{"$pkg\::true"} = sub () {
Data::MessagePack::true()
};
*{"$pkg\::false"} = sub () {
Data::MessagePack::false()
};
}
1;

15
perl/t/data.pl Normal file
View File

@ -0,0 +1,15 @@
no warnings 'uninitialized'; # i need this. i need this.
(
'93 c0 c2 c3' => [undef, false, true],
'94 a0 a1 61 a2 62 63 a3 64 65 66', ["", "a", "bc", "def"],
'92 90 91 91 c0', [[], [[undef]]],
'93 c0 c2 c3', [undef, false, true],
'ce 80 00 00 00', 2147483648,
'99 cc 00 cc 80 cc ff cd 00 00 cd 80 00 cd ff ff ce 00 00 00 00 ce 80 00 00 00 ce ff ff ff ff', [0, 128, 255, 0, 32768, 65535, 0, 2147483648, 4294967295],
'92 93 00 40 7f 93 e0 f0 ff', [[0, 64, 127], [-32, -16, -1]],
'96 dc 00 00 dc 00 01 c0 dc 00 02 c2 c3 dd 00 00 00 00 dd 00 00 00 01 c0 dd 00 00 00 02 c2 c3', [[], [undef], [false, true], [], [undef], [false, true]],
'96 da 00 00 da 00 01 61 da 00 02 61 62 db 00 00 00 00 db 00 00 00 01 61 db 00 00 00 02 61 62', ["", "a", "ab", "", "a", "ab"],
'99 d0 00 d0 80 d0 ff d1 00 00 d1 80 00 d1 ff ff d2 00 00 00 00 d2 80 00 00 00 d2 ff ff ff ff', [0, -128, -1, 0, -32768, -1, 0, -2147483648, -1],
'82 c2 81 c0 c0 c3 81 c0 80', {false,{undef,undef}, true,{undef,{}}},
'96 de 00 00 de 00 01 c0 c2 de 00 02 c0 c2 c3 c2 df 00 00 00 00 df 00 00 00 01 c0 c2 df 00 00 00 02 c0 c2 c3 c2', [{}, {undef,false}, {true,false, undef,false}, {}, {undef,false}, {true,false, undef,false}],
)

268
perl/unpack.c Normal file
View File

@ -0,0 +1,268 @@
#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#ifdef __cplusplus
};
#endif
typedef struct {
int finished;
SV* source;
} unpack_user;
#include "msgpack/unpack_define.h"
#define msgpack_unpack_struct(name) \
struct template ## name
#define msgpack_unpack_func(ret, name) \
ret template ## name
#define msgpack_unpack_callback(name) \
template_callback ## name
#define msgpack_unpack_object SV*
#define msgpack_unpack_user unpack_user
struct template_context;
typedef struct template_context msgpack_unpack_t;
static void template_init(msgpack_unpack_t* u);
static SV* template_data(msgpack_unpack_t* u);
static int template_execute(msgpack_unpack_t* u,
const char* data, size_t len, size_t* off);
static inline SV* template_callback_root(unpack_user* u)
{ return &PL_sv_undef; }
static inline int template_callback_uint8(unpack_user* u, uint8_t d, SV** o)
{ *o = newSVuv(d); return 0; }
static inline int template_callback_uint16(unpack_user* u, uint16_t d, SV** o)
{ *o = newSVuv(d); return 0; }
static inline int template_callback_uint32(unpack_user* u, uint32_t d, SV** o)
{ *o = newSVuv(d); return 0; }
static inline int template_callback_uint64(unpack_user* u, uint64_t d, SV** o)
{ *o = newSVuv(d); return 0; }
static inline int template_callback_int8(unpack_user* u, int8_t d, SV** o)
{ *o = newSViv((long)d); return 0; }
static inline int template_callback_int16(unpack_user* u, int16_t d, SV** o)
{ *o = newSViv((long)d); return 0; }
static inline int template_callback_int32(unpack_user* u, int32_t d, SV** o)
{ *o = newSViv((long)d); return 0; }
static inline int template_callback_int64(unpack_user* u, int64_t d, SV** o)
{ *o = newSViv(d); return 0; }
static inline int template_callback_float(unpack_user* u, float d, SV** o)
{ *o = newSVnv(d); return 0; }
static inline int template_callback_double(unpack_user* u, double d, SV** o)
{ *o = newSVnv(d); return 0; }
static inline int template_callback_nil(unpack_user* u, SV** o)
{ *o = &PL_sv_undef; return 0; }
static inline int template_callback_true(unpack_user* u, SV** o)
{ *o = &PL_sv_yes; return 0; }
static inline int template_callback_false(unpack_user* u, SV** o)
{ *o = &PL_sv_no; return 0;}
static inline int template_callback_array(unpack_user* u, unsigned int n, SV** o)
{ AV* a = newAV(); *o = (SV*)newRV_noinc((SV*)a); av_extend(a, n); return 0; }
static inline int template_callback_array_item(unpack_user* u, SV** c, SV* o)
{ av_push((AV*)SvRV(*c), o); SvREFCNT_inc(o); return 0; } // FIXME set value directry RARRAY_PTR(obj)[RARRAY_LEN(obj)++]
static inline int template_callback_map(unpack_user* u, unsigned int n, SV** o)
{ HV * h = newHV(); *o = newRV_noinc((SV*)h); return 0; }
static inline int template_callback_map_item(unpack_user* u, SV** c, SV* k, SV* v)
{ hv_store_ent((HV*)SvRV(*c), k, v, 0); SvREFCNT_inc(v); return 0; }
static inline int template_callback_raw(unpack_user* u, const char* b, const char* p, unsigned int l, SV** o)
{ *o = (l == 0) ? newSVpv("", 0) : newSVpv(p, l); return 0; }
#define UNPACKER(from, name) \
msgpack_unpack_t *name; \
name = INT2PTR(msgpack_unpack_t*, SvROK((from)) ? SvIV(SvRV((from))) : SvIV((from))); \
if(name == NULL) { \
Perl_croak(aTHX_ "NULL found for " # name " when shouldn't be."); \
}
#include "msgpack/unpack_template.h"
SV* _msgpack_unpack(SV* data, int limit) {
msgpack_unpack_t mp;
unpack_user u = {0, &PL_sv_undef};
int ret;
size_t from = 0;
STRLEN dlen;
const char * dptr = SvPV_const(data, dlen);
template_init(&mp);
mp.user = u;
mp.user.source = data;
ret = template_execute(&mp, dptr, (size_t)dlen, &from);
mp.user.source = &PL_sv_undef;
if(ret < 0) {
Perl_croak(aTHX_ "parse error.");
} else if(ret == 0) {
Perl_croak(aTHX_ "insufficient bytes.");
} else {
if(from < dlen) {
Perl_croak(aTHX_ "extra bytes.");
}
return template_data(&mp);
}
}
XS(xs_unpack_limit) {
dXSARGS;
if (items != 3) {
Perl_croak(aTHX_ "Usage: Data::MessagePack->unpack('datadata', $limit)");
}
{
int limit = SvIV(ST(2));
ST(0) = _msgpack_unpack(ST(1), limit);
}
XSRETURN(1);
}
XS(xs_unpack) {
dXSARGS;
msgpack_unpack_t mp;
if (items != 2) {
Perl_croak(aTHX_ "Usage: Data::MessagePack->unpack('datadata')");
}
{
ST(0) = _msgpack_unpack(ST(1), sv_len(ST(1)));
}
XSRETURN(1);
}
/* ------------------------------ stream -- */
static void _reset(SV* self) {
UNPACKER(self, mp);
template_init(mp);
unpack_user u = {0, &PL_sv_undef};
mp->user = u;
}
XS(xs_unpacker_new) {
dXSARGS;
SV* self = sv_newmortal();
msgpack_unpack_t *mp;
Newx(mp, 1, msgpack_unpack_t);
sv_setref_pv(self, "Data::MessagePack::Unpacker", mp);
_reset(self);
ST(0) = self;
XSRETURN(1);
}
static SV* _execute_impl(SV* self, SV* data, UV off, I32 limit) {
UNPACKER(self, mp);
size_t from = off;
const char* dptr = SvPV_nolen_const(data);
long dlen = limit;
int ret;
if(from >= dlen) {
Perl_croak(aTHX_ "offset is bigger than data buffer size.");
}
mp->user.source = data;
ret = template_execute(mp, dptr, (size_t)dlen, &from);
mp->user.source = &PL_sv_undef;
if(ret < 0) {
Perl_croak(aTHX_ "parse error.");
} else if(ret > 0) {
mp->user.finished = 1;
return newSVuv(from);
} else {
mp->user.finished = 0;
return newSVuv(from);
}
}
XS(xs_unpacker_execute) {
dXSARGS;
SV* self = ST(0);
SV* data = ST(1);
IV off = SvIV(ST(2));
ST(0) = _execute_impl(self, data, off, sv_len(data));
XSRETURN(1);
}
XS(xs_unpacker_execute_limit) {
dXSARGS;
SV* self = ST(0);
SV* data = ST(1);
IV off = SvIV(ST(2));
IV limit = SvIV(ST(3));
ST(0) = _execute_impl(self, data, off, limit);
XSRETURN(1);
}
XS(xs_unpacker_is_finished) {
dXSARGS;
UNPACKER(ST(0), mp);
ST(0) = (mp->user.finished) ? &PL_sv_yes : &PL_sv_no;
XSRETURN(1);
}
XS(xs_unpacker_data) {
dXSARGS;
UNPACKER(ST(0), mp);
ST(0) = template_data(mp);
XSRETURN(1);
}
XS(xs_unpacker_reset) {
dXSARGS;
if (items != 1) {
Perl_croak(aTHX_ "Usage: $unpacker->reset()");
}
_reset(ST(0));
XSRETURN(0);
}

4
perl/xt/99_pod.t Normal file
View File

@ -0,0 +1,4 @@
use Test::More;
eval "use Test::Pod 1.00";
plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
all_pod_files_ok();