diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d740b181 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*.so +ruby/Makefile diff --git a/cpp/ChangeLog b/cpp/ChangeLog index 53a99910..71c7d5bf 100644 --- a/cpp/ChangeLog +++ b/cpp/ChangeLog @@ -1,4 +1,31 @@ +2010-08-29 version 0.5.4: + + * includes msgpack_vc2008.vcproj file in source package + * fixes type::fix_int types + +2010-08-27 version 0.5.3: + + * adds type::fix_{u,}int{8,16,32,64} types + * adds msgpack_pack_fix_{u,}int{8,16,32,64} functions + * adds packer::pack_fix_{u,}int{8,16,32,64} functions + * fixes include paths + +2010-07-14 version 0.5.2: + + * type::raw::str(), operator==, operator!=, operator< and operator> are now const + * generates version.h using AC_OUTPUT macro in ./configure + +2010-07-06 version 0.5.1: + + * Add msgpack_vrefbuffer_new and msgpack_vrefbuffer_free + * Add msgpack_sbuffer_new and msgpack_sbuffer_free + * Add msgpack_unpacker_next and msgpack_unpack_next + * msgpack::unpack returns void + * Add MSGPACK_VERSION{,_MAJOR,_MINOR} macros to check header version + * Add msgpack_version{,_major,_minor} functions to check library version + * ./configure supports --disable-cxx option not to build C++ API + 2010-04-29 version 0.5.0: * msgpack_object_type is changed. MSGPACK_OBJECT_NIL is now 0x00. diff --git a/cpp/Makefile.am b/cpp/Makefile.am index 871ff8c0..ecec1b56 100644 --- a/cpp/Makefile.am +++ b/cpp/Makefile.am @@ -6,13 +6,14 @@ DOC_FILES = \ NOTICE \ msgpack_vc8.vcproj \ msgpack_vc8.sln \ - msgpack_vc8.postbuild.bat + msgpack_vc2008.vcproj \ + msgpack_vc2008.sln \ + msgpack_vc.postbuild.bat EXTRA_DIST = \ $(DOC_FILES) doxygen: - ./preprocess ./preprocess clean cd src && $(MAKE) doxygen ./preprocess diff --git a/cpp/README.md b/cpp/README.md index 454ce1af..eac77935 100644 --- a/cpp/README.md +++ b/cpp/README.md @@ -13,9 +13,10 @@ On UNIX-like platform, run ./configure && make && sudo make install: $ make $ sudo make install -On Windows, open msgpack_vc8.vcproj file and build it using batch build. DLLs are built on lib folder, and the headers are built on include folder. +On Windows, open msgpack_vc8.vcproj or msgpack_vc2008 file and build it using batch build. DLLs are built on lib folder, +and the headers are built on include folder. -To use the library in your program, include msgpack.hpp header and link msgpack and msgpackc library. +To use the library in your program, include msgpack.hpp header and link "msgpack" library. ## Example @@ -34,15 +35,9 @@ To use the library in your program, include msgpack.hpp header and link msgpack msgpack::pack(&buffer, target); // Deserialize the serialized data. - msgpack::zone mempool; // this manages the life of deserialized object - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(buffer.data, buffer.size, NULL, &mempool, &obj); - - if(ret != msgapck::UNPACK_SUCCESS) { - // error check - exit(1); - } + msgpack::unpacked msg; // includes memory pool and deserialized object + msgpack::unpack(&msg, sbuf.data(), sbuf.size()); + msgpack::object obj = msg.get(); // Print the deserialized object to stdout. std::cout << obj << std::endl; // ["Hello," "World!"] @@ -55,24 +50,24 @@ To use the library in your program, include msgpack.hpp header and link msgpack obj.as(); // type is mismatched, msgpack::type_error is thrown } -API document and other example codes are available at the [wiki.](http://msgpack.sourceforge.net/start) +API documents and other example codes are available at the [wiki.](http://redmine.msgpack.org/projects/msgpack/wiki) ## License -Copyright (C) 2008-2010 FURUHASHI Sadayuki - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Copyright (C) 2008-2010 FURUHASHI Sadayuki + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. See also NOTICE file. diff --git a/cpp/configure.in b/cpp/configure.in index 0895be4b..2dd92d18 100644 --- a/cpp/configure.in +++ b/cpp/configure.in @@ -1,6 +1,6 @@ AC_INIT(src/object.cpp) AC_CONFIG_AUX_DIR(ac) -AM_INIT_AUTOMAKE(msgpack, 0.5.0) +AM_INIT_AUTOMAKE(msgpack, 0.5.4) AC_CONFIG_HEADER(config.h) AC_SUBST(CFLAGS) @@ -9,23 +9,31 @@ CFLAGS="-O4 -Wall $CFLAGS" AC_SUBST(CXXFLAGS) CXXFLAGS="-O4 -Wall $CXXFLAGS" + AC_PROG_CC -AC_PROG_CXX + + +AC_MSG_CHECKING([if C++ API is enabled]) +AC_ARG_ENABLE(cxx, + AS_HELP_STRING([--disable-cxx], + [don't build C++ API]) ) +AC_MSG_RESULT([$enable_cxx]) +if test "$enable_cxx" != "no"; then + AC_PROG_CXX + AM_PROG_CC_C_O +fi +AM_CONDITIONAL(ENABLE_CXX, test "$enable_cxx" != "no") + AC_PROG_LIBTOOL AM_PROG_AS -AM_PROG_CC_C_O - -AC_LANG_PUSH([C++]) -AC_CHECK_HEADERS(tr1/unordered_map) -AC_CHECK_HEADERS(tr1/unordered_set) -AC_LANG_POP([C++]) AC_MSG_CHECKING([if debug option is enabled]) AC_ARG_ENABLE(debug, AS_HELP_STRING([--disable-debug], - [disable assert macros and omit -g option.]) ) + [disable assert macros and omit -g option]) ) +AC_MSG_RESULT([$enable_debug]) if test "$enable_debug" != "no"; then CXXFLAGS="$CXXFLAGS -g" CFLAGS="$CFLAGS -g" @@ -33,7 +41,6 @@ else CXXFLAGS="$CXXFLAGS -DNDEBUG" CFLAGS="$CFLAGS -DNDEBUG" fi -AC_MSG_RESULT($enable_debug) AC_CACHE_CHECK([for __sync_* atomic operations], msgpack_cv_atomic_ops, [ @@ -54,5 +61,15 @@ add CFLAGS="--march=i686" and CXXFLAGS="-march=i686" options to ./configure as f ]) fi -AC_OUTPUT([Makefile src/Makefile test/Makefile]) + +major=`echo $VERSION | sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` +minor=`echo $VERSION | sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` +AC_SUBST(VERSION_MAJOR, $major) +AC_SUBST(VERSION_MINOR, $minor) + + +AC_OUTPUT([Makefile + src/Makefile + src/msgpack/version.h + test/Makefile]) diff --git a/cpp/msgpack_vc.postbuild.bat b/cpp/msgpack_vc.postbuild.bat new file mode 100644 index 00000000..33ff232b --- /dev/null +++ b/cpp/msgpack_vc.postbuild.bat @@ -0,0 +1,45 @@ +IF NOT EXIST include MKDIR include +IF NOT EXIST include\msgpack MKDIR include\msgpack +IF NOT EXIST include\msgpack\type MKDIR include\msgpack\type +IF NOT EXIST include\msgpack\type\tr1 MKDIR include\msgpack\type\tr1 +copy src\msgpack\pack_define.h include\msgpack\ +copy src\msgpack\pack_template.h include\msgpack\ +copy src\msgpack\unpack_define.h include\msgpack\ +copy src\msgpack\unpack_template.h include\msgpack\ +copy src\msgpack\sysdep.h include\msgpack\ +copy src\msgpack.h include\ +copy src\msgpack\sbuffer.h include\msgpack\ +copy src\msgpack\version.h include\msgpack\ +copy src\msgpack\vrefbuffer.h include\msgpack\ +copy src\msgpack\zbuffer.h include\msgpack\ +copy src\msgpack\pack.h include\msgpack\ +copy src\msgpack\unpack.h include\msgpack\ +copy src\msgpack\object.h include\msgpack\ +copy src\msgpack\zone.h include\msgpack\ +copy src\msgpack.hpp include\ +copy src\msgpack\sbuffer.hpp include\msgpack\ +copy src\msgpack\vrefbuffer.hpp include\msgpack\ +copy src\msgpack\zbuffer.hpp include\msgpack\ +copy src\msgpack\pack.hpp include\msgpack\ +copy src\msgpack\unpack.hpp include\msgpack\ +copy src\msgpack\object.hpp include\msgpack\ +copy src\msgpack\zone.hpp include\msgpack\ +copy src\msgpack\type.hpp include\msgpack\type\ +copy src\msgpack\type\bool.hpp include\msgpack\type\ +copy src\msgpack\type\deque.hpp include\msgpack\type\ +copy src\msgpack\type\fixint.hpp include\msgpack\type\ +copy src\msgpack\type\float.hpp include\msgpack\type\ +copy src\msgpack\type\int.hpp include\msgpack\type\ +copy src\msgpack\type\list.hpp include\msgpack\type\ +copy src\msgpack\type\map.hpp include\msgpack\type\ +copy src\msgpack\type\nil.hpp include\msgpack\type\ +copy src\msgpack\type\pair.hpp include\msgpack\type\ +copy src\msgpack\type\raw.hpp include\msgpack\type\ +copy src\msgpack\type\set.hpp include\msgpack\type\ +copy src\msgpack\type\string.hpp include\msgpack\type\ +copy src\msgpack\type\vector.hpp include\msgpack\type\ +copy src\msgpack\type\tuple.hpp include\msgpack\type\ +copy src\msgpack\type\define.hpp include\msgpack\type\ +copy src\msgpack\type\tr1\unordered_map.hpp include\msgpack\type\ +copy src\msgpack\type\tr1\unordered_set.hpp include\msgpack\type\ + diff --git a/cpp/msgpack_vc8.postbuild.bat b/cpp/msgpack_vc8.postbuild.bat deleted file mode 100644 index bae13f37..00000000 --- a/cpp/msgpack_vc8.postbuild.bat +++ /dev/null @@ -1,43 +0,0 @@ -IF NOT EXIST include MKDIR include -IF NOT EXIST include\msgpack MKDIR include\msgpack -IF NOT EXIST include\msgpack\type MKDIR include\msgpack\type -IF NOT EXIST include\msgpack\type\tr1 MKDIR include\msgpack\type\tr1 -copy msgpack\pack_define.h include\msgpack\ -copy msgpack\pack_template.h include\msgpack\ -copy msgpack\unpack_define.h include\msgpack\ -copy msgpack\unpack_template.h include\msgpack\ -copy msgpack\sysdep.h include\msgpack\ -copy msgpack.h include\ -copy msgpack\sbuffer.h include\msgpack\ -copy msgpack\vrefbuffer.h include\msgpack\ -copy msgpack\zbuffer.h include\msgpack\ -copy msgpack\pack.h include\msgpack\ -copy msgpack\unpack.h include\msgpack\ -copy msgpack\object.h include\msgpack\ -copy msgpack\zone.h include\msgpack\ -copy msgpack.hpp include\ -copy msgpack\sbuffer.hpp include\msgpack\ -copy msgpack\vrefbuffer.hpp include\msgpack\ -copy msgpack\zbuffer.hpp include\msgpack\ -copy msgpack\pack.hpp include\msgpack\ -copy msgpack\unpack.hpp include\msgpack\ -copy msgpack\object.hpp include\msgpack\ -copy msgpack\zone.hpp include\msgpack\ -copy msgpack\type.hpp include\msgpack\type\ -copy msgpack\type\bool.hpp include\msgpack\type\ -copy msgpack\type\float.hpp include\msgpack\type\ -copy msgpack\type\int.hpp include\msgpack\type\ -copy msgpack\type\list.hpp include\msgpack\type\ -copy msgpack\type\deque.hpp include\msgpack\type\ -copy msgpack\type\map.hpp include\msgpack\type\ -copy msgpack\type\nil.hpp include\msgpack\type\ -copy msgpack\type\pair.hpp include\msgpack\type\ -copy msgpack\type\raw.hpp include\msgpack\type\ -copy msgpack\type\set.hpp include\msgpack\type\ -copy msgpack\type\string.hpp include\msgpack\type\ -copy msgpack\type\vector.hpp include\msgpack\type\ -copy msgpack\type\tuple.hpp include\msgpack\type\ -copy msgpack\type\define.hpp include\msgpack\type\ -copy msgpack\type\tr1\unordered_map.hpp include\msgpack\type\ -copy msgpack\type\tr1\unordered_set.hpp include\msgpack\type\ - diff --git a/cpp/msgpack_vc8.vcproj b/cpp/msgpack_vc8.vcproj index 58047909..72d47b6b 100644 --- a/cpp/msgpack_vc8.vcproj +++ b/cpp/msgpack_vc8.vcproj @@ -28,7 +28,7 @@ + + + + + + + + @@ -247,23 +267,23 @@ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > diff --git a/cpp/preprocess b/cpp/preprocess index 1c169e98..e51c61cf 100755 --- a/cpp/preprocess +++ b/cpp/preprocess @@ -29,3 +29,6 @@ cp -f ../msgpack/unpack_template.h src/msgpack/ cp -f ../test/cases.mpac test/ cp -f ../test/cases_compact.mpac test/ +sed -e 's/8\.00/9.00/' < msgpack_vc8.vcproj > msgpack_vc2008.vcproj +sed -e 's/9\.00/10.00/' -e 's/msgpack_vc8/msgpack_vc2008/' < msgpack_vc8.sln > msgpack_vc2008.sln + diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am index 6b6457e6..0979d235 100644 --- a/cpp/src/Makefile.am +++ b/cpp/src/Makefile.am @@ -4,9 +4,14 @@ lib_LTLIBRARIES = libmsgpack.la libmsgpack_la_SOURCES = \ unpack.c \ objectc.c \ + version.c \ vrefbuffer.c \ - zone.c \ + zone.c + +if ENABLE_CXX +libmsgpack_la_SOURCES += \ object.cpp +endif # -version-info CURRENT:REVISION:AGE libmsgpack_la_LDFLAGS = -version-info 3:0:0 @@ -18,15 +23,12 @@ lib_LTLIBRARIES += libmsgpackc.la libmsgpackc_la_SOURCES = \ unpack.c \ objectc.c \ + version.c \ vrefbuffer.c \ zone.c libmsgpackc_la_LDFLAGS = -version-info 2:0:0 -# work around for duplicated file name -kumo_manager_CFLAGS = $(AM_CFLAGS) -kumo_manager_CXXFLAGS = $(AM_CXXFLAGS) - nobase_include_HEADERS = \ msgpack/pack_define.h \ @@ -36,12 +38,16 @@ nobase_include_HEADERS = \ msgpack/sysdep.h \ msgpack.h \ msgpack/sbuffer.h \ + msgpack/version.h \ msgpack/vrefbuffer.h \ msgpack/zbuffer.h \ msgpack/pack.h \ msgpack/unpack.h \ msgpack/object.h \ - msgpack/zone.h \ + msgpack/zone.h + +if ENABLE_CXX +nobase_include_HEADERS += \ msgpack.hpp \ msgpack/sbuffer.hpp \ msgpack/vrefbuffer.hpp \ @@ -52,10 +58,11 @@ nobase_include_HEADERS = \ msgpack/zone.hpp \ msgpack/type.hpp \ msgpack/type/bool.hpp \ + msgpack/type/deque.hpp \ msgpack/type/float.hpp \ + msgpack/type/fixint.hpp \ msgpack/type/int.hpp \ msgpack/type/list.hpp \ - msgpack/type/deque.hpp \ msgpack/type/map.hpp \ msgpack/type/nil.hpp \ msgpack/type/pair.hpp \ @@ -67,8 +74,10 @@ nobase_include_HEADERS = \ msgpack/type/define.hpp \ msgpack/type/tr1/unordered_map.hpp \ msgpack/type/tr1/unordered_set.hpp +endif EXTRA_DIST = \ + msgpack/version.h.in \ msgpack/zone.hpp.erb \ msgpack/type/define.hpp.erb \ msgpack/type/tuple.hpp.erb diff --git a/cpp/src/msgpack.h b/cpp/src/msgpack.h index 0cd8a199..08f27688 100644 --- a/cpp/src/msgpack.h +++ b/cpp/src/msgpack.h @@ -26,3 +26,5 @@ #include "msgpack/unpack.h" #include "msgpack/sbuffer.h" #include "msgpack/vrefbuffer.h" +#include "msgpack/version.h" + diff --git a/cpp/src/msgpack/object.h b/cpp/src/msgpack/object.h index cf0b4c18..baeeb9b2 100644 --- a/cpp/src/msgpack/object.h +++ b/cpp/src/msgpack/object.h @@ -18,7 +18,7 @@ #ifndef MSGPACK_OBJECT_H__ #define MSGPACK_OBJECT_H__ -#include "msgpack/zone.h" +#include "zone.h" #include #ifdef __cplusplus diff --git a/cpp/src/msgpack/object.hpp b/cpp/src/msgpack/object.hpp index f80a390d..96c026ef 100644 --- a/cpp/src/msgpack/object.hpp +++ b/cpp/src/msgpack/object.hpp @@ -18,9 +18,9 @@ #ifndef MSGPACK_OBJECT_HPP__ #define MSGPACK_OBJECT_HPP__ -#include "msgpack/object.h" -#include "msgpack/pack.hpp" -#include "msgpack/zone.hpp" +#include "object.h" +#include "pack.hpp" +#include "zone.hpp" #include #include #include diff --git a/cpp/src/msgpack/pack.h b/cpp/src/msgpack/pack.h index 1252895d..c1564963 100644 --- a/cpp/src/msgpack/pack.h +++ b/cpp/src/msgpack/pack.h @@ -18,8 +18,8 @@ #ifndef MSGPACK_PACK_H__ #define MSGPACK_PACK_H__ -#include "msgpack/pack_define.h" -#include "msgpack/object.h" +#include "pack_define.h" +#include "object.h" #include #ifdef __cplusplus @@ -70,6 +70,15 @@ static int msgpack_pack_int16(msgpack_packer* pk, int16_t d); static int msgpack_pack_int32(msgpack_packer* pk, int32_t d); static int msgpack_pack_int64(msgpack_packer* pk, int64_t d); +static int msgpack_pack_fix_uint8(msgpack_packer* pk, uint8_t d); +static int msgpack_pack_fix_uint16(msgpack_packer* pk, uint16_t d); +static int msgpack_pack_fix_uint32(msgpack_packer* pk, uint32_t d); +static int msgpack_pack_fix_uint64(msgpack_packer* pk, uint64_t d); +static int msgpack_pack_fix_int8(msgpack_packer* pk, int8_t d); +static int msgpack_pack_fix_int16(msgpack_packer* pk, int16_t d); +static int msgpack_pack_fix_int32(msgpack_packer* pk, int32_t d); +static int msgpack_pack_fix_int64(msgpack_packer* pk, int64_t d); + static int msgpack_pack_float(msgpack_packer* pk, float d); static int msgpack_pack_double(msgpack_packer* pk, double d); @@ -96,12 +105,18 @@ int msgpack_pack_object(msgpack_packer* pk, msgpack_object d); #define msgpack_pack_inline_func_cint(name) \ inline int msgpack_pack ## name +#define msgpack_pack_inline_func_cint(name) \ + inline int msgpack_pack ## name + +#define msgpack_pack_inline_func_fixint(name) \ + inline int msgpack_pack_fix ## name + #define msgpack_pack_user msgpack_packer* #define msgpack_pack_append_buffer(user, buf, len) \ return (*(user)->callback)((user)->data, (const char*)buf, len) -#include "msgpack/pack_template.h" +#include "pack_template.h" inline void msgpack_packer_init(msgpack_packer* pk, void* data, msgpack_packer_write callback) { diff --git a/cpp/src/msgpack/pack.hpp b/cpp/src/msgpack/pack.hpp index ee906900..0090b961 100644 --- a/cpp/src/msgpack/pack.hpp +++ b/cpp/src/msgpack/pack.hpp @@ -18,7 +18,7 @@ #ifndef MSGPACK_PACK_HPP__ #define MSGPACK_PACK_HPP__ -#include "msgpack/pack_define.h" +#include "pack_define.h" #include #include @@ -45,6 +45,15 @@ public: packer& pack_int32(int32_t d); packer& pack_int64(int64_t d); + packer& pack_fix_uint8(uint8_t d); + packer& pack_fix_uint16(uint16_t d); + packer& pack_fix_uint32(uint32_t d); + packer& pack_fix_uint64(uint64_t d); + packer& pack_fix_int8(int8_t d); + packer& pack_fix_int16(int16_t d); + packer& pack_fix_int32(int32_t d); + packer& pack_fix_int64(int64_t d); + packer& pack_short(short d); packer& pack_int(int d); packer& pack_long(long d); @@ -78,6 +87,15 @@ private: static void _pack_int32(Stream& x, int32_t d); static void _pack_int64(Stream& x, int64_t d); + static void _pack_fix_uint8(Stream& x, uint8_t d); + static void _pack_fix_uint16(Stream& x, uint16_t d); + static void _pack_fix_uint32(Stream& x, uint32_t d); + static void _pack_fix_uint64(Stream& x, uint64_t d); + static void _pack_fix_int8(Stream& x, int8_t d); + static void _pack_fix_int16(Stream& x, int16_t d); + static void _pack_fix_int32(Stream& x, int32_t d); + static void _pack_fix_int64(Stream& x, int64_t d); + static void _pack_short(Stream& x, short d); static void _pack_int(Stream& x, int d); static void _pack_long(Stream& x, long d); @@ -133,11 +151,15 @@ inline void pack(Stream& s, const T& v) template \ inline void packer::_pack ## name +#define msgpack_pack_inline_func_fixint(name) \ + template \ + inline void packer::_pack_fix ## name + #define msgpack_pack_user Stream& #define msgpack_pack_append_buffer append_buffer -#include "msgpack/pack_template.h" +#include "pack_template.h" template @@ -149,6 +171,7 @@ packer::packer(Stream& s) : m_stream(s) { } template packer::~packer() { } + template inline packer& packer::pack_uint8(uint8_t d) { _pack_uint8(m_stream, d); return *this; } @@ -182,6 +205,39 @@ inline packer& packer::pack_int64(int64_t d) { _pack_int64(m_stream, d); return *this;} +template +inline packer& packer::pack_fix_uint8(uint8_t d) +{ _pack_fix_uint8(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_uint16(uint16_t d) +{ _pack_fix_uint16(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_uint32(uint32_t d) +{ _pack_fix_uint32(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_uint64(uint64_t d) +{ _pack_fix_uint64(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_int8(int8_t d) +{ _pack_fix_int8(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_int16(int16_t d) +{ _pack_fix_int16(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_int32(int32_t d) +{ _pack_fix_int32(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_int64(int64_t d) +{ _pack_fix_int64(m_stream, d); return *this;} + + template inline packer& packer::pack_short(short d) { _pack_short(m_stream, d); return *this; } diff --git a/cpp/src/msgpack/sbuffer.hpp b/cpp/src/msgpack/sbuffer.hpp index e4a3f969..a9efc6db 100644 --- a/cpp/src/msgpack/sbuffer.hpp +++ b/cpp/src/msgpack/sbuffer.hpp @@ -18,7 +18,7 @@ #ifndef MSGPACK_SBUFFER_HPP__ #define MSGPACK_SBUFFER_HPP__ -#include "msgpack/sbuffer.h" +#include "sbuffer.h" #include namespace msgpack { diff --git a/cpp/src/msgpack/type.hpp b/cpp/src/msgpack/type.hpp index fafa6740..bca69bfd 100644 --- a/cpp/src/msgpack/type.hpp +++ b/cpp/src/msgpack/type.hpp @@ -1,15 +1,16 @@ -#include "msgpack/type/bool.hpp" -#include "msgpack/type/float.hpp" -#include "msgpack/type/int.hpp" -#include "msgpack/type/list.hpp" -#include "msgpack/type/deque.hpp" -#include "msgpack/type/map.hpp" -#include "msgpack/type/nil.hpp" -#include "msgpack/type/pair.hpp" -#include "msgpack/type/raw.hpp" -#include "msgpack/type/set.hpp" -#include "msgpack/type/string.hpp" -#include "msgpack/type/vector.hpp" -#include "msgpack/type/tuple.hpp" -#include "msgpack/type/define.hpp" +#include "type/bool.hpp" +#include "type/deque.hpp" +#include "type/fixint.hpp" +#include "type/float.hpp" +#include "type/int.hpp" +#include "type/list.hpp" +#include "type/map.hpp" +#include "type/nil.hpp" +#include "type/pair.hpp" +#include "type/raw.hpp" +#include "type/set.hpp" +#include "type/string.hpp" +#include "type/vector.hpp" +#include "type/tuple.hpp" +#include "type/define.hpp" diff --git a/cpp/src/msgpack/type/fixint.hpp b/cpp/src/msgpack/type/fixint.hpp new file mode 100644 index 00000000..ebe2696f --- /dev/null +++ b/cpp/src/msgpack/type/fixint.hpp @@ -0,0 +1,172 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2020 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_FIXINT_HPP__ +#define MSGPACK_TYPE_FIXINT_HPP__ + +#include "msgpack/object.hpp" +#include "msgpack/type/int.hpp" + +namespace msgpack { + +namespace type { + + +template +struct fix_int { + fix_int() : value(0) { } + fix_int(T value) : value(value) { } + + operator T() const { return value; } + + T get() const { return value; } + +private: + T value; +}; + + +typedef fix_int fix_uint8; +typedef fix_int fix_uint16; +typedef fix_int fix_uint32; +typedef fix_int fix_uint64; + +typedef fix_int fix_int8; +typedef fix_int fix_int16; +typedef fix_int fix_int32; +typedef fix_int fix_int64; + + +} // namespace type + + +inline type::fix_int8& operator>> (object o, type::fix_int8& v) + { v = type::detail::convert_integer(o); return v; } + +inline type::fix_int16& operator>> (object o, type::fix_int16& v) + { v = type::detail::convert_integer(o); return v; } + +inline type::fix_int32& operator>> (object o, type::fix_int32& v) + { v = type::detail::convert_integer(o); return v; } + +inline type::fix_int64& operator>> (object o, type::fix_int64& v) + { v = type::detail::convert_integer(o); return v; } + + +inline type::fix_uint8& operator>> (object o, type::fix_uint8& v) + { v = type::detail::convert_integer(o); return v; } + +inline type::fix_uint16& operator>> (object o, type::fix_uint16& v) + { v = type::detail::convert_integer(o); return v; } + +inline type::fix_uint32& operator>> (object o, type::fix_uint32& v) + { v = type::detail::convert_integer(o); return v; } + +inline type::fix_uint64& operator>> (object o, type::fix_uint64& v) + { v = type::detail::convert_integer(o); return v; } + + +template +inline packer& operator<< (packer& o, const type::fix_int8& v) + { o.pack_fix_int8(v); return o; } + +template +inline packer& operator<< (packer& o, const type::fix_int16& v) + { o.pack_fix_int16(v); return o; } + +template +inline packer& operator<< (packer& o, const type::fix_int32& v) + { o.pack_fix_int32(v); return o; } + +template +inline packer& operator<< (packer& o, const type::fix_int64& v) + { o.pack_fix_int64(v); return o; } + + +template +inline packer& operator<< (packer& o, const type::fix_uint8& v) + { o.pack_fix_uint8(v); return o; } + +template +inline packer& operator<< (packer& o, const type::fix_uint16& v) + { o.pack_fix_uint16(v); return o; } + +template +inline packer& operator<< (packer& o, const type::fix_uint32& v) + { o.pack_fix_uint32(v); return o; } + +template +inline packer& operator<< (packer& o, const type::fix_uint64& v) + { o.pack_fix_uint64(v); return o; } + + +inline void operator<< (object& o, type::fix_int8 v) + { v.get() < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v.get() : o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + +inline void operator<< (object& o, type::fix_int16 v) + { v.get() < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v.get() : o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + +inline void operator<< (object& o, type::fix_int32 v) + { v.get() < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v.get() : o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + +inline void operator<< (object& o, type::fix_int64 v) + { v.get() < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v.get() : o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + + +inline void operator<< (object& o, type::fix_uint8 v) + { o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + +inline void operator<< (object& o, type::fix_uint16 v) + { o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + +inline void operator<< (object& o, type::fix_uint32 v) + { o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + +inline void operator<< (object& o, type::fix_uint64 v) + { o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + + +inline void operator<< (object::with_zone& o, type::fix_int8 v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, type::fix_int16 v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, type::fix_int32 v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, type::fix_int64 v) + { static_cast(o) << v; } + + +inline void operator<< (object::with_zone& o, type::fix_uint8 v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, type::fix_uint16 v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, type::fix_uint32 v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, type::fix_uint64 v) + { static_cast(o) << v; } + + +} // namespace msgpack + +#endif /* msgpack/type/fixint.hpp */ + diff --git a/cpp/src/msgpack/type/raw.hpp b/cpp/src/msgpack/type/raw.hpp index 21d9a0d9..87d188f6 100644 --- a/cpp/src/msgpack/type/raw.hpp +++ b/cpp/src/msgpack/type/raw.hpp @@ -33,25 +33,25 @@ struct raw_ref { uint32_t size; const char* ptr; - std::string str() { return std::string(ptr, size); } + std::string str() const { return std::string(ptr, size); } - bool operator== (const raw_ref& x) + bool operator== (const raw_ref& x) const { return size == x.size && memcmp(ptr, x.ptr, size) == 0; } - bool operator!= (const raw_ref& x) + bool operator!= (const raw_ref& x) const { return !(*this != x); } - bool operator< (const raw_ref& x) + bool operator< (const raw_ref& x) const { if(size == x.size) { return memcmp(ptr, x.ptr, size) < 0; } else { return size < x.size; } } - bool operator> (const raw_ref& x) + bool operator> (const raw_ref& x) const { if(size == x.size) { return memcmp(ptr, x.ptr, size) > 0; } else { return size > x.size; } diff --git a/cpp/src/msgpack/type/tuple.hpp.erb b/cpp/src/msgpack/type/tuple.hpp.erb index 0d9ae91c..ebef8163 100644 --- a/cpp/src/msgpack/type/tuple.hpp.erb +++ b/cpp/src/msgpack/type/tuple.hpp.erb @@ -187,5 +187,20 @@ inline void operator<< ( } // namespace msgpack + +//inline std::ostream& operator<< (std::ostream& o, const msgpack::type::tuple<>& v) { +// return o << "[]"; +//} +//<%0.upto(GENERATION_LIMIT) {|i|%> +//template , typename A<%=j%><%}%>> +//inline std::ostream& operator<< (std::ostream& o, +// const msgpack::type::tuple, A<%=j%><%}%>>& v) { +// return o << "[" +// <%0.upto(i) {|j|%> +// <<<%if j != 0 then%> ", " <<<%end%> v.template get<<%=j%>>()<%}%> +// << "]"; +//} +//<%}%> + #endif /* msgpack/type/tuple.hpp */ diff --git a/cpp/src/msgpack/unpack.h b/cpp/src/msgpack/unpack.h index 82698fc2..bea7d922 100644 --- a/cpp/src/msgpack/unpack.h +++ b/cpp/src/msgpack/unpack.h @@ -18,8 +18,8 @@ #ifndef MSGPACK_UNPACKER_H__ #define MSGPACK_UNPACKER_H__ -#include "msgpack/zone.h" -#include "msgpack/object.h" +#include "zone.h" +#include "object.h" #include #ifdef __cplusplus diff --git a/cpp/src/msgpack/unpack.hpp b/cpp/src/msgpack/unpack.hpp index 7b45017a..51580b6e 100644 --- a/cpp/src/msgpack/unpack.hpp +++ b/cpp/src/msgpack/unpack.hpp @@ -18,9 +18,9 @@ #ifndef MSGPACK_UNPACK_HPP__ #define MSGPACK_UNPACK_HPP__ -#include "msgpack/unpack.h" -#include "msgpack/object.hpp" -#include "msgpack/zone.hpp" +#include "unpack.h" +#include "object.hpp" +#include "zone.hpp" #include #include diff --git a/cpp/src/msgpack/version.h.in b/cpp/src/msgpack/version.h.in new file mode 100644 index 00000000..f1feb331 --- /dev/null +++ b/cpp/src/msgpack/version.h.in @@ -0,0 +1,40 @@ +/* + * MessagePack for C version information + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MSGPACK_VERSION_H__ +#define MSGPACK_VERSION_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +const char* msgpack_version(void); +int msgpack_version_major(void); +int msgpack_version_minor(void); + +#define MSGPACK_VERSION "@VERSION@" +#define MSGPACK_VERSION_MAJOR @VERSION_MAJOR@ +#define MSGPACK_VERSION_MINOR @VERSION_MINOR@ + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/version.h */ + diff --git a/cpp/src/msgpack/vrefbuffer.h b/cpp/src/msgpack/vrefbuffer.h index 123499d1..0643927e 100644 --- a/cpp/src/msgpack/vrefbuffer.h +++ b/cpp/src/msgpack/vrefbuffer.h @@ -18,7 +18,7 @@ #ifndef MSGPACK_VREFBUFFER_H__ #define MSGPACK_VREFBUFFER_H__ -#include "msgpack/zone.h" +#include "zone.h" #include #ifndef _WIN32 diff --git a/cpp/src/msgpack/vrefbuffer.hpp b/cpp/src/msgpack/vrefbuffer.hpp index 7e0ffb23..82335277 100644 --- a/cpp/src/msgpack/vrefbuffer.hpp +++ b/cpp/src/msgpack/vrefbuffer.hpp @@ -18,7 +18,7 @@ #ifndef MSGPACK_VREFBUFFER_HPP__ #define MSGPACK_VREFBUFFER_HPP__ -#include "msgpack/vrefbuffer.h" +#include "vrefbuffer.h" #include namespace msgpack { diff --git a/cpp/src/msgpack/zbuffer.h b/cpp/src/msgpack/zbuffer.h index abb9c501..efdd3049 100644 --- a/cpp/src/msgpack/zbuffer.h +++ b/cpp/src/msgpack/zbuffer.h @@ -18,7 +18,7 @@ #ifndef MSGPACK_ZBUFFER_H__ #define MSGPACK_ZBUFFER_H__ -#include "msgpack/sysdep.h" +#include "sysdep.h" #include #include #include diff --git a/cpp/src/msgpack/zbuffer.hpp b/cpp/src/msgpack/zbuffer.hpp index 278f076e..08e6fd03 100644 --- a/cpp/src/msgpack/zbuffer.hpp +++ b/cpp/src/msgpack/zbuffer.hpp @@ -18,7 +18,7 @@ #ifndef MSGPACK_ZBUFFER_HPP__ #define MSGPACK_ZBUFFER_HPP__ -#include "msgpack/zbuffer.h" +#include "zbuffer.h" #include namespace msgpack { diff --git a/cpp/src/msgpack/zone.h b/cpp/src/msgpack/zone.h index 0e811dff..d8c60b68 100644 --- a/cpp/src/msgpack/zone.h +++ b/cpp/src/msgpack/zone.h @@ -18,7 +18,7 @@ #ifndef MSGPACK_ZONE_H__ #define MSGPACK_ZONE_H__ -#include "msgpack/sysdep.h" +#include "sysdep.h" #ifdef __cplusplus extern "C" { diff --git a/cpp/src/msgpack/zone.hpp.erb b/cpp/src/msgpack/zone.hpp.erb index 8e69aa44..1cef05ec 100644 --- a/cpp/src/msgpack/zone.hpp.erb +++ b/cpp/src/msgpack/zone.hpp.erb @@ -18,7 +18,7 @@ #ifndef MSGPACK_ZONE_HPP__ #define MSGPACK_ZONE_HPP__ -#include "msgpack/zone.h" +#include "zone.h" #include #include #include diff --git a/cpp/src/version.c b/cpp/src/version.c new file mode 100644 index 00000000..3d956f18 --- /dev/null +++ b/cpp/src/version.c @@ -0,0 +1,17 @@ +#include "msgpack.h" + +const char* msgpack_version(void) +{ + return MSGPACK_VERSION; +} + +int msgpack_version_major(void) +{ + return MSGPACK_VERSION_MAJOR; +} + +int msgpack_version_minor(void) +{ + return MSGPACK_VERSION_MINOR; +} + diff --git a/cpp/test/Makefile.am b/cpp/test/Makefile.am index 1f522150..5225f28b 100644 --- a/cpp/test/Makefile.am +++ b/cpp/test/Makefile.am @@ -13,6 +13,9 @@ check_PROGRAMS = \ convert \ buffer \ cases \ + fixint \ + fixint_c \ + version \ msgpackc_test \ msgpack_test @@ -37,6 +40,12 @@ buffer_LDADD = -lz cases_SOURCES = cases.cc +fixint_SOURCES = fixint.cc + +fixint_c_SOURCES = fixint_c.cc + +version_SOURCES = version.cc + msgpackc_test_SOURCES = msgpackc_test.cpp msgpack_test_SOURCES = msgpack_test.cpp diff --git a/cpp/test/cases.cc b/cpp/test/cases.cc index b408876c..eb1286c7 100644 --- a/cpp/test/cases.cc +++ b/cpp/test/cases.cc @@ -32,5 +32,7 @@ TEST(cases, format) EXPECT_TRUE( pac_compact.next(&result_compact) ); EXPECT_EQ(result_compact.get(), result.get()); } + + EXPECT_FALSE( pac_compact.next(&result) ); } diff --git a/cpp/test/fixint.cc b/cpp/test/fixint.cc new file mode 100644 index 00000000..63288a1b --- /dev/null +++ b/cpp/test/fixint.cc @@ -0,0 +1,55 @@ +#include +#include + +template +void check_size(size_t size) { + T v(0); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, v); + EXPECT_EQ(size, sbuf.size()); +} + +TEST(fixint, size) +{ + check_size(2); + check_size(3); + check_size(5); + check_size(9); + + check_size(2); + check_size(3); + check_size(5); + check_size(9); +} + + +template +void check_convert() { + T v1(-11); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, v1); + + msgpack::unpacked msg; + msgpack::unpack(&msg, sbuf.data(), sbuf.size()); + + T v2; + msg.get().convert(&v2); + + EXPECT_EQ(v1.get(), v2.get()); + + EXPECT_EQ(msg.get(), msgpack::object(T(v1.get()))); +} + +TEST(fixint, convert) +{ + check_convert(); + check_convert(); + check_convert(); + check_convert(); + + check_convert(); + check_convert(); + check_convert(); + check_convert(); +} + diff --git a/cpp/test/fixint_c.cc b/cpp/test/fixint_c.cc new file mode 100644 index 00000000..caa4d262 --- /dev/null +++ b/cpp/test/fixint_c.cc @@ -0,0 +1,32 @@ +#include +#include + +TEST(fixint, size) +{ + msgpack_sbuffer* sbuf = msgpack_sbuffer_new(); + msgpack_packer* pk = msgpack_packer_new(sbuf, msgpack_sbuffer_write); + + size_t sum = 0; + + EXPECT_EQ(0, msgpack_pack_fix_int8(pk, 0)); + EXPECT_EQ(sum+=2, sbuf->size); + EXPECT_EQ(0, msgpack_pack_fix_int16(pk, 0)); + EXPECT_EQ(sum+=3, sbuf->size); + EXPECT_EQ(0, msgpack_pack_fix_int32(pk, 0)); + EXPECT_EQ(sum+=5, sbuf->size); + EXPECT_EQ(0, msgpack_pack_fix_int64(pk, 0)); + EXPECT_EQ(sum+=9, sbuf->size); + + EXPECT_EQ(0, msgpack_pack_fix_uint8(pk, 0)); + EXPECT_EQ(sum+=2, sbuf->size); + EXPECT_EQ(0, msgpack_pack_fix_uint16(pk, 0)); + EXPECT_EQ(sum+=3, sbuf->size); + EXPECT_EQ(0, msgpack_pack_fix_uint32(pk, 0)); + EXPECT_EQ(sum+=5, sbuf->size); + EXPECT_EQ(0, msgpack_pack_fix_uint64(pk, 0)); + EXPECT_EQ(sum+=9, sbuf->size); + + msgpack_sbuffer_free(sbuf); + msgpack_packer_free(pk); +} + diff --git a/cpp/test/version.cc b/cpp/test/version.cc new file mode 100644 index 00000000..9357271e --- /dev/null +++ b/cpp/test/version.cc @@ -0,0 +1,13 @@ +#include +#include + +TEST(version, print) +{ + printf("MSGPACK_VERSION : %s\n", MSGPACK_VERSION); + printf("MSGPACK_VERSION_MAJOR : %d\n", MSGPACK_VERSION_MAJOR); + printf("MSGPACK_VERSION_MINOR : %d\n", MSGPACK_VERSION_MINOR); + printf("msgpack_version() : %s\n", msgpack_version()); + printf("msgpack_version_major() : %d\n", msgpack_version_major()); + printf("msgpack_version_minor() : %d\n", msgpack_version_minor()); +} + diff --git a/erlang/.gitignore b/erlang/.gitignore index 7d6d3247..0f7faad4 100644 --- a/erlang/.gitignore +++ b/erlang/.gitignore @@ -2,3 +2,4 @@ MANIFEST *.beam .omakedb* *.omc +*~ \ No newline at end of file diff --git a/erlang/OMakefile b/erlang/OMakefile index ee72f784..21079400 100644 --- a/erlang/OMakefile +++ b/erlang/OMakefile @@ -22,7 +22,7 @@ # Phony targets are scoped, so you probably want to declare them first. # -.PHONY: all clean test #install +.PHONY: all clean test edoc dialyzer #install ######################################################################## # Subdirectories. @@ -33,10 +33,19 @@ .DEFAULT: msgpack.beam msgpack.beam: msgpack.erl - erlc $< + erlc -Wall +debug_info $< + +msgpack.html: msgpack.erl + erl -noshell -run edoc_run file $< test: msgpack.beam - erl -s msgpack test -s init stop + erl -noshell -s msgpack test -s init stop + +edoc: msgpack.erl + erl -noshell -eval 'ok=edoc:files(["msgpack.erl"], [{dir, "edoc"}]).' -s init stop + +dialyzer: msgpack.erl + dialyzer --src $< clean: - -rm *.beam + -rm -f *.beam *.html diff --git a/erlang/README.md b/erlang/README.md index 50d446d6..8616d5ec 100644 --- a/erlang/README.md +++ b/erlang/README.md @@ -2,18 +2,8 @@ MessagePack for Erlang ====================== Binary-based efficient object serialization library. -## Status +see wiki ( http://redmine.msgpack.org/projects/msgpack/wiki/QuickStartErlang ) for details -still in development. +# Status -TODOs: - - decide string specification. - -## Installation - -## Example - -## License - - - - +0.1.0 released. diff --git a/erlang/edoc/.gitignore b/erlang/edoc/.gitignore new file mode 100644 index 00000000..97f4246c --- /dev/null +++ b/erlang/edoc/.gitignore @@ -0,0 +1,4 @@ +*.html +*.css +*.png +edoc-info diff --git a/erlang/msgpack.erl b/erlang/msgpack.erl index ca9769e6..8c85458e 100644 --- a/erlang/msgpack.erl +++ b/erlang/msgpack.erl @@ -15,318 +15,314 @@ %% See the License for the specific language governing permissions and %% limitations under the License. + +%% @doc MessagePack codec for Erlang. +%% +%% APIs are almost compatible with C API +%% except for buffering functions (both copying and zero-copying), which are unavailable. +%% +%% +%% +%% +%% +%% +%% +%% +%% +%% +%% +%%
Equivalence between Erlang and Msgpack type :
erlang msgpack
integer() pos_fixnum/neg_fixnum/uint8/uint16/uint32/uint64/int8/int16/int32/int64
float() float/double
nil nil
boolean() boolean
binary() fix_raw/raw16/raw32
list() fix_array/array16/array32
{proplist()} fix_map/map16/map32
+%% @end + -module(msgpack). -author('kuenishi+msgpack@gmail.com'). -%% tuples, atoms are not supported. lists, integers, double, and so on. -%% see http://msgpack.sourceforge.jp/spec for -%% supported formats. APIs are almost compatible -%% for C API (http://msgpack.sourceforge.jp/c:doc) -%% except buffering functions (both copying and zero-copying). --export([pack/1, unpack/1, unpack_all/1, test/0]). +-export([pack/1, unpack/1, unpack_all/1]). --include_lib("eunit/include/eunit.hrl"). - -% compile: -% erl> c(msgpack). -% erl> S = . -% erl> {S, <<>>} = msgpack:unpack( msgpack:pack(S) ). +% @type msgpack_term() = [msgpack_term()] +% | {[{msgpack_term(),msgpack_term()}]} +% | integer() | float() | binary(). +% Erlang representation of msgpack data. +-type msgpack_term() :: [msgpack_term()] + | {[{msgpack_term(),msgpack_term()}]} + | integer() | float() | binary(). --type reason() :: enomem. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% external APIs +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% @doc Encode an erlang term into an msgpack binary. +% Returns {error, {badarg, term()}} if the input is illegal. +% @spec pack(Term::msgpack_term()) -> binary() | {error, {badarg, term()}} +-spec pack(Term::msgpack_term()) -> binary() | {error, {badarg, term()}}. +pack(Term)-> + try + pack_(Term) + catch + throw:Exception -> + {error, Exception} + end. + +% @doc Decode an msgpack binary into an erlang term. +% It only decodes the first msgpack packet contained in the binary; the rest is returned as is. +% Returns {error, {badarg, term()}} if the input is corrupted. +% Returns {error, incomplete} if the input is not a full msgpack packet (caller should gather more data and try again). +% @spec unpack(Bin::binary()) -> {msgpack_term(), binary()} | {error, incomplete} | {error, {badarg, term()}} +-spec unpack(Bin::binary()) -> {msgpack_term(), binary()} | {error, incomplete} | {error, {badarg, term()}}. +unpack(Bin) when is_binary(Bin) -> + try + unpack_(Bin) + catch + throw:Exception -> + {error, Exception} + end; +unpack(Other) -> + {error, {badarg, Other}}. + +% @doc Decode an msgpack binary into an erlang terms. +% It only decodes ALL msgpack packets contained in the binary. No packets should not remain. +% Returns {error, {badarg, term()}} if the input is corrupted. +% Returns {error, incomplete} if the input is not a full msgpack packet (caller should gather more data and try again). +% @spec unpack_all(binary()) -> [msgpack_term()] | {error, incomplete} | {error, {badarg, term()}} +-spec unpack_all(binary()) -> [msgpack_term()] | {error, incomplete} | {error, {badarg, term()}}. +unpack_all(Data)-> + try + unpack_all_(Data) + catch + throw:Exception -> + {error, Exception} + end. +unpack_all_(Data)-> + case unpack_(Data) of + { Term, <<>> } -> + [Term]; + { Term, Binary } when is_binary(Binary) -> + [Term|unpack_all_(Binary)] + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% internal APIs +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% pack them all +-spec pack_(msgpack_term()) -> binary() | no_return(). +pack_(I) when is_integer(I) andalso I < 0 -> + pack_int_(I); +pack_(I) when is_integer(I) -> + pack_uint_(I); +pack_(F) when is_float(F) -> + pack_double(F); +pack_(nil) -> + << 16#C0:8 >>; +pack_(true) -> + << 16#C3:8 >>; +pack_(false) -> + << 16#C2:8 >>; +pack_(Bin) when is_binary(Bin) -> + pack_raw(Bin); +pack_(List) when is_list(List) -> + pack_array(List); +pack_({Map}) when is_list(Map) -> + pack_map(Map); +pack_(Other) -> + throw({badarg, Other}). + + +-spec pack_uint_(non_neg_integer()) -> binary(). % positive fixnum -pack_uint_(N) when is_integer( N ) , N < 128 -> +pack_uint_(N) when N < 128 -> << 2#0:1, N:7 >>; % uint 8 -pack_uint_( N ) when is_integer( N ) andalso N < 256 -> +pack_uint_(N) when N < 256 -> << 16#CC:8, N:8 >>; - % uint 16 -pack_uint_( N ) when is_integer( N ) andalso N < 65536 -> +pack_uint_(N) when N < 65536 -> << 16#CD:8, N:16/big-unsigned-integer-unit:1 >>; - % uint 32 -pack_uint_( N ) when is_integer( N ) andalso N < 16#FFFFFFFF-> +pack_uint_(N) when N < 16#FFFFFFFF-> << 16#CE:8, N:32/big-unsigned-integer-unit:1 >>; - % uint 64 -pack_uint_( N ) when is_integer( N )-> +pack_uint_(N) -> << 16#CF:8, N:64/big-unsigned-integer-unit:1 >>. +-spec pack_int_(integer()) -> binary(). % negative fixnum -pack_int_( N ) when is_integer( N ) , N >= -32-> +pack_int_(N) when N >= -32-> << 2#111:3, N:5 >>; % int 8 -pack_int_( N ) when is_integer( N ) , N >= -256 -> - << 16#D0:8, N:8 >>; +pack_int_(N) when N > -128 -> + << 16#D0:8, N:8/big-signed-integer-unit:1 >>; % int 16 -pack_int_( N ) when is_integer( N ), N >= -65536 -> +pack_int_(N) when N > -32768 -> << 16#D1:8, N:16/big-signed-integer-unit:1 >>; % int 32 -pack_int_( N ) when is_integer( N ), N >= -16#FFFFFFFF -> +pack_int_(N) when N > -16#FFFFFFFF -> << 16#D2:8, N:32/big-signed-integer-unit:1 >>; % int 64 -pack_int_( N ) when is_integer( N )-> +pack_int_(N) -> << 16#D3:8, N:64/big-signed-integer-unit:1 >>. -% nil -pack_nil()-> - << 16#C0:8 >>. -% pack_true / pack_false -pack_bool(true)-> << 16#C3:8 >>; -pack_bool(false)-> << 16#C2:8 >>. +-spec pack_double(float()) -> binary(). % float : erlang's float is always IEEE 754 64bit format. -%pack_float(F) when is_float(F)-> +% pack_float(F) when is_float(F)-> % << 16#CA:8, F:32/big-float-unit:1 >>. % pack_double(F). % double -pack_double(F) when is_float(F)-> +pack_double(F) -> << 16#CB:8, F:64/big-float-unit:1 >>. -power(N,0) when is_integer(N) -> 1; -power(N,D) when is_integer(N) and is_integer(D) -> N * power(N, D-1). +-spec pack_raw(binary()) -> binary(). % raw bytes -pack_raw(Bin) when is_binary(Bin)-> - MaxLen = power(2,16), +pack_raw(Bin) -> case byte_size(Bin) of Len when Len < 6-> << 2#101:3, Len:5, Bin/binary >>; - Len when Len < MaxLen -> + Len when Len < 16#10000 -> % 65536 << 16#DA:8, Len:16/big-unsigned-integer-unit:1, Bin/binary >>; Len -> << 16#DB:8, Len:32/big-unsigned-integer-unit:1, Bin/binary >> end. -% list / tuple -pack_array(L) when is_list(L)-> - MaxLen = power(2,16), + +-spec pack_array([msgpack_term()]) -> binary() | no_return(). +% list +pack_array(L) -> case length(L) of Len when Len < 16 -> - << 2#1001:4, Len:4/integer-unit:1, (pack_array_(L))/binary >>; - Len when Len < MaxLen -> - << 16#DC:8, Len:16/big-unsigned-integer-unit:1,(pack_array_(L))/binary >>; + << 2#1001:4, Len:4/integer-unit:1, (pack_array_(L, <<>>))/binary >>; + Len when Len < 16#10000 -> % 65536 + << 16#DC:8, Len:16/big-unsigned-integer-unit:1, (pack_array_(L, <<>>))/binary >>; Len -> - << 16#DD:8, Len:32/big-unsigned-integer-unit:1,(pack_array_(L))/binary >> + << 16#DD:8, Len:32/big-unsigned-integer-unit:1, (pack_array_(L, <<>>))/binary >> end. -pack_array_([])-> <<>>; -pack_array_([Head|Tail])-> - << (pack_object(Head))/binary, (pack_array_(Tail))/binary >>. -unpack_array_(<<>>, 0)-> []; -unpack_array_(Remain, 0) when is_binary(Remain)-> [Remain]; -unpack_array_(Bin, RestLen) when is_binary(Bin)-> - {Term, Rest} = unpack(Bin), - [Term|unpack_array_(Rest, RestLen-1)]. - -pack_map({dict,M})-> - MaxLen = power(2,16), - case dict:size(M) of +pack_array_([], Acc) -> Acc; +pack_array_([Head|Tail], Acc) -> + pack_array_(Tail, <>). + +% Users SHOULD NOT send too long list: this uses lists:reverse/1 +-spec unpack_array_(binary(), non_neg_integer(), [msgpack_term()]) -> {[msgpack_term()], binary()} | no_return(). +unpack_array_(Bin, 0, Acc) -> {lists:reverse(Acc), Bin}; +unpack_array_(Bin, Len, Acc) -> + {Term, Rest} = unpack_(Bin), + unpack_array_(Rest, Len-1, [Term|Acc]). + + +-spec pack_map(M::[{msgpack_term(),msgpack_term()}]) -> binary() | no_return(). +pack_map(M)-> + case length(M) of Len when Len < 16 -> - << 2#1001:4, Len:4/integer-unit:1, (pack_map_(dict:to_list(M))) >>; - Len when Len < MaxLen -> - << 16#DE:8, Len:16/big-unsigned-integer-unit:1, (pack_map_(dict:to_list(M))) >>; + << 2#1000:4, Len:4/integer-unit:1, (pack_map_(M, <<>>))/binary >>; + Len when Len < 16#10000 -> % 65536 + << 16#DE:8, Len:16/big-unsigned-integer-unit:1, (pack_map_(M, <<>>))/binary >>; Len -> - << 16#DF:8, Len:32/big-unsigned-integer-unit:1, (pack_map_(dict:to_list(M))) >> + << 16#DF:8, Len:32/big-unsigned-integer-unit:1, (pack_map_(M, <<>>))/binary >> end. -pack_map_([])-> <<>>; -pack_map_([{Key,Value}|Tail]) -> - << (pack_object(Key)),(pack_object(Value)),(pack_map_(Tail)) >>. +pack_map_([], Acc) -> Acc; +pack_map_([{Key,Value}|Tail], Acc) -> + pack_map_(Tail, << Acc/binary, (pack_(Key))/binary, (pack_(Value))/binary>>). -unpack_map_(<<>>, 0)-> []; -unpack_map_(Bin, 0) when is_binary(Bin)-> [Bin]; -unpack_map_(Bin, Len) when is_binary(Bin) and is_integer(Len) -> - { Key, Rest } = unpack(Bin), - { Value, Rest2 } = unpack(Rest), - [{Key,Value}|unpack_map_(Rest2,Len-1)]. +% Users SHOULD NOT send too long list: this uses lists:reverse/1 +-spec unpack_map_(binary(), non_neg_integer(), [{msgpack_term(), msgpack_term()}]) -> + {{[{msgpack_term(), msgpack_term()}]}, binary()} | no_return(). +unpack_map_(Bin, 0, Acc) -> {{lists:reverse(Acc)}, Bin}; +unpack_map_(Bin, Len, Acc) -> + {Key, Rest} = unpack_(Bin), + {Value, Rest2} = unpack_(Rest), + unpack_map_(Rest2, Len-1, [{Key,Value}|Acc]). -pack_object(O) when is_integer(O) andalso O < 0 -> - pack_int_(O); -pack_object(O) when is_integer(O) -> - pack_uint_(O); -pack_object(O) when is_float(O)-> - pack_double(O); -pack_object(nil) -> - pack_nil(); -pack_object(Bool) when is_atom(Bool) -> - pack_bool(Bool); -pack_object(Bin) when is_binary(Bin)-> - pack_raw(Bin); -pack_object(List) when is_list(List)-> - pack_array(List); -pack_object({dict, Map})-> - pack_map({dict, Map}); -pack_object(_) -> - undefined. +% unpack them all +-spec unpack_(Bin::binary()) -> {msgpack_term(), binary()} | no_return(). +unpack_(Bin) -> + case Bin of +% ATOMS + <<16#C0, Rest/binary>> -> {nil, Rest}; + <<16#C2, Rest/binary>> -> {false, Rest}; + <<16#C3, Rest/binary>> -> {true, Rest}; +% Floats + <<16#CA, V:32/float-unit:1, Rest/binary>> -> {V, Rest}; + <<16#CB, V:64/float-unit:1, Rest/binary>> -> {V, Rest}; +% Unsigned integers + <<16#CC, V:8/unsigned-integer, Rest/binary>> -> {V, Rest}; + <<16#CD, V:16/big-unsigned-integer-unit:1, Rest/binary>> -> {V, Rest}; + <<16#CE, V:32/big-unsigned-integer-unit:1, Rest/binary>> -> {V, Rest}; + <<16#CF, V:64/big-unsigned-integer-unit:1, Rest/binary>> -> {V, Rest}; +% Signed integers + <<16#D0, V:8/signed-integer, Rest/binary>> -> {V, Rest}; + <<16#D1, V:16/big-signed-integer-unit:1, Rest/binary>> -> {V, Rest}; + <<16#D2, V:32/big-signed-integer-unit:1, Rest/binary>> -> {V, Rest}; + <<16#D3, V:64/big-signed-integer-unit:1, Rest/binary>> -> {V, Rest}; +% Raw bytes + <<16#DA, L:16/unsigned-integer-unit:1, V:L/binary, Rest/binary>> -> {V, Rest}; + <<16#DB, L:32/unsigned-integer-unit:1, V:L/binary, Rest/binary>> -> {V, Rest}; +% Arrays + <<16#DC, L:16/big-unsigned-integer-unit:1, Rest/binary>> -> unpack_array_(Rest, L, []); + <<16#DD, L:32/big-unsigned-integer-unit:1, Rest/binary>> -> unpack_array_(Rest, L, []); +% Maps + <<16#DE, L:16/big-unsigned-integer-unit:1, Rest/binary>> -> unpack_map_(Rest, L, []); + <<16#DF, L:32/big-unsigned-integer-unit:1, Rest/binary>> -> unpack_map_(Rest, L, []); -pack(Obj)-> - pack_object(Obj). +% Tag-encoded lengths (kept last, for speed) + <<0:1, V:7, Rest/binary>> -> {V, Rest}; % positive int + <<2#111:3, V:5, Rest/binary>> -> {V - 2#100000, Rest}; % negative int + <<2#101:3, L:5, V:L/binary, Rest/binary>> -> {V, Rest}; % raw bytes + <<2#1001:4, L:4, Rest/binary>> -> unpack_array_(Rest, L, []); % array + <<2#1000:4, L:4, Rest/binary>> -> unpack_map_(Rest, L, []); % map - -% unpacking. -% if failed in decoding and not end, get more data -% and feed more Bin into this function. -% TODO: error case for imcomplete format when short for any type formats. --spec unpack( binary() )-> {term(), binary()} | {more, non_neg_integer()} | {error, reason()}. -unpack(Bin) when not is_binary(Bin)-> - {error, badard}; -unpack(Bin) when bit_size(Bin) >= 8 -> - << Flag:8/unsigned-integer, Payload/binary >> = Bin, - case Flag of - 16#C0 -> - {nil, Payload}; - 16#C2 -> - {false, Payload}; - 16#C3 -> - {true, Payload}; - - 16#CA -> % 32bit float - << Return:32/float-unit:1, Rest/binary >> = Payload, - {Return, Rest}; - 16#CB -> % 64bit float - << Return:64/float-unit:1, Rest/binary >> = Payload, - {Return, Rest}; - - 16#CC -> % uint 8 - << Int:8/unsigned-integer, Rest/binary >> = Payload, - {Int, Rest}; - 16#CD -> % uint 16 - << Int:16/big-unsigned-integer-unit:1, Rest/binary >> = Payload, - {Int, Rest}; - 16#CE -> - << Int:32/big-unsigned-integer-unit:1, Rest/binary >> = Payload, - {Int, Rest}; - 16#CF -> - << Int:64/big-unsigned-integer-unit:1, Rest/binary >> = Payload, - {Int, Rest}; - - 16#D0 -> % int 8 - << Int:8/big-signed-integer-unit:1, Rest/binary >> = Payload, - {Int, Rest}; - 16#D1 -> % int 16 - << Int:16/big-signed-integer-unit:1, Rest/binary >> = Payload, - {Int, Rest}; - 16#D2 -> % int 32 - << Int:32/big-signed-integer-unit:1, Rest/binary >> = Payload, - {Int, Rest}; - 16#D3 -> % int 64 - << Int:64/big-signed-integer-unit:1, Rest/binary >> = Payload, - {Int, Rest}; - 16#DA -> % raw 16 - << Len:16/unsigned-integer-unit:1, Rest/binary >> = Payload, - << Return:Len/binary, Remain/binary >> = Rest, - {Return, Remain}; - 16#DB -> % raw 32 - << Len:32/big-unsigned-integer-unit:1, Rest/binary >> = Payload, - << Return:Len/binary, Remain/binary >> = Rest, - {Return, Remain}; - 16#DC -> % array 16 - << Len:16/big-unsigned-integer-unit:1, Rest/binary >> = Payload, - Array=unpack_array_(Rest, Len), - case length(Array) of - Len -> {Array, <<>>}; - _ -> - {Return, RemainRest} = lists:split(Len, Array), - [Remain] = RemainRest, - {Return, Remain} - end; - 16#DD -> % array 32 - << Len:32/big-unsigned-integer-unit:1, Rest/binary >> = Payload, - Array=unpack_array_(Rest, Len), - case length(Array) of - Len -> {Array, <<>>}; - _ -> - {Return, RemainRest} = lists:split(Len, Array), - [Remain] = RemainRest, - {Return, Remain} - end; - 16#DE -> % map 16 - << Len:16/big-unsigned-integer-unit:1, Rest/binary >> = Payload, - Array=unpack_map_(Rest, Len), - case length(Array) of - Len -> { dict:from_list(Array), <<>>}; - _ -> - {Return, RemainRest} = lists:split(Len, Array), - [Remain] = RemainRest, - {dict:from_list(Return), Remain} - end; - 16#DF -> % map 32 - << Len:32/big-unsigned-integer-unit:1, Rest/binary >> = Payload, - Array=unpack_map_(Rest, Len), - case length(Array) of - Len -> { dict:from_list(Array), <<>>}; - _ -> - {Return, RemainRest} = lists:split(Len, Array), - [Remain] = RemainRest, - {dict:from_list(Return), Remain} - end; - - % positive fixnum - Code when Code >= 2#00000000, Code < 2#10000000-> - {Code, Payload}; - - % negative fixnum - Code when Code >= 2#11100000 -> - {(Code - 16#100), Payload}; - - Code when Code >= 2#10100000 , Code < 2#11000000 -> -% 101XXXXX for FixRaw - Len = Code rem 2#10100000, - << Return:Len/binary, Remain/binary >> = Payload, - {Return, Remain}; - - Code when Code >= 2#10010000 , Code < 2#10100000 -> -% 1001XXXX for FixArray - Len = Code rem 2#10010000, - Array=unpack_array_(Payload, Len), - case length(Array) of - Len -> { Array, <<>>}; - _ -> - {Return, RemainRest} = lists:split(Len, Array), - [Remain] = RemainRest, - {Return, Remain} - end; - - Code when Code >= 2#10000000 , Code < 2#10010000 -> -% 1000XXXX for FixMap - Len = Code rem 2#10000000, - Array=unpack_map_(Payload, Len), - case length(Array) of - Len -> { dict:from_list(Array), <<>>}; - _ -> - {Return, RemainRest} = lists:split(Len, Array), - [Remain] = RemainRest, - {dict:from_list(Return), Remain} - end; - - _Other -> - erlang:display(_Other), - {error, no_code_matches} - end; -unpack(_)-> % when bit_size(Bin) < 8 -> - {more, 8}. - -unpack_all(Data)-> - case unpack(Data) of - { Term, Binary } when bit_size(Binary) =:= 0 -> - [Term]; - { Term, Binary } when is_binary(Binary) -> - [Term|unpack_all(Binary)] +% Invalid data + <> when F==16#C1; + F==16#C4; F==16#C5; F==16#C6; F==16#C7; F==16#C8; F==16#C9; + F==16#D4; F==16#D5; F==16#D6; F==16#D7; F==16#D8; F==16#D9 -> + throw({badarg, <>}); +% Incomplete data (we've covered every complete/invalid case; anything left is incomplete) + _ -> + throw(incomplete) end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% unit tests +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-include_lib("eunit/include/eunit.hrl"). -ifdef(EUNIT). +compare_all([], [])-> ok; +compare_all([], R)-> {toomuchrhs, R}; +compare_all(L, [])-> {toomuchlhs, L}; +compare_all([LH|LTL], [RH|RTL]) -> + ?assertEqual(LH, RH), + compare_all(LTL, RTL). + +port_receive(Port) -> + port_receive(Port, <<>>). +port_receive(Port, Acc) -> + receive + {Port, {data, Data}} -> port_receive(Port, <>); + {Port, eof} -> Acc + after 1000 -> Acc + end. + +test_([]) -> 0; +test_([Term|Rest])-> + Pack = msgpack:pack(Term), + ?assertEqual({Term, <<>>}, msgpack:unpack( Pack )), + 1+test_(Rest). + test_data()-> - [0, 1, 2, 123, 512, 1230, 678908, 16#FFFFFFFFFF, + [true, false, nil, + 0, 1, 2, 123, 512, 1230, 678908, 16#FFFFFFFFFF, -1, -23, -512, -1230, -567898, -16#FFFFFFFFFF, 123.123, -234.4355, 1.0e-34, 1.0e64, [23, 234, 0.23], - "hogehoge", "243546rf7g68h798j", + <<"hogehoge">>, <<"243546rf7g68h798j", 0, 23, 255>>, <<"hoasfdafdas][">>, - [0,42,"sum", [1,2]], [1,42, nil, [3]] + [0,42, <<"sum">>, [1,2]], [1,42, nil, [3]], + -234, -40000, -16#10000000, -16#100000000, + 42 ]. basic_test()-> @@ -336,37 +332,64 @@ basic_test()-> port_test()-> Tests = test_data(), - {[Tests],<<>>} = msgpack:unpack(msgpack:pack([Tests])), - Port = open_port({spawn, "ruby ../test/crosslang.rb"}, [binary]), - true = port_command(Port, msgpack:pack(Tests) ), - %Port ! {self, {command, msgpack:pack(Tests)}}, ... not owner - receive - {Port, {data, Data}}-> {Tests, <<>>}=msgpack:unpack(Data) - after 1024-> ?assert(false) end, + ?assertEqual({[Tests],<<>>}, msgpack:unpack(msgpack:pack([Tests]))), + + Port = open_port({spawn, "ruby ../test/crosslang.rb"}, [binary, eof]), + true = port_command(Port, msgpack:pack(Tests)), + ?assertEqual({Tests, <<>>}, msgpack:unpack(port_receive(Port))), port_close(Port). +test_p(Len,Term,OrigBin,Len) -> + {Term, <<>>}=msgpack:unpack(OrigBin); +test_p(I,_,OrigBin,Len) when I < Len-> + <> = OrigBin, + ?assertEqual({error,incomplete}, msgpack:unpack(Bin)). + +partial_test()-> % error handling test. + Term = lists:seq(0, 45), + Bin=msgpack:pack(Term), + BinLen = byte_size(Bin), + [test_p(X, Term, Bin, BinLen) || X <- lists:seq(0,BinLen)]. + +long_test()-> + Longer = lists:seq(0, 655), + {Longer, <<>>} = msgpack:unpack(msgpack:pack(Longer)). + +map_test()-> + Ints = lists:seq(0, 65), + Map = {[ {X, X*2} || X <- Ints ] ++ [{<<"hage">>, 324}, {43542, [nil, true, false]}]}, + {Map2, <<>>} = msgpack:unpack(msgpack:pack(Map)), + ?assertEqual(Map, Map2), + ok. + unknown_test()-> + Port = open_port({spawn, "ruby testcase_generator.rb"}, [binary, eof]), Tests = [0, 1, 2, 123, 512, 1230, 678908, -1, -23, -512, -1230, -567898, -% "hogehoge", "243546rf7g68h798j", - 123.123 %-234.4355, 1.0e-34, 1.0e64, -% [23, 234, 0.23] -% [0,42,"sum", [1,2]], [1,42, nil, [3]] + <<"hogehoge">>, <<"243546rf7g68h798j">>, + 123.123, + -234.4355, 1.0e-34, 1.0e64, + [23, 234, 0.23], + [0,42,<<"sum">>, [1,2]], [1,42, nil, [3]], + {[{1,2},{<<"hoge">>,nil}]}, % map + -234, -50000, + 42 ], - Port = open_port({spawn, "ruby testcase_generator.rb"}, [binary]), - %Port ! {self, {command, msgpack:pack(Tests)}}, ... not owner - receive - {Port, {data, Data}}-> - Tests=msgpack:unpack_all(Data) -% io:format("~p~n", [Tests]) - after 1024-> ?assert(false) end, + ?assertEqual(ok, compare_all(Tests, msgpack:unpack_all(port_receive(Port)))), port_close(Port). -test_([]) -> 0; -test_([S|Rest])-> - Pack = msgpack:pack(S), -% io:format("testing: ~p => ~p~n", [S, Pack]), - {S, <<>>} = msgpack:unpack( Pack ), - 1+test_(Rest). +other_test()-> + ?assertEqual({error,incomplete},msgpack:unpack(<<>>)). + +benchmark_test()-> + Data=[test_data() || _ <- lists:seq(0, 10000)], + S=?debugTime(" serialize", msgpack:pack(Data)), + {Data,<<>>}=?debugTime("deserialize", msgpack:unpack(S)), + ?debugFmt("for ~p KB test data.", [byte_size(S) div 1024]). + +error_test()-> + ?assertEqual({error,{badarg, atom}}, msgpack:pack(atom)), + Term = {"hoge", "hage", atom}, + ?assertEqual({error,{badarg, Term}}, msgpack:pack(Term)). -endif. diff --git a/erlang/testcase_generator.rb b/erlang/testcase_generator.rb index a173790a..8445bdd5 100644 --- a/erlang/testcase_generator.rb +++ b/erlang/testcase_generator.rb @@ -39,10 +39,14 @@ end objs = [0, 1, 2, 123, 512, 1230, 678908, -1, -23, -512, -1230, -567898, -# "hogehoge", "243546rf7g68h798j", - 123.123, #-234.4355, 1.0e-34, 1.0e64, -# [23, 234, 0.23] -# [0,42,"sum", [1,2]], [1,42, nil, [3]] + "hogehoge", "243546rf7g68h798j", + 123.123, + -234.4355, 1.0e-34, 1.0e64, + [23, 234, 0.23], + [0,42,"sum", [1,2]], [1,42, nil, [3]], + { 1 => 2, "hoge" => nil }, + -234, -50000, + 42 ] begin objs.each do |obj| diff --git a/haskell/LICENSE b/haskell/LICENSE index 2de30f66..3cb4d8c8 100644 --- a/haskell/LICENSE +++ b/haskell/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009, Hideyuki Tanaka +Copyright (c) 2009-2010, Hideyuki Tanaka All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/haskell/cbits/msgpack.c b/haskell/cbits/msgpack.c deleted file mode 100644 index be445925..00000000 --- a/haskell/cbits/msgpack.c +++ /dev/null @@ -1,137 +0,0 @@ -#include - -void msgpack_sbuffer_init_wrap(msgpack_sbuffer* sbuf) -{ - msgpack_sbuffer_init(sbuf); -} - -void msgpack_sbuffer_destroy_wrap(msgpack_sbuffer* sbuf) -{ - msgpack_sbuffer_destroy(sbuf); -} - -int msgpack_sbuffer_write_wrap(void* data, const char* buf, unsigned int len) -{ - return msgpack_sbuffer_write(data, buf, len); -} - -msgpack_packer* msgpack_packer_new_wrap(void *data, msgpack_packer_write callback) -{ - return msgpack_packer_new(data, callback); -} - -void msgpack_packer_free_wrap(msgpack_packer* pk) -{ - msgpack_packer_free(pk); -} - -int msgpack_pack_uint8_wrap(msgpack_packer* pk, uint8_t d) -{ - return msgpack_pack_uint8(pk, d); -} - -int msgpack_pack_uint16_wrap(msgpack_packer* pk, uint16_t d) -{ - return msgpack_pack_uint16(pk, d); -} - -int msgpack_pack_uint32_wrap(msgpack_packer* pk, uint32_t d) -{ - return msgpack_pack_uint32(pk, d); -} - -int msgpack_pack_uint64_wrap(msgpack_packer* pk, uint64_t d) -{ - return msgpack_pack_uint64(pk, d); -} - -int msgpack_pack_int8_wrap(msgpack_packer* pk, int8_t d) -{ - return msgpack_pack_int8(pk, d); -} - -int msgpack_pack_int16_wrap(msgpack_packer* pk, int16_t d) -{ - return msgpack_pack_int16(pk, d); -} - -int msgpack_pack_int32_wrap(msgpack_packer* pk, int32_t d) -{ - return msgpack_pack_int32(pk, d); -} - -int msgpack_pack_int64_wrap(msgpack_packer* pk, int64_t d) -{ - return msgpack_pack_int64(pk, d); -} - -int msgpack_pack_double_wrap(msgpack_packer* pk, double d) -{ - return msgpack_pack_double(pk, d); -} - -int msgpack_pack_nil_wrap(msgpack_packer* pk) -{ - return msgpack_pack_nil(pk); -} - -int msgpack_pack_true_wrap(msgpack_packer* pk) -{ - return msgpack_pack_true(pk); -} - -int msgpack_pack_false_wrap(msgpack_packer* pk) -{ - return msgpack_pack_false(pk); -} - -int msgpack_pack_array_wrap(msgpack_packer* pk, unsigned int n) -{ - return msgpack_pack_array(pk, n); -} - -int msgpack_pack_map_wrap(msgpack_packer* pk, unsigned int n) -{ - return msgpack_pack_map(pk, n); -} - -int msgpack_pack_raw_wrap(msgpack_packer* pk, size_t l) -{ - return msgpack_pack_raw(pk, l); -} - -int msgpack_pack_raw_body_wrap(msgpack_packer* pk, const void *b, size_t l) -{ - return msgpack_pack_raw_body(pk, b, l); -} - -bool msgpack_unpacker_reserve_buffer_wrap(msgpack_unpacker *mpac, size_t size) -{ - return msgpack_unpacker_reserve_buffer(mpac, size); -} - -char *msgpack_unpacker_buffer_wrap(msgpack_unpacker *mpac) -{ - return msgpack_unpacker_buffer(mpac); -} - -size_t msgpack_unpacker_buffer_capacity_wrap(const msgpack_unpacker *mpac) -{ - return msgpack_unpacker_buffer_capacity(mpac); -} - -void msgpack_unpacker_buffer_consumed_wrap(msgpack_unpacker *mpac, size_t size) -{ - msgpack_unpacker_buffer_consumed(mpac, size); -} - -void msgpack_unpacker_data_wrap(msgpack_unpacker *mpac, msgpack_object *obj) -{ - *obj=msgpack_unpacker_data(mpac); -} - -size_t msgpack_unpacker_message_size_wrap(const msgpack_unpacker *mpac) -{ - return msgpack_unpacker_message_size(mpac); -} - diff --git a/haskell/msgpack.cabal b/haskell/msgpack.cabal index 82cdb525..98133a9e 100644 --- a/haskell/msgpack.cabal +++ b/haskell/msgpack.cabal @@ -1,32 +1,48 @@ -Name: msgpack -Version: 0.2.2 -License: BSD3 -License-File: LICENSE -Author: Hideyuki Tanaka -Maintainer: Hideyuki Tanaka -Category: Data -Synopsis: A Haskell binding to MessagePack +Name: msgpack +Version: 0.4.0.1 +Synopsis: A Haskell binding to MessagePack Description: - A Haskell binding to MessagePack -Homepage: http://github.com/tanakh/hsmsgpack -Stability: Experimental -Tested-with: GHC==6.10.4 -Cabal-Version: >=1.2 -Build-Type: Simple + A Haskell binding to MessagePack -library - build-depends: base>=4 && <5, mtl, bytestring - ghc-options: -O2 -Wall - hs-source-dirs: src - extra-libraries: msgpackc +License: BSD3 +License-File: LICENSE +Copyright: Copyright (c) 2009-2010, Hideyuki Tanaka +Category: Data +Author: Hideyuki Tanaka +Maintainer: Hideyuki Tanaka +Homepage: http://github.com/msgpack/msgpack +Stability: Experimental +Cabal-Version: >= 1.6 +Build-Type: Simple + +Extra-source-files: + test/Test.hs + test/UserData.hs + +Library + Build-depends: base >=4 && <5, + transformers >= 0.2.1 && < 0.2.2, + MonadCatchIO-transformers >= 0.2.2 && < 0.2.3, + bytestring >= 0.9 && < 0.10, + vector >= 0.6.0 && < 0.6.1, + iteratee >= 0.4 && < 0.5, + attoparsec >= 0.8.1 && < 0.8.2, + binary >= 0.5.0 && < 0.5.1, + data-binary-ieee754 >= 0.4 && < 0.5, + deepseq >= 1.1 && <1.2, + template-haskell >= 2.4 && < 2.5 + + Ghc-options: -Wall + Hs-source-dirs: src Exposed-modules: Data.MessagePack - Data.MessagePack.Base - Data.MessagePack.Class - Data.MessagePack.Feed - Data.MessagePack.Monad - Data.MessagePack.Stream + Data.MessagePack.Pack + Data.MessagePack.Unpack + Data.MessagePack.Object + Data.MessagePack.Iteratee + Data.MessagePack.Derive - C-Sources: - cbits/msgpack.c +Source-repository head + Type: git + Location: git://github.com/msgpack/msgpack.git diff --git a/haskell/src/Data/MessagePack.hs b/haskell/src/Data/MessagePack.hs index 2949e603..b71190d6 100644 --- a/haskell/src/Data/MessagePack.hs +++ b/haskell/src/Data/MessagePack.hs @@ -1,7 +1,7 @@ -------------------------------------------------------------------- -- | -- Module : Data.MessagePack --- Copyright : (c) Hideyuki Tanaka, 2009 +-- Copyright : (c) Hideyuki Tanaka, 2009-2010 -- License : BSD3 -- -- Maintainer: tanaka.hideyuki@gmail.com @@ -13,51 +13,91 @@ -------------------------------------------------------------------- module Data.MessagePack( - module Data.MessagePack.Base, - module Data.MessagePack.Class, - module Data.MessagePack.Feed, - module Data.MessagePack.Monad, - module Data.MessagePack.Stream, + module Data.MessagePack.Pack, + module Data.MessagePack.Unpack, + module Data.MessagePack.Object, + module Data.MessagePack.Iteratee, + module Data.MessagePack.Derive, - -- * Pack and Unpack - packb, - unpackb, + -- * Pack functions + packToString, + packToHandle, + packToHandle', + packToFile, + + -- * Unpack functions + unpackFromString, + unpackFromHandle, + unpackFromFile, + unpackFromStringI, + unpackFromHandleI, + unpackFromFileI, - -- * Pure version of Pack and Unpack - packb', - unpackb', ) where -import Data.ByteString (ByteString) -import System.IO.Unsafe +import qualified Control.Monad.CatchIO as CIO +import Control.Monad.IO.Class +import qualified Data.Attoparsec as A +import Data.Binary.Put +import qualified Data.ByteString as B +import qualified Data.ByteString.Lazy as L +import qualified Data.Iteratee as I +import System.IO -import Data.MessagePack.Base -import Data.MessagePack.Class -import Data.MessagePack.Feed -import Data.MessagePack.Monad -import Data.MessagePack.Stream +import Data.MessagePack.Pack +import Data.MessagePack.Unpack +import Data.MessagePack.Object +import Data.MessagePack.Iteratee +import Data.MessagePack.Derive --- | Pack Haskell data to MessagePack string. -packb :: OBJECT a => a -> IO ByteString -packb dat = do - sb <- newSimpleBuffer - pc <- newPacker sb - pack pc dat - simpleBufferData sb +bufferSize :: Int +bufferSize = 4 * 1024 --- | Unpack MessagePack string to Haskell data. -unpackb :: OBJECT a => ByteString -> IO (Result a) -unpackb bs = do - withZone $ \z -> do - r <- unpackObject z bs - return $ case r of - Left err -> Left (show err) - Right (_, dat) -> fromObject dat +-- | Pack to ByteString. +packToString :: Put -> L.ByteString +packToString = runPut --- | Pure version of 'packb'. -packb' :: OBJECT a => a -> ByteString -packb' dat = unsafePerformIO $ packb dat +-- | Pack to Handle +packToHandle :: Handle -> Put -> IO () +packToHandle h = L.hPutStr h . packToString --- | Pure version of 'unpackb'. -unpackb' :: OBJECT a => ByteString -> Result a -unpackb' bs = unsafePerformIO $ unpackb bs +-- | Pack to Handle and Flush Handle +packToHandle' :: Handle -> Put -> IO () +packToHandle' h p = packToHandle h p >> hFlush h + +-- | Pack to File +packToFile :: FilePath -> Put -> IO () +packToFile path = L.writeFile path . packToString + +-- | Unpack from ByteString +unpackFromString :: (Monad m, IsByteString s) => s -> A.Parser a -> m a +unpackFromString bs = + unpackFromStringI bs . parserToIteratee + +-- | Unpack from Handle +unpackFromHandle :: CIO.MonadCatchIO m => Handle -> A.Parser a -> m a +unpackFromHandle h = + unpackFromHandleI h .parserToIteratee + +-- | Unpack from File +unpackFromFile :: CIO.MonadCatchIO m => FilePath -> A.Parser a -> m a +unpackFromFile path = + unpackFromFileI path . parserToIteratee + +-- | Iteratee interface to unpack from ByteString +unpackFromStringI :: (Monad m, IsByteString s) => s -> I.Iteratee B.ByteString m a -> m a +unpackFromStringI bs = + I.run . I.joinIM . I.enumPure1Chunk (toBS bs) + +-- | Iteratee interface to unpack from Handle +unpackFromHandleI :: CIO.MonadCatchIO m => Handle -> I.Iteratee B.ByteString m a -> m a +unpackFromHandleI h = + I.run . I.joinIM . enumHandleNonBlocking bufferSize h + +-- | Iteratee interface to unpack from File +unpackFromFileI :: CIO.MonadCatchIO m => FilePath -> I.Iteratee B.ByteString m a -> m a +unpackFromFileI path p = + CIO.bracket + (liftIO $ openBinaryFile path ReadMode) + (liftIO . hClose) + (flip unpackFromHandleI p) diff --git a/haskell/src/Data/MessagePack/Base.hsc b/haskell/src/Data/MessagePack/Base.hsc deleted file mode 100644 index b6cdc287..00000000 --- a/haskell/src/Data/MessagePack/Base.hsc +++ /dev/null @@ -1,584 +0,0 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE ForeignFunctionInterface #-} - --------------------------------------------------------------------- --- | --- Module : Data.MessagePack.Base --- Copyright : (c) Hideyuki Tanaka, 2009 --- License : BSD3 --- --- Maintainer: tanaka.hideyuki@gmail.com --- Stability : experimental --- Portability: portable --- --- Low Level Interface to MessagePack C API --- --------------------------------------------------------------------- - -module Data.MessagePack.Base( - -- * Simple Buffer - SimpleBuffer, - newSimpleBuffer, - simpleBufferData, - - -- * Serializer - Packer, - newPacker, - - packU8, - packU16, - packU32, - packU64, - packS8, - packS16, - packS32, - packS64, - - packTrue, - packFalse, - - packInt, - packDouble, - packNil, - packBool, - - packArray, - packMap, - packRAW, - packRAWBody, - packRAW', - - -- * Stream Deserializer - Unpacker, - defaultInitialBufferSize, - newUnpacker, - unpackerReserveBuffer, - unpackerBuffer, - unpackerBufferCapacity, - unpackerBufferConsumed, - unpackerFeed, - unpackerExecute, - unpackerData, - unpackerReleaseZone, - unpackerResetZone, - unpackerReset, - unpackerMessageSize, - - -- * MessagePack Object - Object(..), - packObject, - - UnpackReturn(..), - unpackObject, - - -- * Memory Zone - Zone, - newZone, - freeZone, - withZone, - ) where - -import Control.Exception -import Control.Monad -import Data.ByteString (ByteString) -import qualified Data.ByteString as BS hiding (pack, unpack) -import Data.Int -import Data.Word -import Foreign.C -import Foreign.Concurrent -import Foreign.ForeignPtr hiding (newForeignPtr) -import Foreign.Marshal.Alloc -import Foreign.Marshal.Array -import Foreign.Ptr -import Foreign.Storable - -#include - -type SimpleBuffer = ForeignPtr () - -type WriteCallback = Ptr () -> CString -> CUInt -> IO CInt - --- | Create a new Simple Buffer. It will be deleted automatically. -newSimpleBuffer :: IO SimpleBuffer -newSimpleBuffer = do - ptr <- mallocBytes (#size msgpack_sbuffer) - fptr <- newForeignPtr ptr $ do - msgpack_sbuffer_destroy ptr - free ptr - withForeignPtr fptr $ \p -> - msgpack_sbuffer_init p - return fptr - --- | Get data of Simple Buffer. -simpleBufferData :: SimpleBuffer -> IO ByteString -simpleBufferData sb = - withForeignPtr sb $ \ptr -> do - size <- (#peek msgpack_sbuffer, size) ptr - dat <- (#peek msgpack_sbuffer, data) ptr - BS.packCStringLen (dat, fromIntegral (size :: CSize)) - -foreign import ccall "msgpack_sbuffer_init_wrap" msgpack_sbuffer_init :: - Ptr () -> IO () - -foreign import ccall "msgpack_sbuffer_destroy_wrap" msgpack_sbuffer_destroy :: - Ptr () -> IO () - -foreign import ccall "msgpack_sbuffer_write_wrap" msgpack_sbuffer_write :: - WriteCallback - -type Packer = ForeignPtr () - --- | Create new Packer. It will be deleted automatically. -newPacker :: SimpleBuffer -> IO Packer -newPacker sbuf = do - cb <- wrap_callback msgpack_sbuffer_write - ptr <- withForeignPtr sbuf $ \ptr -> - msgpack_packer_new ptr cb - fptr <- newForeignPtr ptr $ do - msgpack_packer_free ptr - return fptr - -foreign import ccall "msgpack_packer_new_wrap" msgpack_packer_new :: - Ptr () -> FunPtr WriteCallback -> IO (Ptr ()) - -foreign import ccall "msgpack_packer_free_wrap" msgpack_packer_free :: - Ptr () -> IO () - -foreign import ccall "wrapper" wrap_callback :: - WriteCallback -> IO (FunPtr WriteCallback) - -packU8 :: Packer -> Word8 -> IO Int -packU8 pc n = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_uint8 ptr n - -foreign import ccall "msgpack_pack_uint8_wrap" msgpack_pack_uint8 :: - Ptr () -> Word8 -> IO CInt - -packU16 :: Packer -> Word16 -> IO Int -packU16 pc n = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_uint16 ptr n - -foreign import ccall "msgpack_pack_uint16_wrap" msgpack_pack_uint16 :: - Ptr () -> Word16 -> IO CInt - -packU32 :: Packer -> Word32 -> IO Int -packU32 pc n = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_uint32 ptr n - -foreign import ccall "msgpack_pack_uint32_wrap" msgpack_pack_uint32 :: - Ptr () -> Word32 -> IO CInt - -packU64 :: Packer -> Word64 -> IO Int -packU64 pc n = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_uint64 ptr n - -foreign import ccall "msgpack_pack_uint64_wrap" msgpack_pack_uint64 :: - Ptr () -> Word64 -> IO CInt - -packS8 :: Packer -> Int8 -> IO Int -packS8 pc n = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_int8 ptr n - -foreign import ccall "msgpack_pack_int8_wrap" msgpack_pack_int8 :: - Ptr () -> Int8 -> IO CInt - -packS16 :: Packer -> Int16 -> IO Int -packS16 pc n = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_int16 ptr n - -foreign import ccall "msgpack_pack_int16_wrap" msgpack_pack_int16 :: - Ptr () -> Int16 -> IO CInt - -packS32 :: Packer -> Int32 -> IO Int -packS32 pc n = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_int32 ptr n - -foreign import ccall "msgpack_pack_int32_wrap" msgpack_pack_int32 :: - Ptr () -> Int32 -> IO CInt - -packS64 :: Packer -> Int64 -> IO Int -packS64 pc n = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_int64 ptr n - -foreign import ccall "msgpack_pack_int64_wrap" msgpack_pack_int64 :: - Ptr () -> Int64 -> IO CInt - --- | Pack an integral data. -packInt :: Integral a => Packer -> a -> IO Int -packInt pc n = packS64 pc $ fromIntegral n - --- | Pack a double data. -packDouble :: Packer -> Double -> IO Int -packDouble pc d = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_double ptr (realToFrac d) - -foreign import ccall "msgpack_pack_double_wrap" msgpack_pack_double :: - Ptr () -> CDouble -> IO CInt - --- | Pack a nil. -packNil :: Packer -> IO Int -packNil pc = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_nil ptr - -foreign import ccall "msgpack_pack_nil_wrap" msgpack_pack_nil :: - Ptr () -> IO CInt - -packTrue :: Packer -> IO Int -packTrue pc = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_true ptr - -foreign import ccall "msgpack_pack_true_wrap" msgpack_pack_true :: - Ptr () -> IO CInt - -packFalse :: Packer -> IO Int -packFalse pc = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_false ptr - -foreign import ccall "msgpack_pack_false_wrap" msgpack_pack_false :: - Ptr () -> IO CInt - --- | Pack a bool data. -packBool :: Packer -> Bool -> IO Int -packBool pc True = packTrue pc -packBool pc False = packFalse pc - --- | 'packArray' @p n@ starts packing an array. --- Next @n@ data will consist this array. -packArray :: Packer -> Int -> IO Int -packArray pc n = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_array ptr (fromIntegral n) - -foreign import ccall "msgpack_pack_array_wrap" msgpack_pack_array :: - Ptr () -> CUInt -> IO CInt - --- | 'packMap' @p n@ starts packing a map. --- Next @n@ pairs of data (2*n data) will consist this map. -packMap :: Packer -> Int -> IO Int -packMap pc n = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_map ptr (fromIntegral n) - -foreign import ccall "msgpack_pack_map_wrap" msgpack_pack_map :: - Ptr () -> CUInt -> IO CInt - --- | 'packRAW' @p n@ starts packing a byte sequence. --- Next total @n@ bytes of 'packRAWBody' call will consist this sequence. -packRAW :: Packer -> Int -> IO Int -packRAW pc n = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - msgpack_pack_raw ptr (fromIntegral n) - -foreign import ccall "msgpack_pack_raw_wrap" msgpack_pack_raw :: - Ptr () -> CSize -> IO CInt - --- | Pack a byte sequence. -packRAWBody :: Packer -> ByteString -> IO Int -packRAWBody pc bs = - liftM fromIntegral $ withForeignPtr pc $ \ptr -> - BS.useAsCStringLen bs $ \(str, len) -> - msgpack_pack_raw_body ptr (castPtr str) (fromIntegral len) - -foreign import ccall "msgpack_pack_raw_body_wrap" msgpack_pack_raw_body :: - Ptr () -> Ptr () -> CSize -> IO CInt - --- | Pack a single byte stream. It calls 'packRAW' and 'packRAWBody'. -packRAW' :: Packer -> ByteString -> IO Int -packRAW' pc bs = do - _ <- packRAW pc (BS.length bs) - packRAWBody pc bs - -type Unpacker = ForeignPtr () - -defaultInitialBufferSize :: Int -defaultInitialBufferSize = 32 * 1024 -- #const MSGPACK_UNPACKER_DEFAULT_INITIAL_BUFFER_SIZE - --- | 'newUnpacker' @initialBufferSize@ creates a new Unpacker. It will be deleted automatically. -newUnpacker :: Int -> IO Unpacker -newUnpacker initialBufferSize = do - ptr <- msgpack_unpacker_new (fromIntegral initialBufferSize) - fptr <- newForeignPtr ptr $ do - msgpack_unpacker_free ptr - return fptr - -foreign import ccall "msgpack_unpacker_new" msgpack_unpacker_new :: - CSize -> IO (Ptr ()) - -foreign import ccall "msgpack_unpacker_free" msgpack_unpacker_free :: - Ptr() -> IO () - --- | 'unpackerReserveBuffer' @up size@ reserves at least @size@ bytes of buffer. -unpackerReserveBuffer :: Unpacker -> Int -> IO Bool -unpackerReserveBuffer up size = - withForeignPtr up $ \ptr -> - liftM (/=0) $ msgpack_unpacker_reserve_buffer ptr (fromIntegral size) - -foreign import ccall "msgpack_unpacker_reserve_buffer_wrap" msgpack_unpacker_reserve_buffer :: - Ptr () -> CSize -> IO CChar - --- | Get a pointer of unpacker buffer. -unpackerBuffer :: Unpacker -> IO (Ptr CChar) -unpackerBuffer up = - withForeignPtr up $ \ptr -> - msgpack_unpacker_buffer ptr - -foreign import ccall "msgpack_unpacker_buffer_wrap" msgpack_unpacker_buffer :: - Ptr () -> IO (Ptr CChar) - --- | Get size of allocated buffer. -unpackerBufferCapacity :: Unpacker -> IO Int -unpackerBufferCapacity up = - withForeignPtr up $ \ptr -> - liftM fromIntegral $ msgpack_unpacker_buffer_capacity ptr - -foreign import ccall "msgpack_unpacker_buffer_capacity_wrap" msgpack_unpacker_buffer_capacity :: - Ptr () -> IO CSize - --- | 'unpackerBufferConsumed' @up size@ notices that writed @size@ bytes to buffer. -unpackerBufferConsumed :: Unpacker -> Int -> IO () -unpackerBufferConsumed up size = - withForeignPtr up $ \ptr -> - msgpack_unpacker_buffer_consumed ptr (fromIntegral size) - -foreign import ccall "msgpack_unpacker_buffer_consumed_wrap" msgpack_unpacker_buffer_consumed :: - Ptr () -> CSize -> IO () - --- | Write byte sequence to Unpacker. It is utility funciton, calls 'unpackerReserveBuffer', 'unpackerBuffer' and 'unpackerBufferConsumed'. -unpackerFeed :: Unpacker -> ByteString -> IO () -unpackerFeed up bs = - BS.useAsCStringLen bs $ \(str, len) -> do - True <- unpackerReserveBuffer up len - ptr <- unpackerBuffer up - copyArray ptr str len - unpackerBufferConsumed up len - --- | Execute deserializing. It returns 0 when buffer contains not enough bytes, returns 1 when succeeded, returns negative value when it failed. -unpackerExecute :: Unpacker -> IO Int -unpackerExecute up = - withForeignPtr up $ \ptr -> - liftM fromIntegral $ msgpack_unpacker_execute ptr - -foreign import ccall "msgpack_unpacker_execute" msgpack_unpacker_execute :: - Ptr () -> IO CInt - --- | Returns a deserialized object when 'unpackerExecute' returned 1. -unpackerData :: Unpacker -> IO Object -unpackerData up = - withForeignPtr up $ \ptr -> - allocaBytes (#size msgpack_object) $ \pobj -> do - msgpack_unpacker_data ptr pobj - peekObject pobj - -foreign import ccall "msgpack_unpacker_data_wrap" msgpack_unpacker_data :: - Ptr () -> Ptr () -> IO () - --- | Release memory zone. The returned zone must be freed by calling 'freeZone'. -unpackerReleaseZone :: Unpacker -> IO Zone -unpackerReleaseZone up = - withForeignPtr up $ \ptr -> - msgpack_unpacker_release_zone ptr - -foreign import ccall "msgpack_unpacker_release_zone" msgpack_unpacker_release_zone :: - Ptr () -> IO (Ptr ()) - --- | Free memory zone used by Unapcker. -unpackerResetZone :: Unpacker -> IO () -unpackerResetZone up = - withForeignPtr up $ \ptr -> - msgpack_unpacker_reset_zone ptr - -foreign import ccall "msgpack_unpacker_reset_zone" msgpack_unpacker_reset_zone :: - Ptr () -> IO () - --- | Reset Unpacker state except memory zone. -unpackerReset :: Unpacker -> IO () -unpackerReset up = - withForeignPtr up $ \ptr -> - msgpack_unpacker_reset ptr - -foreign import ccall "msgpack_unpacker_reset" msgpack_unpacker_reset :: - Ptr () -> IO () - --- | Returns number of bytes of sequence of deserializing object. -unpackerMessageSize :: Unpacker -> IO Int -unpackerMessageSize up = - withForeignPtr up $ \ptr -> - liftM fromIntegral $ msgpack_unpacker_message_size ptr - -foreign import ccall "msgpack_unpacker_message_size_wrap" msgpack_unpacker_message_size :: - Ptr () -> IO CSize - -type Zone = Ptr () - --- | Create a new memory zone. It must be freed manually. -newZone :: IO Zone -newZone = - msgpack_zone_new (#const MSGPACK_ZONE_CHUNK_SIZE) - --- | Free a memory zone. -freeZone :: Zone -> IO () -freeZone z = - msgpack_zone_free z - --- | Create a memory zone, then execute argument, then free memory zone. -withZone :: (Zone -> IO a) -> IO a -withZone z = - bracket newZone freeZone z - -foreign import ccall "msgpack_zone_new" msgpack_zone_new :: - CSize -> IO Zone - -foreign import ccall "msgpack_zone_free" msgpack_zone_free :: - Zone -> IO () - --- | Object Representation of MessagePack data. -data Object = - ObjectNil - | ObjectBool Bool - | ObjectInteger Int - | ObjectDouble Double - | ObjectRAW ByteString - | ObjectArray [Object] - | ObjectMap [(Object, Object)] - deriving (Show) - -peekObject :: Ptr a -> IO Object -peekObject ptr = do - typ <- (#peek msgpack_object, type) ptr - case (typ :: CInt) of - (#const MSGPACK_OBJECT_NIL) -> - return ObjectNil - (#const MSGPACK_OBJECT_BOOLEAN) -> - peekObjectBool ptr - (#const MSGPACK_OBJECT_POSITIVE_INTEGER) -> - peekObjectPositiveInteger ptr - (#const MSGPACK_OBJECT_NEGATIVE_INTEGER) -> - peekObjectNegativeInteger ptr - (#const MSGPACK_OBJECT_DOUBLE) -> - peekObjectDouble ptr - (#const MSGPACK_OBJECT_RAW) -> - peekObjectRAW ptr - (#const MSGPACK_OBJECT_ARRAY) -> - peekObjectArray ptr - (#const MSGPACK_OBJECT_MAP) -> - peekObjectMap ptr - _ -> - fail $ "peekObject: unknown object type (" ++ show typ ++ ")" - -peekObjectBool :: Ptr a -> IO Object -peekObjectBool ptr = do - b <- (#peek msgpack_object, via.boolean) ptr - return $ ObjectBool $ (b :: CUChar) /= 0 - -peekObjectPositiveInteger :: Ptr a -> IO Object -peekObjectPositiveInteger ptr = do - n <- (#peek msgpack_object, via.u64) ptr - return $ ObjectInteger $ fromIntegral (n :: Word64) - -peekObjectNegativeInteger :: Ptr a -> IO Object -peekObjectNegativeInteger ptr = do - n <- (#peek msgpack_object, via.i64) ptr - return $ ObjectInteger $ fromIntegral (n :: Int64) - -peekObjectDouble :: Ptr a -> IO Object -peekObjectDouble ptr = do - d <- (#peek msgpack_object, via.dec) ptr - return $ ObjectDouble $ realToFrac (d :: CDouble) - -peekObjectRAW :: Ptr a -> IO Object -peekObjectRAW ptr = do - size <- (#peek msgpack_object, via.raw.size) ptr - p <- (#peek msgpack_object, via.raw.ptr) ptr - bs <- BS.packCStringLen (p, fromIntegral (size :: Word32)) - return $ ObjectRAW bs - -peekObjectArray :: Ptr a -> IO Object -peekObjectArray ptr = do - csize <- (#peek msgpack_object, via.array.size) ptr - let size = fromIntegral (csize :: Word32) - p <- (#peek msgpack_object, via.array.ptr) ptr - objs <- mapM (\i -> peekObject $ p `plusPtr` - ((#size msgpack_object) * i)) - [0..size-1] - return $ ObjectArray objs - -peekObjectMap :: Ptr a -> IO Object -peekObjectMap ptr = do - csize <- (#peek msgpack_object, via.map.size) ptr - let size = fromIntegral (csize :: Word32) - p <- (#peek msgpack_object, via.map.ptr) ptr - dat <- mapM (\i -> peekObjectKV $ p `plusPtr` - ((#size msgpack_object_kv) * i)) - [0..size-1] - return $ ObjectMap dat - -peekObjectKV :: Ptr a -> IO (Object, Object) -peekObjectKV ptr = do - k <- peekObject $ ptr `plusPtr` (#offset msgpack_object_kv, key) - v <- peekObject $ ptr `plusPtr` (#offset msgpack_object_kv, val) - return (k, v) - --- | Pack a Object. -packObject :: Packer -> Object -> IO () -packObject pc ObjectNil = packNil pc >> return () - -packObject pc (ObjectBool b) = packBool pc b >> return () - -packObject pc (ObjectInteger n) = packInt pc n >> return () - -packObject pc (ObjectDouble d) = packDouble pc d >> return () - -packObject pc (ObjectRAW bs) = packRAW' pc bs >> return () - -packObject pc (ObjectArray ls) = do - _ <- packArray pc (length ls) - mapM_ (packObject pc) ls - -packObject pc (ObjectMap ls) = do - _ <- packMap pc (length ls) - mapM_ (\(a, b) -> packObject pc a >> packObject pc b) ls - -data UnpackReturn = - UnpackContinue -- ^ not enough bytes to unpack object - | UnpackParseError -- ^ got invalid bytes - | UnpackError -- ^ other error - deriving (Eq, Show) - --- | Unpack a single MessagePack object from byte sequence. -unpackObject :: Zone -> ByteString -> IO (Either UnpackReturn (Int, Object)) -unpackObject z dat = - allocaBytes (#size msgpack_object) $ \ptr -> - BS.useAsCStringLen dat $ \(str, len) -> - alloca $ \poff -> do - poke poff 0 - ret <- msgpack_unpack str (fromIntegral len) poff z ptr - case ret of - (#const MSGPACK_UNPACK_SUCCESS) -> do - off <- peek poff - obj <- peekObject ptr - return $ Right (fromIntegral off, obj) - (#const MSGPACK_UNPACK_EXTRA_BYTES) -> do - off <- peek poff - obj <- peekObject ptr - return $ Right (fromIntegral off, obj) - (#const MSGPACK_UNPACK_CONTINUE) -> - return $ Left UnpackContinue - (#const MSGPACK_UNPACK_PARSE_ERROR) -> - return $ Left UnpackParseError - _ -> - return $ Left UnpackError - -foreign import ccall "msgpack_unpack" msgpack_unpack :: - Ptr CChar -> CSize -> Ptr CSize -> Zone -> Ptr () -> IO CInt diff --git a/haskell/src/Data/MessagePack/Class.hs b/haskell/src/Data/MessagePack/Class.hs deleted file mode 100644 index 365acc5f..00000000 --- a/haskell/src/Data/MessagePack/Class.hs +++ /dev/null @@ -1,101 +0,0 @@ -{-# LANGUAGE TypeSynonymInstances #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE OverlappingInstances #-} -{-# LANGUAGE IncoherentInstances #-} - --------------------------------------------------------------------- --- | --- Module : Data.MessagePack.Class --- Copyright : (c) Hideyuki Tanaka, 2009 --- License : BSD3 --- --- Maintainer: tanaka.hideyuki@gmail.com --- Stability : experimental --- Portability: portable --- --- Serializing Haskell values to and from MessagePack Objects. --- --------------------------------------------------------------------- - -module Data.MessagePack.Class( - -- * Serialization to and from Object - OBJECT(..), - Result, - pack, - ) where - -import Control.Monad.Error -import Data.ByteString.Char8 (ByteString) -import qualified Data.ByteString.Char8 as C8 - -import Data.MessagePack.Base - --- | The class of types serializable to and from MessagePack object -class OBJECT a where - toObject :: a -> Object - fromObject :: Object -> Result a - --- | A type for parser results -type Result a = Either String a - -instance OBJECT Object where - toObject = id - fromObject = Right - -fromObjectError :: String -fromObjectError = "fromObject: cannot cast" - -instance OBJECT () where - toObject = const ObjectNil - fromObject ObjectNil = Right () - fromObject _ = Left fromObjectError - -instance OBJECT Int where - toObject = ObjectInteger - fromObject (ObjectInteger n) = Right n - fromObject _ = Left fromObjectError - -instance OBJECT Bool where - toObject = ObjectBool - fromObject (ObjectBool b) = Right b - fromObject _ = Left fromObjectError - -instance OBJECT Double where - toObject = ObjectDouble - fromObject (ObjectDouble d) = Right d - fromObject _ = Left fromObjectError - -instance OBJECT ByteString where - toObject = ObjectRAW - fromObject (ObjectRAW bs) = Right bs - fromObject _ = Left fromObjectError - -instance OBJECT String where - toObject = toObject . C8.pack - fromObject obj = liftM C8.unpack $ fromObject obj - -instance OBJECT a => OBJECT [a] where - toObject = ObjectArray . map toObject - fromObject (ObjectArray arr) = - mapM fromObject arr - fromObject _ = - Left fromObjectError - -instance (OBJECT a, OBJECT b) => OBJECT [(a, b)] where - toObject = - ObjectMap . map (\(a, b) -> (toObject a, toObject b)) - fromObject (ObjectMap mem) = do - mapM (\(a, b) -> liftM2 (,) (fromObject a) (fromObject b)) mem - fromObject _ = - Left fromObjectError - -instance OBJECT a => OBJECT (Maybe a) where - toObject (Just a) = toObject a - toObject Nothing = ObjectNil - - fromObject ObjectNil = return Nothing - fromObject obj = liftM Just $ fromObject obj - --- | Pack a serializable Haskell value. -pack :: OBJECT a => Packer -> a -> IO () -pack pc = packObject pc . toObject diff --git a/haskell/src/Data/MessagePack/Derive.hs b/haskell/src/Data/MessagePack/Derive.hs new file mode 100644 index 00000000..74943e9d --- /dev/null +++ b/haskell/src/Data/MessagePack/Derive.hs @@ -0,0 +1,106 @@ +{-# Language TemplateHaskell #-} + +module Data.MessagePack.Derive ( + derivePack, + deriveUnpack, + deriveObject, + ) where + +import Control.Applicative +import Control.Monad +import Language.Haskell.TH + +import Data.MessagePack.Pack +import Data.MessagePack.Unpack +import Data.MessagePack.Object + +deriveUnpack :: Name -> Q [Dec] +deriveUnpack typName = do + TyConI (DataD _ name _ cons _) <- reify typName + + return + [ InstanceD [] (AppT (ConT ''Unpackable) (ConT name)) + [ FunD 'get [Clause [] (NormalB $ ch $ map body cons) []] + ]] + + where + body (NormalC conName elms) = + DoE $ + tupOrListP (map VarP names) (VarE 'get) ++ + [ NoBindS $ AppE (VarE 'return) $ foldl AppE (ConE conName) $ map VarE names ] + where + names = zipWith (\ix _ -> mkName $ "a" ++ show (ix :: Int)) [1..] elms + + body (RecC conName elms) = + body (NormalC conName $ map (\(_, b, c) -> (b, c)) elms) + + ch = foldl1 (\e f -> AppE (AppE (VarE '(<|>)) e) f) + +derivePack :: Name -> Q [Dec] +derivePack typName = do + TyConI (DataD _ name _ cons _) <- reify typName + + return + [ InstanceD [] (AppT (ConT ''Packable) (ConT name)) + [ FunD 'put (map body cons) + ]] + + where + body (NormalC conName elms) = + Clause + [ ConP conName $ map VarP names ] + (NormalB $ AppE (VarE 'put) $ tupOrListE $ map VarE names) [] + where + names = zipWith (\ix _ -> mkName $ "a" ++ show (ix :: Int)) [1..] elms + + body (RecC conName elms) = + body (NormalC conName $ map (\(_, b, c) -> (b, c)) elms) + +deriveObject :: Name -> Q [Dec] +deriveObject typName = do + g <- derivePack typName + p <- deriveUnpack typName + + TyConI (DataD _ name _ cons _) <- reify typName + let o = InstanceD [] (AppT (ConT ''OBJECT) (ConT name)) + [ FunD 'toObject (map toObjectBody cons), + FunD 'tryFromObject [Clause [ VarP oname ] + (NormalB $ ch $ map tryFromObjectBody cons) []]] + + return $ g ++ p ++ [o] + where + toObjectBody (NormalC conName elms) = + Clause + [ ConP conName $ map VarP names ] + (NormalB $ AppE (VarE 'toObject) $ tupOrListE $ map VarE names) [] + where + names = zipWith (\ix _ -> mkName $ "a" ++ show (ix :: Int)) [1..] elms + toObjectBody (RecC conName elms) = + toObjectBody (NormalC conName $ map (\(_, b, c) -> (b, c)) elms) + + tryFromObjectBody (NormalC conName elms) = + DoE $ + tupOrListP (map VarP names) (AppE (VarE 'tryFromObject) (VarE oname)) ++ + [ NoBindS $ AppE (VarE 'return) $ foldl AppE (ConE conName) $ map VarE names ] + where + names = zipWith (\ix _ -> mkName $ "a" ++ show (ix :: Int)) [1..] elms + tryFromObjectBody (RecC conName elms) = + tryFromObjectBody (NormalC conName $ map (\(_, b, c) -> (b, c)) elms) + + oname = mkName "o" + ch = foldl1 (\e f -> AppE (AppE (VarE '(<|>)) e) f) + +tupOrListP :: [Pat] -> Exp -> [Stmt] +tupOrListP ls e + | length ls == 0 = + let lsname = mkName "ls" in + [ BindS (VarP lsname) e + , NoBindS $ AppE (VarE 'guard) $ AppE (VarE 'null) $ SigE (VarE lsname) (AppT ListT (ConT ''())) ] + | length ls == 1 = [ BindS (ListP ls) e ] + | otherwise = [ BindS (TupP ls) e ] + +tupOrListE :: [Exp] -> Exp +tupOrListE ls + | length ls == 0 = SigE (ListE []) (AppT ListT (ConT ''())) + | length ls == 1 = ListE ls + | otherwise = TupE ls diff --git a/haskell/src/Data/MessagePack/Feed.hs b/haskell/src/Data/MessagePack/Feed.hs deleted file mode 100644 index 4b486396..00000000 --- a/haskell/src/Data/MessagePack/Feed.hs +++ /dev/null @@ -1,62 +0,0 @@ --------------------------------------------------------------------- --- | --- Module : Data.MessagePack.Feed --- Copyright : (c) Hideyuki Tanaka, 2009 --- License : BSD3 --- --- Maintainer: tanaka.hideyuki@gmail.com --- Stability : experimental --- Portability: portable --- --- Feeders for Stream Deserializers --- --------------------------------------------------------------------- - -module Data.MessagePack.Feed( - -- * Feeder type - Feeder, - -- * Feeders - feederFromHandle, - feederFromFile, - feederFromString, - ) where - -import Data.ByteString (ByteString) -import qualified Data.ByteString as BS -import Data.IORef -import System.IO - --- | Feeder returns Just ByteString when bytes remains, otherwise Nothing. -type Feeder = IO (Maybe ByteString) - --- | Feeder from Handle -feederFromHandle :: Handle -> IO Feeder -feederFromHandle h = return $ do - bs <- BS.hGetNonBlocking h bufSize - if BS.length bs > 0 - then do return $ Just bs - else do - c <- BS.hGet h 1 - if BS.length c > 0 - then do return $ Just c - else do - hClose h - return Nothing - where - bufSize = 4096 - --- | Feeder from File -feederFromFile :: FilePath -> IO Feeder -feederFromFile path = - openFile path ReadMode >>= feederFromHandle - --- | Feeder from ByteString -feederFromString :: ByteString -> IO Feeder -feederFromString bs = do - r <- newIORef (Just bs) - return $ f r - where - f r = do - mb <- readIORef r - writeIORef r Nothing - return mb diff --git a/haskell/src/Data/MessagePack/Iteratee.hs b/haskell/src/Data/MessagePack/Iteratee.hs new file mode 100644 index 00000000..6bc08980 --- /dev/null +++ b/haskell/src/Data/MessagePack/Iteratee.hs @@ -0,0 +1,82 @@ +-------------------------------------------------------------------- +-- | +-- Module : Data.MessagePack.Iteratee +-- Copyright : (c) Hideyuki Tanaka, 2009-2010 +-- License : BSD3 +-- +-- Maintainer: tanaka.hideyuki@gmail.com +-- Stability : experimental +-- Portability: portable +-- +-- MessagePack Deserializer interface to @Data.Iteratee@ +-- +-------------------------------------------------------------------- + +module Data.MessagePack.Iteratee( + -- * Iteratee version of deserializer + getI, + -- * Non Blocking Enumerator + enumHandleNonBlocking, + -- * Convert Parser to Iteratee + parserToIteratee, + ) where + +import Control.Exception +import Control.Monad.IO.Class +import qualified Data.Attoparsec as A +import qualified Data.ByteString as B +import qualified Data.Iteratee as I +import System.IO + +import Data.MessagePack.Unpack + +-- | Deserialize a value +getI :: (Monad m, Unpackable a) => I.Iteratee B.ByteString m a +getI = parserToIteratee get + +-- | Enumerator +enumHandleNonBlocking :: MonadIO m => Int -> Handle -> I.Enumerator B.ByteString m a +enumHandleNonBlocking bufSize h = + I.enumFromCallback $ readSome bufSize h + +readSome :: MonadIO m => Int -> Handle -> m (Either SomeException (Bool, B.ByteString)) +readSome bufSize h = liftIO $ do + ebs <- try $ hGetSome bufSize h + case ebs of + Left exc -> + return $ Left (exc :: SomeException) + Right bs | B.null bs -> + return $ Right (False, B.empty) + Right bs -> + return $ Right (True, bs) + +hGetSome :: Int -> Handle -> IO B.ByteString +hGetSome bufSize h = do + bs <- B.hGetNonBlocking h bufSize + if B.null bs + then do + hd <- B.hGet h 1 + if B.null hd + then do + return B.empty + else do + rest <- B.hGetNonBlocking h (bufSize - 1) + return $ B.cons (B.head hd) rest + else do + return bs + +-- | Convert Parser to Iteratee +parserToIteratee :: Monad m => A.Parser a -> I.Iteratee B.ByteString m a +parserToIteratee p = I.icont (itr (A.parse p)) Nothing + where + itr pcont s = case s of + I.EOF _ -> + I.throwErr (I.setEOF s) + I.Chunk bs -> + case pcont bs of + A.Fail _ _ msg -> + I.throwErr (I.iterStrExc msg) + A.Partial cont -> + I.icont (itr cont) Nothing + A.Done remain ret -> + I.idone ret (I.Chunk remain) diff --git a/haskell/src/Data/MessagePack/Monad.hs b/haskell/src/Data/MessagePack/Monad.hs deleted file mode 100644 index 15f21fe0..00000000 --- a/haskell/src/Data/MessagePack/Monad.hs +++ /dev/null @@ -1,156 +0,0 @@ --------------------------------------------------------------------- --- | --- Module : Data.MessagePack.Monad --- Copyright : (c) Hideyuki Tanaka, 2009 --- License : BSD3 --- --- Maintainer: tanaka.hideyuki@gmail.com --- Stability : experimental --- Portability: portable --- --- Monadic Stream Serializers and Deserializers --- --------------------------------------------------------------------- - -module Data.MessagePack.Monad( - -- * Classes - MonadPacker(..), - MonadUnpacker(..), - - -- * Packer and Unpacker type - PackerT(..), - UnpackerT(..), - - -- * Packers - packToString, - packToHandle, - packToFile, - - -- * Unpackers - unpackFrom, - unpackFromString, - unpackFromHandle, - unpackFromFile, - ) where - -import Control.Monad -import Control.Monad.Trans -import Data.ByteString (ByteString) -import qualified Data.ByteString as BS -import System.IO - -import Data.MessagePack.Base hiding (Unpacker) -import qualified Data.MessagePack.Base as Base -import Data.MessagePack.Class -import Data.MessagePack.Feed - -class Monad m => MonadPacker m where - -- | Serialize a object - put :: OBJECT a => a -> m () - -class Monad m => MonadUnpacker m where - -- | Deserialize a object - get :: OBJECT a => m a - --- | Serializer Type -newtype PackerT m r = PackerT { runPackerT :: Base.Packer -> m r } - -instance Monad m => Monad (PackerT m) where - a >>= b = - PackerT $ \pc -> do - r <- runPackerT a pc - runPackerT (b r) pc - - return r = - PackerT $ \_ -> return r - -instance MonadTrans PackerT where - lift m = PackerT $ \_ -> m - -instance MonadIO m => MonadIO (PackerT m) where - liftIO = lift . liftIO - -instance MonadIO m => MonadPacker (PackerT m) where - put v = PackerT $ \pc -> liftIO $ do - pack pc v - --- | Execute given serializer and returns byte sequence. -packToString :: MonadIO m => PackerT m r -> m ByteString -packToString m = do - sb <- liftIO $ newSimpleBuffer - pc <- liftIO $ newPacker sb - _ <- runPackerT m pc - liftIO $ simpleBufferData sb - --- | Execute given serializer and write byte sequence to Handle. -packToHandle :: MonadIO m => Handle -> PackerT m r -> m () -packToHandle h m = do - sb <- packToString m - liftIO $ BS.hPut h sb - liftIO $ hFlush h - --- | Execute given serializer and write byte sequence to file. -packToFile :: MonadIO m => FilePath -> PackerT m r -> m () -packToFile p m = do - sb <- packToString m - liftIO $ BS.writeFile p sb - --- | Deserializer type -newtype UnpackerT m r = UnpackerT { runUnpackerT :: Base.Unpacker -> Feeder -> m r } - -instance Monad m => Monad (UnpackerT m) where - a >>= b = - UnpackerT $ \up feed -> do - r <- runUnpackerT a up feed - runUnpackerT (b r) up feed - - return r = - UnpackerT $ \_ _ -> return r - -instance MonadTrans UnpackerT where - lift m = UnpackerT $ \_ _ -> m - -instance MonadIO m => MonadIO (UnpackerT m) where - liftIO = lift . liftIO - -instance MonadIO m => MonadUnpacker (UnpackerT m) where - get = UnpackerT $ \up feed -> liftIO $ do - executeOne up feed - obj <- unpackerData up - freeZone =<< unpackerReleaseZone up - unpackerReset up - let Right r = fromObject obj - return r - - where - executeOne up feed = do - resp <- unpackerExecute up - guard $ resp>=0 - when (resp==0) $ do - Just bs <- feed - unpackerFeed up bs - executeOne up feed - --- | Execute deserializer using given feeder. -unpackFrom :: MonadIO m => Feeder -> UnpackerT m r -> m r -unpackFrom f m = do - up <- liftIO $ newUnpacker defaultInitialBufferSize - runUnpackerT m up f - --- | Execute deserializer using given handle. -unpackFromHandle :: MonadIO m => Handle -> UnpackerT m r -> m r -unpackFromHandle h m = - flip unpackFrom m =<< liftIO (feederFromHandle h) - --- | Execute deserializer using given file content. -unpackFromFile :: MonadIO m => FilePath -> UnpackerT m r -> m r -unpackFromFile p m = do - h <- liftIO $ openFile p ReadMode - r <- flip unpackFrom m =<< liftIO (feederFromHandle h) - liftIO $ hClose h - return r - --- | Execute deserializer from given byte sequence. -unpackFromString :: MonadIO m => ByteString -> UnpackerT m r -> m r -unpackFromString bs m = do - flip unpackFrom m =<< liftIO (feederFromString bs) diff --git a/haskell/src/Data/MessagePack/Object.hs b/haskell/src/Data/MessagePack/Object.hs new file mode 100644 index 00000000..5111ebb6 --- /dev/null +++ b/haskell/src/Data/MessagePack/Object.hs @@ -0,0 +1,301 @@ +{-# Language TypeSynonymInstances #-} +{-# Language FlexibleInstances #-} +{-# Language OverlappingInstances #-} +{-# Language IncoherentInstances #-} +{-# Language DeriveDataTypeable #-} + +-------------------------------------------------------------------- +-- | +-- Module : Data.MessagePack.Object +-- Copyright : (c) Hideyuki Tanaka, 2009-2010 +-- License : BSD3 +-- +-- Maintainer: tanaka.hideyuki@gmail.com +-- Stability : experimental +-- Portability: portable +-- +-- MessagePack object definition +-- +-------------------------------------------------------------------- + +module Data.MessagePack.Object( + -- * MessagePack Object + Object(..), + + -- * Serialization to and from Object + OBJECT(..), + -- Result, + ) where + +import Control.DeepSeq +import Control.Exception +import Control.Monad +import Control.Monad.Trans.Error () +import qualified Data.Attoparsec as A +import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as C8 +import Data.Typeable + +import Data.MessagePack.Pack +import Data.MessagePack.Unpack + +-- | Object Representation of MessagePack data. +data Object = + ObjectNil + | ObjectBool Bool + | ObjectInteger Int + | ObjectDouble Double + | ObjectRAW B.ByteString + | ObjectArray [Object] + | ObjectMap [(Object, Object)] + deriving (Show, Eq, Ord, Typeable) + +instance NFData Object where + rnf obj = + case obj of + ObjectNil -> () + ObjectBool b -> rnf b + ObjectInteger n -> rnf n + ObjectDouble d -> rnf d + ObjectRAW bs -> bs `seq` () + ObjectArray a -> rnf a + ObjectMap m -> rnf m + +instance Unpackable Object where + get = + A.choice + [ liftM ObjectInteger get + , liftM (\() -> ObjectNil) get + , liftM ObjectBool get + , liftM ObjectDouble get + , liftM ObjectRAW get + , liftM ObjectArray get + , liftM ObjectMap get + ] + +instance Packable Object where + put obj = + case obj of + ObjectInteger n -> + put n + ObjectNil -> + put () + ObjectBool b -> + put b + ObjectDouble d -> + put d + ObjectRAW raw -> + put raw + ObjectArray arr -> + put arr + ObjectMap m -> + put m + +-- | The class of types serializable to and from MessagePack object +class (Unpackable a, Packable a) => OBJECT a where + -- | Encode a value to MessagePack object + toObject :: a -> Object + toObject = unpack . pack + + -- | Decode a value from MessagePack object + fromObject :: Object -> a + fromObject a = + case tryFromObject a of + Left err -> + throw $ UnpackError err + Right ret -> + ret + + -- | Decode a value from MessagePack object + tryFromObject :: Object -> Either String a + tryFromObject = tryUnpack . pack + +instance OBJECT Object where + toObject = id + tryFromObject = Right + +tryFromObjectError :: Either String a +tryFromObjectError = Left "tryFromObject: cannot cast" + +instance OBJECT () where + toObject = const ObjectNil + tryFromObject ObjectNil = Right () + tryFromObject _ = tryFromObjectError + +instance OBJECT Int where + toObject = ObjectInteger + tryFromObject (ObjectInteger n) = Right n + tryFromObject _ = tryFromObjectError + +instance OBJECT Bool where + toObject = ObjectBool + tryFromObject (ObjectBool b) = Right b + tryFromObject _ = tryFromObjectError + +instance OBJECT Double where + toObject = ObjectDouble + tryFromObject (ObjectDouble d) = Right d + tryFromObject _ = tryFromObjectError + +instance OBJECT B.ByteString where + toObject = ObjectRAW + tryFromObject (ObjectRAW bs) = Right bs + tryFromObject _ = tryFromObjectError + +instance OBJECT String where + toObject = toObject . C8.pack + tryFromObject obj = liftM C8.unpack $ tryFromObject obj + +instance OBJECT a => OBJECT [a] where + toObject = ObjectArray . map toObject + tryFromObject (ObjectArray arr) = + mapM tryFromObject arr + tryFromObject _ = + tryFromObjectError + +instance (OBJECT a1, OBJECT a2) => OBJECT (a1, a2) where + toObject (a1, a2) = ObjectArray [toObject a1, toObject a2] + tryFromObject (ObjectArray arr) = + case arr of + [o1, o2] -> do + v1 <- tryFromObject o1 + v2 <- tryFromObject o2 + return (v1, v2) + _ -> + tryFromObjectError + tryFromObject _ = + tryFromObjectError + +instance (OBJECT a1, OBJECT a2, OBJECT a3) => OBJECT (a1, a2, a3) where + toObject (a1, a2, a3) = ObjectArray [toObject a1, toObject a2, toObject a3] + tryFromObject (ObjectArray arr) = + case arr of + [o1, o2, o3] -> do + v1 <- tryFromObject o1 + v2 <- tryFromObject o2 + v3 <- tryFromObject o3 + return (v1, v2, v3) + _ -> + tryFromObjectError + tryFromObject _ = + tryFromObjectError + +instance (OBJECT a1, OBJECT a2, OBJECT a3, OBJECT a4) => OBJECT (a1, a2, a3, a4) where + toObject (a1, a2, a3, a4) = ObjectArray [toObject a1, toObject a2, toObject a3, toObject a4] + tryFromObject (ObjectArray arr) = + case arr of + [o1, o2, o3, o4] -> do + v1 <- tryFromObject o1 + v2 <- tryFromObject o2 + v3 <- tryFromObject o3 + v4 <- tryFromObject o4 + return (v1, v2, v3, v4) + _ -> + tryFromObjectError + tryFromObject _ = + tryFromObjectError + +instance (OBJECT a1, OBJECT a2, OBJECT a3, OBJECT a4, OBJECT a5) => OBJECT (a1, a2, a3, a4, a5) where + toObject (a1, a2, a3, a4, a5) = ObjectArray [toObject a1, toObject a2, toObject a3, toObject a4, toObject a5] + tryFromObject (ObjectArray arr) = + case arr of + [o1, o2, o3, o4, o5] -> do + v1 <- tryFromObject o1 + v2 <- tryFromObject o2 + v3 <- tryFromObject o3 + v4 <- tryFromObject o4 + v5 <- tryFromObject o5 + return (v1, v2, v3, v4, v5) + _ -> + tryFromObjectError + tryFromObject _ = + tryFromObjectError + +instance (OBJECT a1, OBJECT a2, OBJECT a3, OBJECT a4, OBJECT a5, OBJECT a6) => OBJECT (a1, a2, a3, a4, a5, a6) where + toObject (a1, a2, a3, a4, a5, a6) = ObjectArray [toObject a1, toObject a2, toObject a3, toObject a4, toObject a5, toObject a6] + tryFromObject (ObjectArray arr) = + case arr of + [o1, o2, o3, o4, o5, o6] -> do + v1 <- tryFromObject o1 + v2 <- tryFromObject o2 + v3 <- tryFromObject o3 + v4 <- tryFromObject o4 + v5 <- tryFromObject o5 + v6 <- tryFromObject o6 + return (v1, v2, v3, v4, v5, v6) + _ -> + tryFromObjectError + tryFromObject _ = + tryFromObjectError + +instance (OBJECT a1, OBJECT a2, OBJECT a3, OBJECT a4, OBJECT a5, OBJECT a6, OBJECT a7) => OBJECT (a1, a2, a3, a4, a5, a6, a7) where + toObject (a1, a2, a3, a4, a5, a6, a7) = ObjectArray [toObject a1, toObject a2, toObject a3, toObject a4, toObject a5, toObject a6, toObject a7] + tryFromObject (ObjectArray arr) = + case arr of + [o1, o2, o3, o4, o5, o6, o7] -> do + v1 <- tryFromObject o1 + v2 <- tryFromObject o2 + v3 <- tryFromObject o3 + v4 <- tryFromObject o4 + v5 <- tryFromObject o5 + v6 <- tryFromObject o6 + v7 <- tryFromObject o7 + return (v1, v2, v3, v4, v5, v6, v7) + _ -> + tryFromObjectError + tryFromObject _ = + tryFromObjectError + +instance (OBJECT a1, OBJECT a2, OBJECT a3, OBJECT a4, OBJECT a5, OBJECT a6, OBJECT a7, OBJECT a8) => OBJECT (a1, a2, a3, a4, a5, a6, a7, a8) where + toObject (a1, a2, a3, a4, a5, a6, a7, a8) = ObjectArray [toObject a1, toObject a2, toObject a3, toObject a4, toObject a5, toObject a6, toObject a7, toObject a8] + tryFromObject (ObjectArray arr) = + case arr of + [o1, o2, o3, o4, o5, o6, o7, o8] -> do + v1 <- tryFromObject o1 + v2 <- tryFromObject o2 + v3 <- tryFromObject o3 + v4 <- tryFromObject o4 + v5 <- tryFromObject o5 + v6 <- tryFromObject o6 + v7 <- tryFromObject o7 + v8 <- tryFromObject o8 + return (v1, v2, v3, v4, v5, v6, v7, v8) + _ -> + tryFromObjectError + tryFromObject _ = + tryFromObjectError + +instance (OBJECT a1, OBJECT a2, OBJECT a3, OBJECT a4, OBJECT a5, OBJECT a6, OBJECT a7, OBJECT a8, OBJECT a9) => OBJECT (a1, a2, a3, a4, a5, a6, a7, a8, a9) where + toObject (a1, a2, a3, a4, a5, a6, a7, a8, a9) = ObjectArray [toObject a1, toObject a2, toObject a3, toObject a4, toObject a5, toObject a6, toObject a7, toObject a8, toObject a9] + tryFromObject (ObjectArray arr) = + case arr of + [o1, o2, o3, o4, o5, o6, o7, o8, o9] -> do + v1 <- tryFromObject o1 + v2 <- tryFromObject o2 + v3 <- tryFromObject o3 + v4 <- tryFromObject o4 + v5 <- tryFromObject o5 + v6 <- tryFromObject o6 + v7 <- tryFromObject o7 + v8 <- tryFromObject o8 + v9 <- tryFromObject o9 + return (v1, v2, v3, v4, v5, v6, v7, v8, v9) + _ -> + tryFromObjectError + tryFromObject _ = + tryFromObjectError + +instance (OBJECT a, OBJECT b) => OBJECT [(a, b)] where + toObject = + ObjectMap . map (\(a, b) -> (toObject a, toObject b)) + tryFromObject (ObjectMap mem) = do + mapM (\(a, b) -> liftM2 (,) (tryFromObject a) (tryFromObject b)) mem + tryFromObject _ = + tryFromObjectError + +instance OBJECT a => OBJECT (Maybe a) where + toObject (Just a) = toObject a + toObject Nothing = ObjectNil + + tryFromObject ObjectNil = return Nothing + tryFromObject obj = liftM Just $ tryFromObject obj diff --git a/haskell/src/Data/MessagePack/Pack.hs b/haskell/src/Data/MessagePack/Pack.hs new file mode 100644 index 00000000..16243ad9 --- /dev/null +++ b/haskell/src/Data/MessagePack/Pack.hs @@ -0,0 +1,186 @@ +{-# Language FlexibleInstances #-} +{-# Language IncoherentInstances #-} +{-# Language OverlappingInstances #-} +{-# Language TypeSynonymInstances #-} + +-------------------------------------------------------------------- +-- | +-- Module : Data.MessagePack.Pack +-- Copyright : (c) Hideyuki Tanaka, 2009-2010 +-- License : BSD3 +-- +-- Maintainer: tanaka.hideyuki@gmail.com +-- Stability : experimental +-- Portability: portable +-- +-- MessagePack Serializer using @Data.Binary.Pack@ +-- +-------------------------------------------------------------------- + +module Data.MessagePack.Pack ( + -- * Serializable class + Packable(..), + -- * Simple function to pack a Haskell value + pack, + ) where + +import Data.Binary.Put +import Data.Binary.IEEE754 +import Data.Bits +import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as B8 +import qualified Data.ByteString.Lazy as L +import qualified Data.Vector as V + +-- | Serializable class +class Packable a where + -- | Serialize a value + put :: a -> Put + +-- | Pack Haskell data to MessagePack string. +pack :: Packable a => a -> L.ByteString +pack = runPut . put + +instance Packable Int where + put n = + case n of + _ | n >= 0 && n <= 127 -> + putWord8 $ fromIntegral n + _ | n >= -32 && n <= -1 -> + putWord8 $ fromIntegral n + _ | n >= 0 && n < 0x100 -> do + putWord8 0xCC + putWord8 $ fromIntegral n + _ | n >= 0 && n < 0x10000 -> do + putWord8 0xCD + putWord16be $ fromIntegral n + _ | n >= 0 && n < 0x100000000 -> do + putWord8 0xCE + putWord32be $ fromIntegral n + _ | n >= 0 -> do + putWord8 0xCF + putWord64be $ fromIntegral n + _ | n >= -0x80 -> do + putWord8 0xD0 + putWord8 $ fromIntegral n + _ | n >= -0x8000 -> do + putWord8 0xD1 + putWord16be $ fromIntegral n + _ | n >= -0x80000000 -> do + putWord8 0xD2 + putWord32be $ fromIntegral n + _ -> do + putWord8 0xD3 + putWord64be $ fromIntegral n + +instance Packable () where + put _ = + putWord8 0xC0 + +instance Packable Bool where + put True = putWord8 0xC3 + put False = putWord8 0xC2 + +instance Packable Double where + put d = do + putWord8 0xCB + putFloat64be d + +instance Packable String where + put = putString length (putByteString . B8.pack) + +instance Packable B.ByteString where + put = putString B.length putByteString + +instance Packable L.ByteString where + put = putString (fromIntegral . L.length) putLazyByteString + +putString :: (s -> Int) -> (s -> Put) -> s -> Put +putString lf pf str = do + case lf str of + len | len <= 31 -> do + putWord8 $ 0xA0 .|. fromIntegral len + len | len < 0x10000 -> do + putWord8 0xDA + putWord16be $ fromIntegral len + len -> do + putWord8 0xDB + putWord32be $ fromIntegral len + pf str + +instance Packable a => Packable [a] where + put = putArray length (mapM_ put) + +instance Packable a => Packable (V.Vector a) where + put = putArray V.length (V.mapM_ put) + +instance (Packable a1, Packable a2) => Packable (a1, a2) where + put = putArray (const 2) f where + f (a1, a2) = put a1 >> put a2 + +instance (Packable a1, Packable a2, Packable a3) => Packable (a1, a2, a3) where + put = putArray (const 3) f where + f (a1, a2, a3) = put a1 >> put a2 >> put a3 + +instance (Packable a1, Packable a2, Packable a3, Packable a4) => Packable (a1, a2, a3, a4) where + put = putArray (const 4) f where + f (a1, a2, a3, a4) = put a1 >> put a2 >> put a3 >> put a4 + +instance (Packable a1, Packable a2, Packable a3, Packable a4, Packable a5) => Packable (a1, a2, a3, a4, a5) where + put = putArray (const 5) f where + f (a1, a2, a3, a4, a5) = put a1 >> put a2 >> put a3 >> put a4 >> put a5 + +instance (Packable a1, Packable a2, Packable a3, Packable a4, Packable a5, Packable a6) => Packable (a1, a2, a3, a4, a5, a6) where + put = putArray (const 6) f where + f (a1, a2, a3, a4, a5, a6) = put a1 >> put a2 >> put a3 >> put a4 >> put a5 >> put a6 + +instance (Packable a1, Packable a2, Packable a3, Packable a4, Packable a5, Packable a6, Packable a7) => Packable (a1, a2, a3, a4, a5, a6, a7) where + put = putArray (const 7) f where + f (a1, a2, a3, a4, a5, a6, a7) = put a1 >> put a2 >> put a3 >> put a4 >> put a5 >> put a6 >> put a7 + +instance (Packable a1, Packable a2, Packable a3, Packable a4, Packable a5, Packable a6, Packable a7, Packable a8) => Packable (a1, a2, a3, a4, a5, a6, a7, a8) where + put = putArray (const 8) f where + f (a1, a2, a3, a4, a5, a6, a7, a8) = put a1 >> put a2 >> put a3 >> put a4 >> put a5 >> put a6 >> put a7 >> put a8 + +instance (Packable a1, Packable a2, Packable a3, Packable a4, Packable a5, Packable a6, Packable a7, Packable a8, Packable a9) => Packable (a1, a2, a3, a4, a5, a6, a7, a8, a9) where + put = putArray (const 9) f where + f (a1, a2, a3, a4, a5, a6, a7, a8, a9) = put a1 >> put a2 >> put a3 >> put a4 >> put a5 >> put a6 >> put a7 >> put a8 >> put a9 + +putArray :: (a -> Int) -> (a -> Put) -> a -> Put +putArray lf pf arr = do + case lf arr of + len | len <= 15 -> + putWord8 $ 0x90 .|. fromIntegral len + len | len < 0x10000 -> do + putWord8 0xDC + putWord16be $ fromIntegral len + len -> do + putWord8 0xDD + putWord32be $ fromIntegral len + pf arr + +instance (Packable k, Packable v) => Packable [(k, v)] where + put = putMap length (mapM_ putPair) + +instance (Packable k, Packable v) => Packable (V.Vector (k, v)) where + put = putMap V.length (V.mapM_ putPair) + +putPair :: (Packable a, Packable b) => (a, b) -> Put +putPair (a, b) = put a >> put b + +putMap :: (a -> Int) -> (a -> Put) -> a -> Put +putMap lf pf m = do + case lf m of + len | len <= 15 -> + putWord8 $ 0x80 .|. fromIntegral len + len | len < 0x10000 -> do + putWord8 0xDE + putWord16be $ fromIntegral len + len -> do + putWord8 0xDF + putWord32be $ fromIntegral len + pf m + +instance Packable a => Packable (Maybe a) where + put Nothing = put () + put (Just a) = put a diff --git a/haskell/src/Data/MessagePack/Stream.hs b/haskell/src/Data/MessagePack/Stream.hs deleted file mode 100644 index c56fe8d4..00000000 --- a/haskell/src/Data/MessagePack/Stream.hs +++ /dev/null @@ -1,82 +0,0 @@ --------------------------------------------------------------------- --- | --- Module : Data.MessagePack.Stream --- Copyright : (c) Hideyuki Tanaka, 2009 --- License : BSD3 --- --- Maintainer: tanaka.hideyuki@gmail.com --- Stability : experimental --- Portability: portable --- --- Lazy Stream Serializers and Deserializers --- --------------------------------------------------------------------- - -module Data.MessagePack.Stream( - unpackObjects, - unpackObjectsFromFile, - unpackObjectsFromHandle, - unpackObjectsFromString, - ) where - -import Data.ByteString (ByteString) -import System.IO -import System.IO.Unsafe - -import Data.MessagePack.Base -import Data.MessagePack.Feed - --- | Unpack objects using given feeder. -unpackObjects :: Feeder -> IO [Object] -unpackObjects feeder = do - up <- newUnpacker defaultInitialBufferSize - f up - where - f up = unsafeInterleaveIO $ do - mbo <- unpackOnce up - case mbo of - Just o -> do - os <- f up - return $ o:os - Nothing -> - return [] - - unpackOnce up = do - resp <- unpackerExecute up - case resp of - 0 -> do - r <- feedOnce up - if r - then unpackOnce up - else return Nothing - 1 -> do - obj <- unpackerData up - freeZone =<< unpackerReleaseZone up - unpackerReset up - return $ Just obj - _ -> - error $ "unpackerExecute fails: " ++ show resp - - feedOnce up = do - dat <- feeder - case dat of - Nothing -> - return False - Just bs -> do - unpackerFeed up bs - return True - --- | Unpack objects from file. -unpackObjectsFromFile :: FilePath -> IO [Object] -unpackObjectsFromFile fname = - unpackObjects =<< feederFromFile fname - --- | Unpack objects from handle. -unpackObjectsFromHandle :: Handle -> IO [Object] -unpackObjectsFromHandle h = - unpackObjects =<< feederFromHandle h - --- | Unpack oobjects from given byte sequence. -unpackObjectsFromString :: ByteString -> IO [Object] -unpackObjectsFromString bs = - unpackObjects =<< feederFromString bs diff --git a/haskell/src/Data/MessagePack/Unpack.hs b/haskell/src/Data/MessagePack/Unpack.hs new file mode 100644 index 00000000..a0d618ec --- /dev/null +++ b/haskell/src/Data/MessagePack/Unpack.hs @@ -0,0 +1,308 @@ +{-# Language FlexibleInstances #-} +{-# Language IncoherentInstances #-} +{-# Language OverlappingInstances #-} +{-# Language TypeSynonymInstances #-} +{-# Language DeriveDataTypeable #-} + +-------------------------------------------------------------------- +-- | +-- Module : Data.MessagePack.Unpack +-- Copyright : (c) Hideyuki Tanaka, 2009-2010 +-- License : BSD3 +-- +-- Maintainer: tanaka.hideyuki@gmail.com +-- Stability : experimental +-- Portability: portable +-- +-- MessagePack Deserializer using @Data.Attoparsec@ +-- +-------------------------------------------------------------------- + +module Data.MessagePack.Unpack( + -- * MessagePack deserializer + Unpackable(..), + -- * Simple function to unpack a Haskell value + unpack, + tryUnpack, + -- * Unpack exception + UnpackError(..), + -- * ByteString utils + IsByteString(..), + ) where + +import Control.Exception +import Control.Monad +import qualified Data.Attoparsec as A +import Data.Binary.Get +import Data.Binary.IEEE754 +import Data.Bits +import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as B8 +import qualified Data.ByteString.Lazy as L +import Data.Int +import Data.Typeable +import qualified Data.Vector as V +import Data.Word +import Text.Printf + +-- | Deserializable class +class Unpackable a where + -- | Deserialize a value + get :: A.Parser a + +class IsByteString s where + toBS :: s -> B.ByteString + +instance IsByteString B.ByteString where + toBS = id + +instance IsByteString L.ByteString where + toBS = B.concat . L.toChunks + +-- | The exception of unpack +data UnpackError = + UnpackError String + deriving (Show, Typeable) + +instance Exception UnpackError + +-- | Unpack MessagePack string to Haskell data. +unpack :: (Unpackable a, IsByteString s) => s -> a +unpack bs = + case tryUnpack bs of + Left err -> + throw $ UnpackError err + Right ret -> + ret + +-- | Unpack MessagePack string to Haskell data. +tryUnpack :: (Unpackable a, IsByteString s) => s -> Either String a +tryUnpack bs = + case A.parse get (toBS bs) of + A.Fail _ _ err -> + Left err + A.Partial _ -> + Left "not enough input" + A.Done _ ret -> + Right ret + +instance Unpackable Int where + get = do + c <- A.anyWord8 + case c of + _ | c .&. 0x80 == 0x00 -> + return $ fromIntegral c + _ | c .&. 0xE0 == 0xE0 -> + return $ fromIntegral (fromIntegral c :: Int8) + 0xCC -> + return . fromIntegral =<< A.anyWord8 + 0xCD -> + return . fromIntegral =<< parseUint16 + 0xCE -> + return . fromIntegral =<< parseUint32 + 0xCF -> + return . fromIntegral =<< parseUint64 + 0xD0 -> + return . fromIntegral =<< parseInt8 + 0xD1 -> + return . fromIntegral =<< parseInt16 + 0xD2 -> + return . fromIntegral =<< parseInt32 + 0xD3 -> + return . fromIntegral =<< parseInt64 + _ -> + fail $ printf "invlid integer tag: 0x%02X" c + +instance Unpackable () where + get = do + c <- A.anyWord8 + case c of + 0xC0 -> + return () + _ -> + fail $ printf "invlid nil tag: 0x%02X" c + +instance Unpackable Bool where + get = do + c <- A.anyWord8 + case c of + 0xC3 -> + return True + 0xC2 -> + return False + _ -> + fail $ printf "invlid bool tag: 0x%02X" c + +instance Unpackable Double where + get = do + c <- A.anyWord8 + case c of + 0xCA -> + return . realToFrac . runGet getFloat32be . toLBS =<< A.take 4 + 0xCB -> + return . runGet getFloat64be . toLBS =<< A.take 8 + _ -> + fail $ printf "invlid double tag: 0x%02X" c + +instance Unpackable String where + get = parseString (\n -> return . B8.unpack =<< A.take n) + +instance Unpackable B.ByteString where + get = parseString A.take + +instance Unpackable L.ByteString where + get = parseString (\n -> do bs <- A.take n; return $ L.fromChunks [bs]) + +parseString :: (Int -> A.Parser a) -> A.Parser a +parseString aget = do + c <- A.anyWord8 + case c of + _ | c .&. 0xE0 == 0xA0 -> + aget . fromIntegral $ c .&. 0x1F + 0xDA -> + aget . fromIntegral =<< parseUint16 + 0xDB -> + aget . fromIntegral =<< parseUint32 + _ -> + fail $ printf "invlid raw tag: 0x%02X" c + +instance Unpackable a => Unpackable [a] where + get = parseArray (flip replicateM get) + +instance Unpackable a => Unpackable (V.Vector a) where + get = parseArray (flip V.replicateM get) + +instance (Unpackable a1, Unpackable a2) => Unpackable (a1, a2) where + get = parseArray f where + f 2 = get >>= \a1 -> get >>= \a2 -> return (a1, a2) + f n = fail $ printf "wrong tupple size: expected 2 but got " n + +instance (Unpackable a1, Unpackable a2, Unpackable a3) => Unpackable (a1, a2, a3) where + get = parseArray f where + f 3 = get >>= \a1 -> get >>= \a2 -> get >>= \a3 -> return (a1, a2, a3) + f n = fail $ printf "wrong tupple size: expected 3 but got " n + +instance (Unpackable a1, Unpackable a2, Unpackable a3, Unpackable a4) => Unpackable (a1, a2, a3, a4) where + get = parseArray f where + f 4 = get >>= \a1 -> get >>= \a2 -> get >>= \a3 -> get >>= \a4 -> return (a1, a2, a3, a4) + f n = fail $ printf "wrong tupple size: expected 4 but got " n + +instance (Unpackable a1, Unpackable a2, Unpackable a3, Unpackable a4, Unpackable a5) => Unpackable (a1, a2, a3, a4, a5) where + get = parseArray f where + f 5 = get >>= \a1 -> get >>= \a2 -> get >>= \a3 -> get >>= \a4 -> get >>= \a5 -> return (a1, a2, a3, a4, a5) + f n = fail $ printf "wrong tupple size: expected 5 but got " n + +instance (Unpackable a1, Unpackable a2, Unpackable a3, Unpackable a4, Unpackable a5, Unpackable a6) => Unpackable (a1, a2, a3, a4, a5, a6) where + get = parseArray f where + f 6 = get >>= \a1 -> get >>= \a2 -> get >>= \a3 -> get >>= \a4 -> get >>= \a5 -> get >>= \a6 -> return (a1, a2, a3, a4, a5, a6) + f n = fail $ printf "wrong tupple size: expected 6 but got " n + +instance (Unpackable a1, Unpackable a2, Unpackable a3, Unpackable a4, Unpackable a5, Unpackable a6, Unpackable a7) => Unpackable (a1, a2, a3, a4, a5, a6, a7) where + get = parseArray f where + f 7 = get >>= \a1 -> get >>= \a2 -> get >>= \a3 -> get >>= \a4 -> get >>= \a5 -> get >>= \a6 -> get >>= \a7 -> return (a1, a2, a3, a4, a5, a6, a7) + f n = fail $ printf "wrong tupple size: expected 7 but got " n + +instance (Unpackable a1, Unpackable a2, Unpackable a3, Unpackable a4, Unpackable a5, Unpackable a6, Unpackable a7, Unpackable a8) => Unpackable (a1, a2, a3, a4, a5, a6, a7, a8) where + get = parseArray f where + f 8 = get >>= \a1 -> get >>= \a2 -> get >>= \a3 -> get >>= \a4 -> get >>= \a5 -> get >>= \a6 -> get >>= \a7 -> get >>= \a8 -> return (a1, a2, a3, a4, a5, a6, a7, a8) + f n = fail $ printf "wrong tupple size: expected 8 but got " n + +instance (Unpackable a1, Unpackable a2, Unpackable a3, Unpackable a4, Unpackable a5, Unpackable a6, Unpackable a7, Unpackable a8, Unpackable a9) => Unpackable (a1, a2, a3, a4, a5, a6, a7, a8, a9) where + get = parseArray f where + f 9 = get >>= \a1 -> get >>= \a2 -> get >>= \a3 -> get >>= \a4 -> get >>= \a5 -> get >>= \a6 -> get >>= \a7 -> get >>= \a8 -> get >>= \a9 -> return (a1, a2, a3, a4, a5, a6, a7, a8, a9) + f n = fail $ printf "wrong tupple size: expected 9 but got " n + +parseArray :: (Int -> A.Parser a) -> A.Parser a +parseArray aget = do + c <- A.anyWord8 + case c of + _ | c .&. 0xF0 == 0x90 -> + aget . fromIntegral $ c .&. 0x0F + 0xDC -> + aget . fromIntegral =<< parseUint16 + 0xDD -> + aget . fromIntegral =<< parseUint32 + _ -> + fail $ printf "invlid array tag: 0x%02X" c + +instance (Unpackable k, Unpackable v) => Unpackable [(k, v)] where + get = parseMap (flip replicateM parsePair) + +instance (Unpackable k, Unpackable v) => Unpackable (V.Vector (k, v)) where + get = parseMap (flip V.replicateM parsePair) + +parsePair :: (Unpackable k, Unpackable v) => A.Parser (k, v) +parsePair = do + a <- get + b <- get + return (a, b) + +parseMap :: (Int -> A.Parser a) -> A.Parser a +parseMap aget = do + c <- A.anyWord8 + case c of + _ | c .&. 0xF0 == 0x80 -> + aget . fromIntegral $ c .&. 0x0F + 0xDE -> + aget . fromIntegral =<< parseUint16 + 0xDF -> + aget . fromIntegral =<< parseUint32 + _ -> + fail $ printf "invlid map tag: 0x%02X" c + +instance Unpackable a => Unpackable (Maybe a) where + get = + A.choice + [ liftM Just get + , liftM (\() -> Nothing) get ] + +parseUint16 :: A.Parser Word16 +parseUint16 = do + b0 <- A.anyWord8 + b1 <- A.anyWord8 + return $ (fromIntegral b0 `shiftL` 8) .|. fromIntegral b1 + +parseUint32 :: A.Parser Word32 +parseUint32 = do + b0 <- A.anyWord8 + b1 <- A.anyWord8 + b2 <- A.anyWord8 + b3 <- A.anyWord8 + return $ (fromIntegral b0 `shiftL` 24) .|. + (fromIntegral b1 `shiftL` 16) .|. + (fromIntegral b2 `shiftL` 8) .|. + fromIntegral b3 + +parseUint64 :: A.Parser Word64 +parseUint64 = do + b0 <- A.anyWord8 + b1 <- A.anyWord8 + b2 <- A.anyWord8 + b3 <- A.anyWord8 + b4 <- A.anyWord8 + b5 <- A.anyWord8 + b6 <- A.anyWord8 + b7 <- A.anyWord8 + return $ (fromIntegral b0 `shiftL` 56) .|. + (fromIntegral b1 `shiftL` 48) .|. + (fromIntegral b2 `shiftL` 40) .|. + (fromIntegral b3 `shiftL` 32) .|. + (fromIntegral b4 `shiftL` 24) .|. + (fromIntegral b5 `shiftL` 16) .|. + (fromIntegral b6 `shiftL` 8) .|. + fromIntegral b7 + +parseInt8 :: A.Parser Int8 +parseInt8 = return . fromIntegral =<< A.anyWord8 + +parseInt16 :: A.Parser Int16 +parseInt16 = return . fromIntegral =<< parseUint16 + +parseInt32 :: A.Parser Int32 +parseInt32 = return . fromIntegral =<< parseUint32 + +parseInt64 :: A.Parser Int64 +parseInt64 = return . fromIntegral =<< parseUint64 + +toLBS :: B.ByteString -> L.ByteString +toLBS bs = L.fromChunks [bs] diff --git a/haskell/test/Monad.hs b/haskell/test/Monad.hs index 4bee5c54..2ec40938 100644 --- a/haskell/test/Monad.hs +++ b/haskell/test/Monad.hs @@ -1,16 +1,21 @@ -import Control.Monad.Trans +{-# Language OverloadedStrings #-} + +import Control.Monad.IO.Class +import qualified Data.ByteString as B import Data.MessagePack main = do - sb <- packToString $ do + sb <- return $ packToString $ do put [1,2,3::Int] put (3.14 :: Double) - put "Hoge" + put ("Hoge" :: B.ByteString) print sb - unpackFromString sb $ do + r <- unpackFromString sb $ do arr <- get dbl <- get str <- get - liftIO $ print (arr :: [Int], dbl :: Double, str :: String) + return (arr :: [Int], dbl :: Double, str :: B.ByteString) + + print r diff --git a/haskell/test/Stream.hs b/haskell/test/Stream.hs deleted file mode 100644 index ce060dea..00000000 --- a/haskell/test/Stream.hs +++ /dev/null @@ -1,14 +0,0 @@ -import Control.Applicative -import qualified Data.ByteString as BS -import Data.MessagePack - -main = do - sb <- newSimpleBuffer - pc <- newPacker sb - pack pc [1,2,3::Int] - pack pc True - pack pc "hoge" - bs <- simpleBufferData sb - - os <- unpackObjectsFromString bs - mapM_ print os diff --git a/haskell/test/Test.hs b/haskell/test/Test.hs index 4e713ba6..43af2efc 100644 --- a/haskell/test/Test.hs +++ b/haskell/test/Test.hs @@ -1,36 +1,64 @@ +import Test.Framework +import Test.Framework.Providers.QuickCheck2 +import Test.QuickCheck + import Control.Monad +import qualified Data.ByteString.Char8 as B +import qualified Data.ByteString.Lazy.Char8 as L import Data.MessagePack -{- -main = do - sb <- newSimpleBuffer - pc <- newPacker sb - - pack pc [(1,2),(2,3),(3::Int,4::Int)] - pack pc [4,5,6::Int] - pack pc "hoge" - - bs <- simpleBufferData sb - print bs - - up <- newUnpacker defaultInitialBufferSize - - unpackerFeed up bs +mid :: (Packable a, Unpackable a) => a -> a +mid = unpack . pack - let f = do - res <- unpackerExecute up - when (res==1) $ do - obj <- unpackerData up - print obj - f - - f +prop_mid_int a = a == mid a + where types = a :: Int +prop_mid_nil a = a == mid a + where types = a :: () +prop_mid_bool a = a == mid a + where types = a :: Bool +prop_mid_double a = a == mid a + where types = a :: Double +prop_mid_string a = a == mid a + where types = a :: String +prop_mid_bytestring a = B.pack a == mid (B.pack a) + where types = a :: String +prop_mid_lazy_bytestring a = (L.pack a) == mid (L.pack a) + where types = a :: String +prop_mid_array_int a = a == mid a + where types = a :: [Int] +prop_mid_array_string a = a == mid a + where types = a :: [String] +prop_mid_pair2 a = a == mid a + where types = a :: (Int, Int) +prop_mid_pair3 a = a == mid a + where types = a :: (Int, Int, Int) +prop_mid_pair4 a = a == mid a + where types = a :: (Int, Int, Int, Int) +prop_mid_pair5 a = a == mid a + where types = a :: (Int, Int, Int, Int, Int) +prop_mid_map_int_double a = a == mid a + where types = a :: [(Int, Double)] +prop_mid_map_string_string a = a == mid a + where types = a :: [(String, String)] - return () --} +tests = + [ testGroup "simple" + [ testProperty "int" prop_mid_int + , testProperty "nil" prop_mid_nil + , testProperty "bool" prop_mid_bool + , testProperty "double" prop_mid_double + , testProperty "string" prop_mid_string + , testProperty "bytestring" prop_mid_bytestring + , testProperty "lazy-bytestring" prop_mid_lazy_bytestring + , testProperty "[int]" prop_mid_array_int + , testProperty "[string]" prop_mid_array_string + , testProperty "(int, int)" prop_mid_pair2 + , testProperty "(int, int, int)" prop_mid_pair3 + , testProperty "(int, int, int, int)" prop_mid_pair4 + , testProperty "(int, int, int, int, int)" prop_mid_pair5 + , testProperty "[(int, double)]" prop_mid_map_int_double + , testProperty "[(string, string)]" prop_mid_map_string_string + ] + ] -main = do - bs <- packb [(1,2),(2,3),(3::Int,4::Int)] - print bs - dat <- unpackb bs - print (dat :: Result [(Int, Int)]) +main = defaultMain tests diff --git a/haskell/test/UserData.hs b/haskell/test/UserData.hs new file mode 100644 index 00000000..5e5d0ea0 --- /dev/null +++ b/haskell/test/UserData.hs @@ -0,0 +1,43 @@ +{-# Language TemplateHaskell #-} + +import Data.MessagePack +import Data.MessagePack.Derive + +data T + = A Int String + | B Double + deriving (Show, Eq) + +$(deriveObject ''T) + +data U + = C { c1 :: Int, c2 :: String } + | D { d1 :: Double } + deriving (Show, Eq) + +$(deriveObject ''U) + +data V + = E String | F + deriving (Show, Eq) + +$(deriveObject ''V) + +test :: (OBJECT a, Show a, Eq a) => a -> IO () +test v = do + let bs = pack v + print bs + print (unpack bs == v) + + let oa = toObject v + print oa + print (fromObject oa == v) + +main = do + test $ A 123 "hoge" + test $ B 3.14 + test $ C 123 "hoge" + test $ D 3.14 + test $ E "hello" + test $ F + return () \ No newline at end of file diff --git a/java/pom.xml b/java/pom.xml index 9b74b4cf..70da6a6f 100755 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ MessagePack for Java MessagePack for Java - http://msgpack.sourceforge.net/ + http://msgpack.org/ @@ -29,6 +29,12 @@ 4.8.1 test + + javassist + javassist + 3.12.1.GA + compile + @@ -97,29 +103,31 @@ - msgpack.sourceforge.net + msgpack.org MessagePack Maven2 Repository - http://msgpack.sourceforge.net/maven2 + http://msgpack.org/maven2 - msgpack.sourceforge.net - MessagePack Maven2 Snapshot Repository - http://msgpack.sourceforge.net/maven2-snapshot + repository.jboss.org + https://repository.jboss.org/nexus/content/groups/public/ + + false + false - shell.sourceforge.net - Repository at sourceforge.net - scp://shell.sourceforge.net/home/groups/m/ms/msgpack/htdocs/maven2/ + msgpack.org + Repository at msgpack.org + file://${project.build.directory}/website/maven2/ true - shell.sourceforge.net - Repository Name - scp://shell.sourceforge.net/home/groups/m/ms/msgpack/htdocs/maven2-snapshot/ + msgpack.org + Repository at msgpack.org + file://${project.build.directory}/website/maven2/ diff --git a/java/src/main/java/org/msgpack/BufferedUnpackerImpl.java b/java/src/main/java/org/msgpack/BufferedUnpackerImpl.java index cc6604d4..5b449c79 100644 --- a/java/src/main/java/org/msgpack/BufferedUnpackerImpl.java +++ b/java/src/main/java/org/msgpack/BufferedUnpackerImpl.java @@ -19,7 +19,7 @@ package org.msgpack; import java.io.IOException; import java.nio.ByteBuffer; -//import java.math.BigInteger; +import java.math.BigInteger; abstract class BufferedUnpackerImpl extends UnpackerImpl { int offset = 0; @@ -47,7 +47,7 @@ abstract class BufferedUnpackerImpl extends UnpackerImpl { offset = noffset; } while(!super.isFinished()); - Object obj = super.getData(); + MessagePackObject obj = super.getData(); super.reset(); result.done(obj); @@ -103,7 +103,7 @@ abstract class BufferedUnpackerImpl extends UnpackerImpl { case 0xcc: // unsigned int 8 more(2); advance(2); - return (int)((short)buffer[offset+1] & 0xff); + return (int)((short)(buffer[offset-1]) & 0xff); case 0xcd: // unsigned int 16 more(3); castBuffer.rewind(); @@ -137,7 +137,7 @@ abstract class BufferedUnpackerImpl extends UnpackerImpl { case 0xd0: // signed int 8 more(2); advance(2); - return (int)buffer[offset+1]; + return (int)buffer[offset-1]; case 0xd1: // signed int 16 more(3); castBuffer.rewind(); @@ -178,7 +178,7 @@ abstract class BufferedUnpackerImpl extends UnpackerImpl { case 0xcc: // unsigned int 8 more(2); advance(2); - return (long)((short)buffer[offset+1] & 0xff); + return (long)((short)(buffer[offset-1]) & 0xff); case 0xcd: // unsigned int 16 more(3); castBuffer.rewind(); @@ -198,8 +198,7 @@ abstract class BufferedUnpackerImpl extends UnpackerImpl { { long o = castBuffer.getLong(0); if(o < 0) { - // FIXME - throw new MessageTypeException("uint 64 bigger than 0x7fffffff is not supported"); + throw new MessageTypeException(); } advance(9); return o; @@ -207,7 +206,7 @@ abstract class BufferedUnpackerImpl extends UnpackerImpl { case 0xd0: // signed int 8 more(2); advance(2); - return (long)buffer[offset+1]; + return (long)buffer[offset-1]; case 0xd1: // signed int 16 more(3); castBuffer.rewind(); @@ -231,6 +230,26 @@ abstract class BufferedUnpackerImpl extends UnpackerImpl { } } + final BigInteger unpackBigInteger() throws IOException, MessageTypeException { + more(1); + int b = buffer[offset]; + if((b & 0xff) != 0xcf) { + return BigInteger.valueOf(unpackLong()); + } + + // unsigned int 64 + more(9); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 8); + advance(9); + long o = castBuffer.getLong(0); + if(o < 0) { + return new BigInteger(1, castBuffer.array()); + } else { + return BigInteger.valueOf(o); + } + } + final float unpackFloat() throws IOException, MessageTypeException { more(1); int b = buffer[offset]; @@ -414,7 +433,7 @@ abstract class BufferedUnpackerImpl extends UnpackerImpl { return s; } - final Object unpackObject() throws IOException { + final MessagePackObject unpackObject() throws IOException { UnpackResult result = new UnpackResult(); if(!next(result)) { super.reset(); diff --git a/java/src/main/java/org/msgpack/MessageConvertable.java b/java/src/main/java/org/msgpack/MessageConvertable.java index da251dc1..8acf1f28 100644 --- a/java/src/main/java/org/msgpack/MessageConvertable.java +++ b/java/src/main/java/org/msgpack/MessageConvertable.java @@ -18,6 +18,6 @@ package org.msgpack; public interface MessageConvertable { - public void messageConvert(Object obj) throws MessageTypeException; + public void messageConvert(MessagePackObject obj) throws MessageTypeException; } diff --git a/java/src/main/java/org/msgpack/MessagePackObject.java b/java/src/main/java/org/msgpack/MessagePackObject.java new file mode 100644 index 00000000..2424446f --- /dev/null +++ b/java/src/main/java/org/msgpack/MessagePackObject.java @@ -0,0 +1,136 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package org.msgpack; + +import java.util.List; +import java.util.Set; +import java.util.Map; +import java.math.BigInteger; + +public abstract class MessagePackObject implements Cloneable, MessagePackable { + public boolean isNil() { + return false; + } + + public boolean isBooleanType() { + return false; + } + + public boolean isIntegerType() { + return false; + } + + public boolean isFloatType() { + return false; + } + + public boolean isArrayType() { + return false; + } + + public boolean isMapType() { + return false; + } + + public boolean isRawType() { + return false; + } + + public boolean asBoolean() { + throw new MessageTypeException("type error"); + } + + public byte asByte() { + throw new MessageTypeException("type error"); + } + + public short asShort() { + throw new MessageTypeException("type error"); + } + + public int asInt() { + throw new MessageTypeException("type error"); + } + + public long asLong() { + throw new MessageTypeException("type error"); + } + + public BigInteger asBigInteger() { + throw new MessageTypeException("type error"); + } + + public float asFloat() { + throw new MessageTypeException("type error"); + } + + public double asDouble() { + throw new MessageTypeException("type error"); + } + + public byte[] asByteArray() { + throw new MessageTypeException("type error"); + } + + public String asString() { + throw new MessageTypeException("type error"); + } + + public MessagePackObject[] asArray() { + throw new MessageTypeException("type error"); + } + + public List asList() { + throw new MessageTypeException("type error"); + } + + public Map asMap() { + throw new MessageTypeException("type error"); + } + + public byte byteValue() { + throw new MessageTypeException("type error"); + } + + public short shortValue() { + throw new MessageTypeException("type error"); + } + + public int intValue() { + throw new MessageTypeException("type error"); + } + + public long longValue() { + throw new MessageTypeException("type error"); + } + + public BigInteger bigIntegerValue() { + throw new MessageTypeException("type error"); + } + + public float floatValue() { + throw new MessageTypeException("type error"); + } + + public double doubleValue() { + throw new MessageTypeException("type error"); + } + + abstract public Object clone(); +} + diff --git a/java/src/main/java/org/msgpack/MessageTypeException.java b/java/src/main/java/org/msgpack/MessageTypeException.java index 0fa37b76..698ef6dd 100644 --- a/java/src/main/java/org/msgpack/MessageTypeException.java +++ b/java/src/main/java/org/msgpack/MessageTypeException.java @@ -23,15 +23,5 @@ public class MessageTypeException extends RuntimeException { public MessageTypeException(String s) { super(s); } - - public static MessageTypeException invalidConvert(Object from, Schema to) { - return new MessageTypeException(from.getClass().getName()+" cannot be convert to "+to.getExpression()); - } - - /* FIXME - public static MessageTypeException schemaMismatch(Schema to) { - return new MessageTypeException("schema mismatch "+to.getExpression()); - } - */ } diff --git a/java/src/main/java/org/msgpack/Packer.java b/java/src/main/java/org/msgpack/Packer.java index a92d2b68..139b3b15 100644 --- a/java/src/main/java/org/msgpack/Packer.java +++ b/java/src/main/java/org/msgpack/Packer.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.Map; +import java.math.BigInteger; /** * Packer enables you to serialize objects into OutputStream. @@ -194,6 +195,27 @@ public class Packer { return this; } + public Packer packBigInteger(BigInteger d) throws IOException { + if(d.bitLength() <= 63) { + return packLong(d.longValue()); + } else if(d.bitLength() <= 64 && d.signum() >= 0) { + castBytes[0] = (byte)0xcf; + byte[] barray = d.toByteArray(); + castBytes[1] = barray[barray.length-8]; + castBytes[2] = barray[barray.length-7]; + castBytes[3] = barray[barray.length-6]; + castBytes[4] = barray[barray.length-5]; + castBytes[5] = barray[barray.length-4]; + castBytes[6] = barray[barray.length-3]; + castBytes[7] = barray[barray.length-2]; + castBytes[8] = barray[barray.length-1]; + out.write(castBytes); + return this; + } else { + throw new MessageTypeException("can't pack BigInteger larger than 0xffffffffffffffff"); + } + } + public Packer packFloat(float d) throws IOException { castBytes[0] = (byte)0xca; castBuffer.putFloat(1, d); @@ -223,6 +245,10 @@ public class Packer { return this; } + public Packer packBoolean(boolean d) throws IOException { + return d ? packTrue() : packFalse(); + } + public Packer packArray(int n) throws IOException { if(n < 16) { final int d = 0x90 | n; @@ -282,12 +308,6 @@ public class Packer { } - public Packer packWithSchema(Object o, Schema s) throws IOException { - s.pack(this, o); - return this; - } - - public Packer packString(String s) throws IOException { byte[] b = ((String)s).getBytes("UTF-8"); packRaw(b.length); @@ -295,39 +315,36 @@ public class Packer { } - public Packer pack(String o) throws IOException { - if(o == null) { return packNil(); } - return packString(o); - } - - public Packer pack(MessagePackable o) throws IOException { - if(o == null) { return packNil(); } - o.messagePack(this); - return this; - } - - public Packer pack(byte[] o) throws IOException { - if(o == null) { return packNil(); } - packRaw(o.length); - return packRawBody(o); - } - - public Packer pack(List o) throws IOException { - if(o == null) { return packNil(); } - packArray(o.size()); - for(Object i : o) { pack(i); } - return this; - } - - @SuppressWarnings("unchecked") - public Packer pack(Map o) throws IOException { - if(o == null) { return packNil(); } - packMap(o.size()); - for(Map.Entry e : ((Map)o).entrySet()) { - pack(e.getKey()); - pack(e.getValue()); + public Packer pack(boolean o) throws IOException { + if(o) { + return packTrue(); + } else { + return packFalse(); } - return this; + } + + public Packer pack(byte o) throws IOException { + return packByte(o); + } + + public Packer pack(short o) throws IOException { + return packShort(o); + } + + public Packer pack(int o) throws IOException { + return packInt(o); + } + + public Packer pack(long o) throws IOException { + return packLong(o); + } + + public Packer pack(float o) throws IOException { + return packFloat(o); + } + + public Packer pack(double o) throws IOException { + return packDouble(o); } public Packer pack(Boolean o) throws IOException { @@ -359,6 +376,11 @@ public class Packer { return packLong(o); } + public Packer pack(BigInteger o) throws IOException { + if(o == null) { return packNil(); } + return packBigInteger(o); + } + public Packer pack(Float o) throws IOException { if(o == null) { return packNil(); } return packFloat(o); @@ -369,8 +391,41 @@ public class Packer { return packDouble(o); } + public Packer pack(String o) throws IOException { + if(o == null) { return packNil(); } + return packString(o); + } + + public Packer pack(MessagePackable o) throws IOException { + if(o == null) { return packNil(); } + o.messagePack(this); + return this; + } + + public Packer pack(byte[] o) throws IOException { + if(o == null) { return packNil(); } + packRaw(o.length); + return packRawBody(o); + } + + public Packer pack(List o) throws IOException { + if(o == null) { return packNil(); } + packArray(o.size()); + for(Object i : o) { pack(i); } + return this; + } + + public Packer pack(Map o) throws IOException { + if(o == null) { return packNil(); } + packMap(o.size()); + for(Map.Entry e : ((Map)o).entrySet()) { + pack(e.getKey()); + pack(e.getValue()); + } + return this; + } + - @SuppressWarnings("unchecked") public Packer pack(Object o) throws IOException { if(o == null) { return packNil(); @@ -393,7 +448,7 @@ public class Packer { } else if(o instanceof Map) { Map m = (Map)o; packMap(m.size()); - for(Map.Entry e : m.entrySet()) { + for(Map.Entry e : m.entrySet()) { pack(e.getKey()); pack(e.getValue()); } @@ -416,6 +471,8 @@ public class Packer { return packFloat((Float)o); } else if(o instanceof Double) { return packDouble((Double)o); + } else if(o instanceof BigInteger) { + return packBigInteger((BigInteger)o); } else { throw new MessageTypeException("unknown object "+o+" ("+o.getClass()+")"); } diff --git a/java/src/main/java/org/msgpack/Schema.java b/java/src/main/java/org/msgpack/Schema.java deleted file mode 100644 index f191f7a9..00000000 --- a/java/src/main/java/org/msgpack/Schema.java +++ /dev/null @@ -1,133 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack; - -import java.io.Writer; -import java.io.IOException; -import org.msgpack.schema.SSchemaParser; -import org.msgpack.schema.ClassGenerator; - -public abstract class Schema { - private String expression; - private String name; - - public Schema(String name) { - this.expression = expression; - this.name = name; - } - - public String getName() { - return name; - } - - public String getFullName() { - return name; - } - - public String getExpression() { - return name; - } - - public static Schema parse(String source) { - return SSchemaParser.parse(source); - } - - public static Schema load(String source) { - return SSchemaParser.load(source); - } - - public void write(Writer output) throws IOException { - ClassGenerator.write(this, output); - } - - public abstract void pack(Packer pk, Object obj) throws IOException; - - public abstract Object convert(Object obj) throws MessageTypeException; - - - public Object createFromNil() { - return null; - } - - public Object createFromBoolean(boolean v) { - throw new RuntimeException("type error"); - } - - public Object createFromByte(byte v) { - throw new RuntimeException("type error"); - } - - public Object createFromShort(short v) { - throw new RuntimeException("type error"); - } - - public Object createFromInt(int v) { - throw new RuntimeException("type error"); - } - - public Object createFromLong(long v) { - throw new RuntimeException("type error"); - } - - public Object createFromFloat(float v) { - throw new RuntimeException("type error"); - } - - public Object createFromDouble(double v) { - throw new RuntimeException("type error"); - } - - public Object createFromRaw(byte[] b, int offset, int length) { - throw new RuntimeException("type error"); - } - - /* FIXME - public Object createFromBoolean(boolean v) { - throw MessageTypeException.schemaMismatch(this); - } - - public Object createFromByte(byte v) { - throw MessageTypeException.schemaMismatch(this); - } - - public Object createFromShort(short v) { - throw MessageTypeException.schemaMismatch(this); - } - - public Object createFromInt(int v) { - throw MessageTypeException.schemaMismatch(this); - } - - public Object createFromLong(long v) { - throw MessageTypeException.schemaMismatch(this); - } - - public Object createFromFloat(float v) { - throw MessageTypeException.schemaMismatch(this); - } - - public Object createFromDouble(double v) { - throw MessageTypeException.schemaMismatch(this); - } - - public Object createFromRaw(byte[] b, int offset, int length) { - throw MessageTypeException.schemaMismatch(this); - } - */ -} - diff --git a/java/src/main/java/org/msgpack/UnpackIterator.java b/java/src/main/java/org/msgpack/UnpackIterator.java index f17e2290..8c778b67 100644 --- a/java/src/main/java/org/msgpack/UnpackIterator.java +++ b/java/src/main/java/org/msgpack/UnpackIterator.java @@ -21,7 +21,7 @@ import java.io.IOException; import java.util.Iterator; import java.util.NoSuchElementException; -public class UnpackIterator extends UnpackResult implements Iterator { +public class UnpackIterator extends UnpackResult implements Iterator { private Unpacker pac; UnpackIterator(Unpacker pac) { @@ -38,7 +38,7 @@ public class UnpackIterator extends UnpackResult implements Iterator { } } - public Object next() { + public MessagePackObject next() { if(!finished && !hasNext()) { throw new NoSuchElementException(); } diff --git a/java/src/main/java/org/msgpack/UnpackResult.java b/java/src/main/java/org/msgpack/UnpackResult.java index cec18a16..bb981c12 100644 --- a/java/src/main/java/org/msgpack/UnpackResult.java +++ b/java/src/main/java/org/msgpack/UnpackResult.java @@ -19,13 +19,13 @@ package org.msgpack; public class UnpackResult { protected boolean finished = false; - protected Object data = null; + protected MessagePackObject data = null; public boolean isFinished() { return finished; } - public Object getData() { + public MessagePackObject getData() { return data; } @@ -34,7 +34,7 @@ public class UnpackResult { data = null; } - void done(Object obj) { + void done(MessagePackObject obj) { finished = true; data = obj; } diff --git a/java/src/main/java/org/msgpack/Unpacker.java b/java/src/main/java/org/msgpack/Unpacker.java index f22c58b6..3cae502e 100644 --- a/java/src/main/java/org/msgpack/Unpacker.java +++ b/java/src/main/java/org/msgpack/Unpacker.java @@ -22,12 +22,13 @@ import java.io.InputStream; import java.io.IOException; import java.util.Iterator; import java.nio.ByteBuffer; +import java.math.BigInteger; /** * Unpacker enables you to deserialize objects from stream. * - * Unpacker provides Buffered API, Unbuffered API, Schema API - * and Direct Conversion API. + * Unpacker provides Buffered API, Unbuffered API and + * Direct Conversion API. * * Buffered API uses the internal buffer of the Unpacker. * Following code uses Buffered API with an InputStream: @@ -39,7 +40,7 @@ import java.nio.ByteBuffer; * UnpackResult result = pac.next(); * * // use an iterator. - * for(Object obj : pac) { + * for(MessagePackObject obj : pac) { * // use MessageConvertable interface to convert the * // the generic object to the specific type. * } @@ -56,7 +57,7 @@ import java.nio.ByteBuffer; * pac.feed(input_bytes); * * // use next() method or iterators. - * for(Object obj : pac) { + * for(MessagePackObject obj : pac) { * // ... * } * @@ -75,7 +76,7 @@ import java.nio.ByteBuffer; * System.in.read(pac.getBuffer(), pac.getBufferOffset(), pac.getBufferCapacity()); * * // use next() method or iterators. - * for(Object obj : pac) { + * for(MessagePackObject obj : pac) { * // ... * } * @@ -96,12 +97,12 @@ import java.nio.ByteBuffer; * * // take out object if deserialized object is ready. * if(pac.isFinished()) { - * Object obj = pac.getData(); + * MessagePackObject obj = pac.getData(); * // ... * } * */ -public class Unpacker implements Iterable { +public class Unpacker implements Iterable { // buffer: // +---------------------------------------------+ @@ -170,16 +171,6 @@ public class Unpacker implements Iterable { this.stream = stream; } - /** - * Sets schema to convert deserialized object into specific type. - * Default schema is {@link GenericSchema} that leaves objects for generic type. Use {@link MessageConvertable#messageConvert(Object)} method to convert the generic object. - * @param s schem to use - */ - public Unpacker useSchema(Schema s) { - impl.setSchema(s); - return this; - } - /** * Gets the input stream. @@ -255,7 +246,7 @@ public class Unpacker implements Iterable { /** * Returns the iterator that calls {@link next()} method repeatedly. */ - public Iterator iterator() { + public Iterator iterator() { return new UnpackIterator(this); } @@ -387,7 +378,7 @@ public class Unpacker implements Iterable { /** * Gets the object deserialized by {@link execute(byte[])} method. */ - public Object getData() { + public MessagePackObject getData() { return impl.getData(); } @@ -462,6 +453,15 @@ public class Unpacker implements Iterable { return impl.unpackLong(); } + /** + * Gets one {@code BigInteger} value from the buffer. + * This method calls {@link fill()} method if needed. + * @throws MessageTypeException the first value of the buffer is not a {@code BigInteger}. + */ + public BigInteger unpackBigInteger() throws IOException, MessageTypeException { + return impl.unpackBigInteger(); + } + /** * Gets one {@code float} value from the buffer. * This method calls {@link fill()} method if needed. @@ -557,15 +557,15 @@ public class Unpacker implements Iterable { * Gets one {@code Object} value from the buffer. * This method calls {@link fill()} method if needed. */ - final public Object unpackObject() throws IOException { + final public MessagePackObject unpackObject() throws IOException { return impl.unpackObject(); } - final void unpack(MessageUnpackable obj) throws IOException, MessageTypeException { + final public void unpack(MessageUnpackable obj) throws IOException, MessageTypeException { obj.messageUnpack(this); } - final boolean tryUnpackNull() throws IOException { + final public boolean tryUnpackNull() throws IOException { return impl.tryUnpackNull(); } } diff --git a/java/src/main/java/org/msgpack/UnpackerImpl.java b/java/src/main/java/org/msgpack/UnpackerImpl.java index 10cf5f01..6a7085fb 100644 --- a/java/src/main/java/org/msgpack/UnpackerImpl.java +++ b/java/src/main/java/org/msgpack/UnpackerImpl.java @@ -18,11 +18,8 @@ package org.msgpack; import java.nio.ByteBuffer; -//import java.math.BigInteger; -import org.msgpack.*; -import org.msgpack.schema.GenericSchema; -import org.msgpack.schema.IMapSchema; -import org.msgpack.schema.IArraySchema; +import java.math.BigInteger; +import org.msgpack.object.*; public class UnpackerImpl { static final int CS_HEADER = 0x00; @@ -55,30 +52,19 @@ public class UnpackerImpl { private int[] stack_ct = new int[MAX_STACK_SIZE]; private int[] stack_count = new int[MAX_STACK_SIZE]; private Object[] stack_obj = new Object[MAX_STACK_SIZE]; - private Schema[] stack_schema = new Schema[MAX_STACK_SIZE]; private int top_ct; private int top_count; private Object top_obj; - private Schema top_schema; private ByteBuffer castBuffer = ByteBuffer.allocate(8); private boolean finished = false; - private Object data = null; - - private static final Schema GENERIC_SCHEMA = new GenericSchema(); - private Schema rootSchema; + private MessagePackObject data = null; public UnpackerImpl() { - setSchema(GENERIC_SCHEMA); - } - - public void setSchema(Schema schema) - { - this.rootSchema = schema; reset(); } - public final Object getData() + public final MessagePackObject getData() { return data; } @@ -94,7 +80,6 @@ public class UnpackerImpl { top_ct = 0; top_count = 0; top_obj = null; - top_schema = rootSchema; } public final void reset() @@ -127,20 +112,20 @@ public class UnpackerImpl { if((b & 0x80) == 0) { // Positive Fixnum //System.out.println("positive fixnum "+b); - obj = top_schema.createFromByte((byte)b); + obj = IntegerType.create((byte)b); break _push; } if((b & 0xe0) == 0xe0) { // Negative Fixnum //System.out.println("negative fixnum "+b); - obj = top_schema.createFromByte((byte)b); + obj = IntegerType.create((byte)b); break _push; } if((b & 0xe0) == 0xa0) { // FixRaw trail = b & 0x1f; if(trail == 0) { - obj = top_schema.createFromRaw(new byte[0], 0, 0); + obj = RawType.create(new byte[0]); break _push; } cs = ACS_RAW_VALUE; @@ -151,25 +136,20 @@ public class UnpackerImpl { if(top >= MAX_STACK_SIZE) { throw new UnpackException("parse error"); } - if(!(top_schema instanceof IArraySchema)) { - throw new RuntimeException("type error"); - } count = b & 0x0f; //System.out.println("fixarray count:"+count); - obj = new Object[count]; + obj = new MessagePackObject[count]; if(count == 0) { - obj = ((IArraySchema)top_schema).createFromArray((Object[])obj); + obj = ArrayType.create((MessagePackObject[])obj); break _push; } ++top; stack_obj[top] = top_obj; stack_ct[top] = top_ct; stack_count[top] = top_count; - stack_schema[top] = top_schema; top_obj = obj; top_ct = CT_ARRAY_ITEM; top_count = count; - top_schema = ((IArraySchema)top_schema).getElementSchema(0); break _header_again; } @@ -177,13 +157,10 @@ public class UnpackerImpl { if(top >= MAX_STACK_SIZE) { throw new UnpackException("parse error"); } - if(!(top_schema instanceof IMapSchema)) { - throw new RuntimeException("type error"); - } count = b & 0x0f; - obj = new Object[count*2]; + obj = new MessagePackObject[count*2]; if(count == 0) { - obj = ((IMapSchema)top_schema).createFromMap((Object[])obj); + obj = MapType.create((MessagePackObject[])obj); break _push; } //System.out.println("fixmap count:"+count); @@ -191,23 +168,21 @@ public class UnpackerImpl { stack_obj[top] = top_obj; stack_ct[top] = top_ct; stack_count[top] = top_count; - stack_schema[top] = top_schema; top_obj = obj; top_ct = CT_MAP_KEY; top_count = count; - top_schema = ((IMapSchema)top_schema).getKeySchema(); break _header_again; } switch(b & 0xff) { // FIXME case 0xc0: // nil - obj = top_schema.createFromNil(); + obj = NilType.create(); break _push; case 0xc2: // false - obj = top_schema.createFromBoolean(false); + obj = BooleanType.create(false); break _push; case 0xc3: // true - obj = top_schema.createFromBoolean(true); + obj = BooleanType.create(true); break _push; case 0xca: // float case 0xcb: // double @@ -251,13 +226,13 @@ public class UnpackerImpl { case CS_FLOAT: castBuffer.rewind(); castBuffer.put(src, n, 4); - obj = top_schema.createFromFloat( castBuffer.getFloat(0) ); + obj = FloatType.create( castBuffer.getFloat(0) ); //System.out.println("float "+obj); break _push; case CS_DOUBLE: castBuffer.rewind(); castBuffer.put(src, n, 8); - obj = top_schema.createFromDouble( castBuffer.getDouble(0) ); + obj = FloatType.create( castBuffer.getDouble(0) ); //System.out.println("double "+obj); break _push; case CS_UINT_8: @@ -265,7 +240,7 @@ public class UnpackerImpl { //System.out.println(src[n]); //System.out.println(src[n+1]); //System.out.println(src[n-1]); - obj = top_schema.createFromShort( (short)((src[n]) & 0xff) ); + obj = IntegerType.create( (short)((src[n]) & 0xff) ); //System.out.println("uint8 "+obj); break _push; case CS_UINT_16: @@ -273,13 +248,13 @@ public class UnpackerImpl { //System.out.println(src[n+1]); castBuffer.rewind(); castBuffer.put(src, n, 2); - obj = top_schema.createFromInt( ((int)castBuffer.getShort(0)) & 0xffff ); + obj = IntegerType.create( ((int)castBuffer.getShort(0)) & 0xffff ); //System.out.println("uint 16 "+obj); break _push; case CS_UINT_32: castBuffer.rewind(); castBuffer.put(src, n, 4); - obj = top_schema.createFromLong( ((long)castBuffer.getInt(0)) & 0xffffffffL ); + obj = IntegerType.create( ((long)castBuffer.getInt(0)) & 0xffffffffL ); //System.out.println("uint 32 "+obj); break _push; case CS_UINT_64: @@ -288,38 +263,36 @@ public class UnpackerImpl { { long o = castBuffer.getLong(0); if(o < 0) { - // FIXME - //obj = GenericBigInteger.valueOf(o & 0x7fffffffL).setBit(31); - throw new UnpackException("uint 64 bigger than 0x7fffffff is not supported"); + obj = IntegerType.create(new BigInteger(1, castBuffer.array())); } else { - obj = top_schema.createFromLong( o ); + obj = IntegerType.create(o); } } break _push; case CS_INT_8: - obj = top_schema.createFromByte( src[n] ); + obj = IntegerType.create( src[n] ); break _push; case CS_INT_16: castBuffer.rewind(); castBuffer.put(src, n, 2); - obj = top_schema.createFromShort( castBuffer.getShort(0) ); + obj = IntegerType.create( castBuffer.getShort(0) ); break _push; case CS_INT_32: castBuffer.rewind(); castBuffer.put(src, n, 4); - obj = top_schema.createFromInt( castBuffer.getInt(0) ); + obj = IntegerType.create( castBuffer.getInt(0) ); break _push; case CS_INT_64: castBuffer.rewind(); castBuffer.put(src, n, 8); - obj = top_schema.createFromLong( castBuffer.getLong(0) ); + obj = IntegerType.create( castBuffer.getLong(0) ); break _push; case CS_RAW_16: castBuffer.rewind(); castBuffer.put(src, n, 2); trail = ((int)castBuffer.getShort(0)) & 0xffff; if(trail == 0) { - obj = top_schema.createFromRaw(new byte[0], 0, 0); + obj = RawType.create(new byte[0]); break _push; } cs = ACS_RAW_VALUE; @@ -330,77 +303,68 @@ public class UnpackerImpl { // FIXME overflow check trail = castBuffer.getInt(0) & 0x7fffffff; if(trail == 0) { - obj = top_schema.createFromRaw(new byte[0], 0, 0); + obj = RawType.create(new byte[0]); break _push; } cs = ACS_RAW_VALUE; - case ACS_RAW_VALUE: - obj = top_schema.createFromRaw(src, n, trail); + break _fixed_trail_again; + case ACS_RAW_VALUE: { + byte[] raw = new byte[trail]; + System.arraycopy(src, n, raw, 0, trail); + obj = RawType.create(raw); + } break _push; case CS_ARRAY_16: if(top >= MAX_STACK_SIZE) { throw new UnpackException("parse error"); } - if(!(top_schema instanceof IArraySchema)) { - throw new RuntimeException("type error"); - } castBuffer.rewind(); castBuffer.put(src, n, 2); count = ((int)castBuffer.getShort(0)) & 0xffff; - obj = new Object[count]; + obj = new MessagePackObject[count]; if(count == 0) { - obj = ((IArraySchema)top_schema).createFromArray((Object[])obj); + obj = ArrayType.create((MessagePackObject[])obj); break _push; } ++top; stack_obj[top] = top_obj; stack_ct[top] = top_ct; stack_count[top] = top_count; - stack_schema[top] = top_schema; top_obj = obj; top_ct = CT_ARRAY_ITEM; top_count = count; - top_schema = ((IArraySchema)top_schema).getElementSchema(0); break _header_again; case CS_ARRAY_32: if(top >= MAX_STACK_SIZE) { throw new UnpackException("parse error"); } - if(!(top_schema instanceof IArraySchema)) { - throw new RuntimeException("type error"); - } castBuffer.rewind(); castBuffer.put(src, n, 4); // FIXME overflow check count = castBuffer.getInt(0) & 0x7fffffff; - obj = new Object[count]; + obj = new MessagePackObject[count]; if(count == 0) { - obj = ((IArraySchema)top_schema).createFromArray((Object[])obj); + obj = ArrayType.create((MessagePackObject[])obj); break _push; } ++top; stack_obj[top] = top_obj; stack_ct[top] = top_ct; stack_count[top] = top_count; - stack_schema[top] = top_schema; top_obj = obj; top_ct = CT_ARRAY_ITEM; top_count = count; - top_schema = ((IArraySchema)top_schema).getElementSchema(0); break _header_again; case CS_MAP_16: if(top >= MAX_STACK_SIZE) { throw new UnpackException("parse error"); } - if(!(top_schema instanceof IMapSchema)) { - throw new RuntimeException("type error"); - } castBuffer.rewind(); castBuffer.put(src, n, 2); count = ((int)castBuffer.getShort(0)) & 0xffff; - obj = new Object[count*2]; + obj = new MessagePackObject[count*2]; if(count == 0) { - obj = ((IMapSchema)top_schema).createFromMap((Object[])obj); + obj = MapType.create((MessagePackObject[])obj); break _push; } //System.out.println("fixmap count:"+count); @@ -408,26 +372,21 @@ public class UnpackerImpl { stack_obj[top] = top_obj; stack_ct[top] = top_ct; stack_count[top] = top_count; - stack_schema[top] = top_schema; top_obj = obj; top_ct = CT_MAP_KEY; top_count = count; - top_schema = ((IMapSchema)top_schema).getKeySchema(); break _header_again; case CS_MAP_32: if(top >= MAX_STACK_SIZE) { throw new UnpackException("parse error"); } - if(!(top_schema instanceof IMapSchema)) { - throw new RuntimeException("type error"); - } castBuffer.rewind(); castBuffer.put(src, n, 4); // FIXME overflow check count = castBuffer.getInt(0) & 0x7fffffff; - obj = new Object[count*2]; + obj = new MessagePackObject[count*2]; if(count == 0) { - obj = ((IMapSchema)top_schema).createFromMap((Object[])obj); + obj = MapType.create((MessagePackObject[])obj); break _push; } //System.out.println("fixmap count:"+count); @@ -435,11 +394,9 @@ public class UnpackerImpl { stack_obj[top] = top_obj; stack_ct[top] = top_ct; stack_count[top] = top_count; - stack_schema[top] = top_schema; top_obj = obj; top_ct = CT_MAP_KEY; top_count = count; - top_schema = ((IMapSchema)top_schema).getKeySchema(); break _header_again; default: throw new UnpackException("parse error"); @@ -454,7 +411,7 @@ public class UnpackerImpl { //System.out.println("push top:"+top); if(top == -1) { ++i; - data = obj; + data = (MessagePackObject)obj; finished = true; break _out; } @@ -468,14 +425,10 @@ public class UnpackerImpl { top_obj = stack_obj[top]; top_ct = stack_ct[top]; top_count = stack_count[top]; - top_schema = stack_schema[top]; - obj = ((IArraySchema)top_schema).createFromArray(ar); + obj = ArrayType.create((MessagePackObject[])ar); stack_obj[top] = null; - stack_schema[top] = null; --top; break _push; - } else { - top_schema = ((IArraySchema)stack_schema[top]).getElementSchema(ar.length - top_count); } break _header_again; } @@ -484,7 +437,6 @@ public class UnpackerImpl { Object[] mp = (Object[])top_obj; mp[mp.length - top_count*2] = obj; top_ct = CT_MAP_VALUE; - top_schema = ((IMapSchema)stack_schema[top]).getValueSchema(); break _header_again; } case CT_MAP_VALUE: { @@ -495,10 +447,8 @@ public class UnpackerImpl { top_obj = stack_obj[top]; top_ct = stack_ct[top]; top_count = stack_count[top]; - top_schema = stack_schema[top]; - obj = ((IMapSchema)top_schema).createFromMap(mp); + obj = MapType.create((MessagePackObject[])mp); stack_obj[top] = null; - stack_schema[top] = null; --top; break _push; } diff --git a/java/src/main/java/org/msgpack/object/ArrayType.java b/java/src/main/java/org/msgpack/object/ArrayType.java new file mode 100644 index 00000000..36134dc7 --- /dev/null +++ b/java/src/main/java/org/msgpack/object/ArrayType.java @@ -0,0 +1,81 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package org.msgpack.object; + +import java.util.List; +import java.util.Arrays; +import java.io.IOException; +import org.msgpack.*; + +public class ArrayType extends MessagePackObject { + private MessagePackObject[] array; + + ArrayType(MessagePackObject[] array) { + this.array = array; + } + + public static ArrayType create(MessagePackObject[] array) { + return new ArrayType(array); + } + + @Override + public boolean isArrayType() { + return true; + } + + @Override + public MessagePackObject[] asArray() { + return array; + } + + @Override + public List asList() { + return Arrays.asList(array); + } + + @Override + public void messagePack(Packer pk) throws IOException { + pk.packArray(array.length); + for(int i=0; i < array.length; i++) { + array[i].messagePack(pk); + } + } + + @Override + public boolean equals(Object obj) { + if(obj.getClass() != getClass()) { + return false; + } + return Arrays.equals(((ArrayType)obj).array, array); + } + + @Override + public int hashCode() { + return array.hashCode(); + } + + @Override + public Object clone() { + MessagePackObject[] copy = new MessagePackObject[array.length]; + for(int i=0; i < array.length; i++) { + copy[i] = (MessagePackObject)array[i].clone(); + } + return copy; + } +} + diff --git a/java/src/main/java/org/msgpack/object/BigIntegerTypeIMPL.java b/java/src/main/java/org/msgpack/object/BigIntegerTypeIMPL.java new file mode 100644 index 00000000..b29879f4 --- /dev/null +++ b/java/src/main/java/org/msgpack/object/BigIntegerTypeIMPL.java @@ -0,0 +1,131 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package org.msgpack.object; + +import java.math.BigInteger; +import java.io.IOException; +import org.msgpack.*; + +class BigIntegerTypeIMPL extends IntegerType { + private BigInteger value; + + BigIntegerTypeIMPL(BigInteger value) { + this.value = value; + } + + @Override + public byte asByte() { + if(value.compareTo(BigInteger.valueOf((long)Byte.MAX_VALUE)) > 0) { + throw new MessageTypeException("type error"); + } + return value.byteValue(); + } + + @Override + public short asShort() { + if(value.compareTo(BigInteger.valueOf((long)Short.MAX_VALUE)) > 0) { + throw new MessageTypeException("type error"); + } + return value.shortValue(); + } + + @Override + public int asInt() { + if(value.compareTo(BigInteger.valueOf((long)Integer.MAX_VALUE)) > 0) { + throw new MessageTypeException("type error"); + } + return value.intValue(); + } + + @Override + public long asLong() { + if(value.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) { + throw new MessageTypeException("type error"); + } + return value.longValue(); + } + + @Override + public BigInteger asBigInteger() { + return value; + } + + @Override + public byte byteValue() { + return value.byteValue(); + } + + @Override + public short shortValue() { + return value.shortValue(); + } + + @Override + public int intValue() { + return value.intValue(); + } + + @Override + public long longValue() { + return value.longValue(); + } + + @Override + public BigInteger bigIntegerValue() { + return value; + } + + @Override + public float floatValue() { + return value.floatValue(); + } + + @Override + public double doubleValue() { + return value.doubleValue(); + } + + @Override + public void messagePack(Packer pk) throws IOException { + pk.packBigInteger(value); + } + + @Override + public boolean equals(Object obj) { + if(obj.getClass() != getClass()) { + if(obj.getClass() == ShortIntegerTypeIMPL.class) { + return BigInteger.valueOf(((ShortIntegerTypeIMPL)obj).longValue()).equals(value); + } else if(obj.getClass() == LongIntegerTypeIMPL.class) { + return BigInteger.valueOf(((LongIntegerTypeIMPL)obj).longValue()).equals(value); + } + return false; + } + return ((BigIntegerTypeIMPL)obj).value.equals(value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public Object clone() { + return new BigIntegerTypeIMPL(value); + } +} + diff --git a/java/src/main/java/org/msgpack/object/BooleanType.java b/java/src/main/java/org/msgpack/object/BooleanType.java new file mode 100644 index 00000000..65bd42b4 --- /dev/null +++ b/java/src/main/java/org/msgpack/object/BooleanType.java @@ -0,0 +1,71 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package org.msgpack.object; + +import java.io.IOException; +import org.msgpack.*; + +public class BooleanType extends MessagePackObject { + private boolean value; + + BooleanType(boolean value) { + this.value = value; + } + + public static BooleanType create(boolean value) { + return new BooleanType(value); + } + + @Override + public boolean isBooleanType() { + return true; + } + + @Override + public boolean asBoolean() { + return value; + } + + @Override + public void messagePack(Packer pk) throws IOException { + pk.packBoolean(value); + } + + @Override + public boolean equals(Object obj) { + if(obj.getClass() != getClass()) { + return false; + } + return ((BooleanType)obj).value == value; + } + + @Override + public int hashCode() { + if(value) { + return 1231; + } else { + return 1237; + } + } + + @Override + public Object clone() { + return new BooleanType(value); + } +} + diff --git a/java/src/main/java/org/msgpack/object/DoubleTypeIMPL.java b/java/src/main/java/org/msgpack/object/DoubleTypeIMPL.java new file mode 100644 index 00000000..fd38089e --- /dev/null +++ b/java/src/main/java/org/msgpack/object/DoubleTypeIMPL.java @@ -0,0 +1,101 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package org.msgpack.object; + +import java.math.BigInteger; +import java.io.IOException; +import org.msgpack.*; + +class DoubleTypeIMPL extends FloatType { + private double value; + + public DoubleTypeIMPL(double value) { + this.value = value; + } + + @Override + public float asFloat() { + // FIXME check overflow, underflow? + return (float)value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public byte byteValue() { + return (byte)value; + } + + @Override + public short shortValue() { + return (short)value; + } + + @Override + public int intValue() { + return (int)value; + } + + @Override + public long longValue() { + return (long)value; + } + + @Override + public BigInteger bigIntegerValue() { + return BigInteger.valueOf((long)value); + } + + @Override + public float floatValue() { + return (float)value; + } + + @Override + public double doubleValue() { + return (double)value; + } + + @Override + public void messagePack(Packer pk) throws IOException { + pk.packDouble(value); + } + + @Override + public boolean equals(Object obj) { + if(obj.getClass() != getClass()) { + return false; + } + return ((DoubleTypeIMPL)obj).value == value; + } + + @Override + public int hashCode() { + long v = Double.doubleToLongBits(value); + return (int)(v^(v>>>32)); + } + + @Override + public Object clone() { + return new DoubleTypeIMPL(value); + } +} + diff --git a/java/src/main/java/org/msgpack/schema/IArraySchema.java b/java/src/main/java/org/msgpack/object/FloatType.java similarity index 65% rename from java/src/main/java/org/msgpack/schema/IArraySchema.java rename to java/src/main/java/org/msgpack/object/FloatType.java index 67e9f55a..514efd5b 100644 --- a/java/src/main/java/org/msgpack/schema/IArraySchema.java +++ b/java/src/main/java/org/msgpack/object/FloatType.java @@ -15,12 +15,22 @@ // See the License for the specific language governing permissions and // limitations under the License. // -package org.msgpack.schema; +package org.msgpack.object; -import org.msgpack.Schema; +import org.msgpack.*; -public interface IArraySchema { - public Schema getElementSchema(int index); - public Object createFromArray(Object[] obj); +public abstract class FloatType extends MessagePackObject { + @Override + public boolean isFloatType() { + return true; + } + + public static FloatType create(float value) { + return new FloatTypeIMPL(value); + } + + public static FloatType create(double value) { + return new DoubleTypeIMPL(value); + } } diff --git a/java/src/main/java/org/msgpack/object/FloatTypeIMPL.java b/java/src/main/java/org/msgpack/object/FloatTypeIMPL.java new file mode 100644 index 00000000..1d799611 --- /dev/null +++ b/java/src/main/java/org/msgpack/object/FloatTypeIMPL.java @@ -0,0 +1,94 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package org.msgpack.object; + +import java.math.BigInteger; +import java.io.IOException; +import org.msgpack.*; + +class FloatTypeIMPL extends FloatType { + private float value; + + public FloatTypeIMPL(float value) { + this.value = value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return (double)value; + } + + @Override + public byte byteValue() { + return (byte)value; + } + + @Override + public short shortValue() { + return (short)value; + } + + @Override + public int intValue() { + return (int)value; + } + + @Override + public long longValue() { + return (long)value; + } + + @Override + public float floatValue() { + return (float)value; + } + + @Override + public double doubleValue() { + return (double)value; + } + + @Override + public void messagePack(Packer pk) throws IOException { + pk.packFloat(value); + } + + @Override + public boolean equals(Object obj) { + if(obj.getClass() != getClass()) { + return false; + } + return ((FloatTypeIMPL)obj).value == value; + } + + @Override + public int hashCode() { + return Float.floatToIntBits(value); + } + + @Override + public Object clone() { + return new FloatTypeIMPL(value); + } +} + diff --git a/java/src/main/java/org/msgpack/object/IntegerType.java b/java/src/main/java/org/msgpack/object/IntegerType.java new file mode 100644 index 00000000..43357e8e --- /dev/null +++ b/java/src/main/java/org/msgpack/object/IntegerType.java @@ -0,0 +1,49 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package org.msgpack.object; + +import java.math.BigInteger; +import org.msgpack.*; + +public abstract class IntegerType extends MessagePackObject { + @Override + public boolean isIntegerType() { + return true; + } + + public static IntegerType create(byte value) { + return new ShortIntegerTypeIMPL((int)value); + } + + public static IntegerType create(short value) { + return new ShortIntegerTypeIMPL((int)value); + } + + public static IntegerType create(int value) { + return new ShortIntegerTypeIMPL(value); + } + + public static IntegerType create(long value) { + return new LongIntegerTypeIMPL(value); + } + + public static IntegerType create(BigInteger value) { + return new BigIntegerTypeIMPL(value); + } +} + diff --git a/java/src/main/java/org/msgpack/object/LongIntegerTypeIMPL.java b/java/src/main/java/org/msgpack/object/LongIntegerTypeIMPL.java new file mode 100644 index 00000000..d052e77a --- /dev/null +++ b/java/src/main/java/org/msgpack/object/LongIntegerTypeIMPL.java @@ -0,0 +1,128 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package org.msgpack.object; + +import java.math.BigInteger; +import java.io.IOException; +import org.msgpack.*; + +class LongIntegerTypeIMPL extends IntegerType { + private long value; + + LongIntegerTypeIMPL(long value) { + this.value = value; + } + + @Override + public byte asByte() { + if(value > (long)Byte.MAX_VALUE) { + throw new MessageTypeException("type error"); + } + return (byte)value; + } + + @Override + public short asShort() { + if(value > (long)Short.MAX_VALUE) { + throw new MessageTypeException("type error"); + } + return (short)value; + } + + @Override + public int asInt() { + if(value > (long)Integer.MAX_VALUE) { + throw new MessageTypeException("type error"); + } + return (int)value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf(value); + } + + @Override + public byte byteValue() { + return (byte)value; + } + + @Override + public short shortValue() { + return (short)value; + } + + @Override + public int intValue() { + return (int)value; + } + + @Override + public long longValue() { + return (long)value; + } + + @Override + public BigInteger bigIntegerValue() { + return BigInteger.valueOf(value); + } + + @Override + public float floatValue() { + return (float)value; + } + + @Override + public double doubleValue() { + return (double)value; + } + + @Override + public void messagePack(Packer pk) throws IOException { + pk.packLong(value); + } + + @Override + public boolean equals(Object obj) { + if(obj.getClass() != getClass()) { + if(obj.getClass() == ShortIntegerTypeIMPL.class) { + return value == ((ShortIntegerTypeIMPL)obj).longValue(); + } else if(obj.getClass() == BigIntegerTypeIMPL.class) { + return BigInteger.valueOf(value).equals(((BigIntegerTypeIMPL)obj).bigIntegerValue()); + } + return false; + } + return ((LongIntegerTypeIMPL)obj).value == value; + } + + @Override + public int hashCode() { + return (int)(value^(value>>>32)); + } + + @Override + public Object clone() { + return new LongIntegerTypeIMPL(value); + } +} + diff --git a/java/src/main/java/org/msgpack/object/MapType.java b/java/src/main/java/org/msgpack/object/MapType.java new file mode 100644 index 00000000..148c0bf4 --- /dev/null +++ b/java/src/main/java/org/msgpack/object/MapType.java @@ -0,0 +1,84 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package org.msgpack.object; + +import java.util.HashMap; +import java.util.Map; +import java.util.Arrays; +import java.io.IOException; +import org.msgpack.*; + +public class MapType extends MessagePackObject { + private MessagePackObject[] map; + + MapType(MessagePackObject[] map) { + this.map = map; + } + + public static MapType create(MessagePackObject[] map) { + return new MapType(map); + } + + @Override + public boolean isMapType() { + return true; + } + + @Override + public Map asMap() { + HashMap m = new HashMap(map.length / 2); + int i = 0; + while(i < map.length) { + MessagePackObject k = map[i++]; + MessagePackObject v = map[i++]; + m.put(k, v); + } + return m; + } + + @Override + public void messagePack(Packer pk) throws IOException { + pk.packMap(map.length / 2); + for(int i=0; i < map.length; i++) { + map[i].messagePack(pk); + } + } + + @Override + public boolean equals(Object obj) { + if(obj.getClass() != getClass()) { + return false; + } + return Arrays.equals(((MapType)obj).map, map); + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + @Override + public Object clone() { + MessagePackObject[] copy = new MessagePackObject[map.length]; + for(int i=0; i < map.length; i++) { + copy[i] = (MessagePackObject)map[i].clone(); + } + return copy; + } +} + diff --git a/java/src/main/java/org/msgpack/schema/FieldSchema.java b/java/src/main/java/org/msgpack/object/NilType.java similarity index 52% rename from java/src/main/java/org/msgpack/schema/FieldSchema.java rename to java/src/main/java/org/msgpack/object/NilType.java index 66c2ff23..c443db18 100644 --- a/java/src/main/java/org/msgpack/schema/FieldSchema.java +++ b/java/src/main/java/org/msgpack/object/NilType.java @@ -15,29 +15,46 @@ // See the License for the specific language governing permissions and // limitations under the License. // -package org.msgpack.schema; +package org.msgpack.object; -import org.msgpack.Schema; +import java.io.IOException; +import org.msgpack.*; -public class FieldSchema { - private String name; - private Schema schema; +public class NilType extends MessagePackObject { + private final static NilType INSTANCE = new NilType(); - public FieldSchema(String name, Schema schema) { - this.name = name; - this.schema = schema; + public static NilType create() { + return INSTANCE; } - public final String getName() { - return name; + private NilType() { } + + @Override + public boolean isNil() { + return true; } - public final Schema getSchema() { - return schema; + @Override + public void messagePack(Packer pk) throws IOException { + pk.packNil(); } - public String getExpression() { - return "(field "+name+" "+schema.getExpression()+")"; + @Override + public boolean equals(Object obj) { + if(obj.getClass() != getClass()) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public Object clone() { + return INSTANCE; } } diff --git a/java/src/main/java/org/msgpack/object/RawType.java b/java/src/main/java/org/msgpack/object/RawType.java new file mode 100644 index 00000000..26f6e0d9 --- /dev/null +++ b/java/src/main/java/org/msgpack/object/RawType.java @@ -0,0 +1,90 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package org.msgpack.object; + +import java.util.Arrays; +import java.io.IOException; +import org.msgpack.*; + +public class RawType extends MessagePackObject { + private byte[] bytes; + + RawType(byte[] bytes) { + this.bytes = bytes; + } + + RawType(String str) { + try { + this.bytes = str.getBytes("UTF-8"); + } catch (Exception e) { + throw new MessageTypeException("type error"); + } + } + + public static RawType create(byte[] bytes) { + return new RawType(bytes); + } + + public static RawType create(String str) { + return new RawType(str); + } + + @Override + public boolean isRawType() { + return true; + } + + @Override + public byte[] asByteArray() { + return bytes; + } + + @Override + public String asString() { + try { + return new String(bytes, "UTF-8"); + } catch (Exception e) { + throw new MessageTypeException("type error"); + } + } + + @Override + public void messagePack(Packer pk) throws IOException { + pk.packRaw(bytes.length); + pk.packRawBody(bytes); + } + + @Override + public boolean equals(Object obj) { + if(obj.getClass() != getClass()) { + return false; + } + return Arrays.equals(((RawType)obj).bytes, bytes); + } + + @Override + public int hashCode() { + return bytes.hashCode(); + } + + @Override + public Object clone() { + return new RawType((byte[])bytes.clone()); + } +} + diff --git a/java/src/main/java/org/msgpack/object/ShortIntegerTypeIMPL.java b/java/src/main/java/org/msgpack/object/ShortIntegerTypeIMPL.java new file mode 100644 index 00000000..dbed4263 --- /dev/null +++ b/java/src/main/java/org/msgpack/object/ShortIntegerTypeIMPL.java @@ -0,0 +1,125 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package org.msgpack.object; + +import java.math.BigInteger; +import java.io.IOException; +import org.msgpack.*; + +class ShortIntegerTypeIMPL extends IntegerType { + private int value; + + ShortIntegerTypeIMPL(int value) { + this.value = value; + } + + @Override + public byte asByte() { + if(value > (int)Byte.MAX_VALUE) { + throw new MessageTypeException("type error"); + } + return (byte)value; + } + + @Override + public short asShort() { + if(value > (int)Short.MAX_VALUE) { + throw new MessageTypeException("type error"); + } + return (short)value; + } + + @Override + public int asInt() { + return value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf((long)value); + } + + @Override + public byte byteValue() { + return (byte)value; + } + + @Override + public short shortValue() { + return (short)value; + } + + @Override + public int intValue() { + return (int)value; + } + + @Override + public long longValue() { + return (long)value; + } + + @Override + public BigInteger bigIntegerValue() { + return BigInteger.valueOf((long)value); + } + + @Override + public float floatValue() { + return (float)value; + } + + @Override + public double doubleValue() { + return (double)value; + } + + @Override + public void messagePack(Packer pk) throws IOException { + pk.packInt(value); + } + + @Override + public boolean equals(Object obj) { + if(obj.getClass() != getClass()) { + if(obj.getClass() == LongIntegerTypeIMPL.class) { + return (long)value == ((LongIntegerTypeIMPL)obj).longValue(); + } else if(obj.getClass() == BigIntegerTypeIMPL.class) { + return ((BigIntegerTypeIMPL)obj).bigIntegerValue().equals(BigInteger.valueOf((long)value)); + } + return false; + } + return ((ShortIntegerTypeIMPL)obj).value == value; + } + + @Override + public int hashCode() { + return value; + } + + @Override + public Object clone() { + return new ShortIntegerTypeIMPL(value); + } +} + diff --git a/java/src/main/java/org/msgpack/schema/ArraySchema.java b/java/src/main/java/org/msgpack/schema/ArraySchema.java deleted file mode 100644 index fd471439..00000000 --- a/java/src/main/java/org/msgpack/schema/ArraySchema.java +++ /dev/null @@ -1,125 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Set; -import java.util.List; -import java.util.ArrayList; -import java.util.RandomAccess; -import java.io.IOException; -import org.msgpack.*; - -public class ArraySchema extends Schema implements IArraySchema { - private Schema elementSchema; - - public ArraySchema(Schema elementSchema) - { - super("array"); - this.elementSchema = elementSchema; - } - - @Override - public String getFullName() - { - return "List<"+elementSchema.getFullName()+">"; - } - - @Override - public String getExpression() - { - return "(array "+elementSchema.getExpression()+")"; - } - - @Override - @SuppressWarnings("unchecked") - public void pack(Packer pk, Object obj) throws IOException - { - if(obj instanceof List) { - ArrayList d = (ArrayList)obj; - pk.packArray(d.size()); - if(obj instanceof RandomAccess) { - for(int i=0; i < d.size(); ++i) { - elementSchema.pack(pk, d.get(i)); - } - } else { - for(Object e : d) { - elementSchema.pack(pk, e); - } - } - - } else if(obj instanceof Set) { - Set d = (Set)obj; - pk.packArray(d.size()); - for(Object e : d) { - elementSchema.pack(pk, e); - } - - } else if(obj == null) { - pk.packNil(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - @SuppressWarnings("unchecked") - public Object convert(Object obj) throws MessageTypeException - { - if(obj instanceof List) { - List d = (List)obj; - ArrayList ar = new ArrayList(d.size()); - if(obj instanceof RandomAccess) { - for(int i=0; i < d.size(); ++i) { - ar.add( elementSchema.convert(d.get(i)) ); - } - } else { - for(Object e : d) { - ar.add( elementSchema.convert(e) ); - } - } - return ar; - - } else if(obj instanceof Collection) { - Collection d = (Collection)obj; - ArrayList ar = new ArrayList(d.size()); - for(Object e : (Collection)obj) { - ar.add( elementSchema.convert(e) ); - } - return ar; - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Schema getElementSchema(int index) - { - return elementSchema; - } - - @Override - public Object createFromArray(Object[] obj) - { - return Arrays.asList(obj); - } -} - diff --git a/java/src/main/java/org/msgpack/schema/ByteSchema.java b/java/src/main/java/org/msgpack/schema/ByteSchema.java deleted file mode 100644 index 9ee6a82b..00000000 --- a/java/src/main/java/org/msgpack/schema/ByteSchema.java +++ /dev/null @@ -1,89 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.io.IOException; -import org.msgpack.*; - -public class ByteSchema extends Schema { - public ByteSchema() { - super("Byte"); - } - - @Override - public String getExpression() { - return "byte"; - } - - @Override - public void pack(Packer pk, Object obj) throws IOException { - if(obj instanceof Number) { - pk.packByte( ((Number)obj).byteValue() ); - - } else if(obj == null) { - pk.packNil(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object convert(Object obj) throws MessageTypeException { - if(obj instanceof Byte) { - return obj; - - } else if(obj instanceof Number) { - return ((Number)obj).byteValue(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object createFromByte(byte v) { - return (byte)v; - } - - @Override - public Object createFromShort(short v) { - return (byte)v; - } - - @Override - public Object createFromInt(int v) { - return (byte)v; - } - - @Override - public Object createFromLong(long v) { - return (byte)v; - } - - @Override - public Object createFromFloat(float v) { - return (byte)v; - } - - @Override - public Object createFromDouble(double v) { - return (byte)v; - } -} - diff --git a/java/src/main/java/org/msgpack/schema/ClassGenerator.java b/java/src/main/java/org/msgpack/schema/ClassGenerator.java deleted file mode 100644 index f8a13fad..00000000 --- a/java/src/main/java/org/msgpack/schema/ClassGenerator.java +++ /dev/null @@ -1,241 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.util.ArrayList; -import java.util.List; -import java.io.IOException; -import java.io.File; -import java.io.Writer; -import org.msgpack.*; - -public class ClassGenerator { - private ClassSchema schema; - private Writer writer; - private int indent; - - private ClassGenerator(Writer writer) { - this.writer = writer; - this.indent = 0; - } - - public static void write(Schema schema, Writer dest) throws IOException { - if(!(schema instanceof ClassSchema)) { - throw new RuntimeException("schema is not class schema"); - } - ClassSchema cs = (ClassSchema)schema; - new ClassGenerator(dest).run(cs); - } - - private void run(ClassSchema cs) throws IOException { - List subclasses = new ArrayList(); - for(FieldSchema f : cs.getFields()) { - findSubclassSchema(subclasses, f.getSchema()); - } - - for(ClassSchema sub : subclasses) { - sub.setNamespace(cs.getNamespace()); - sub.setImports(cs.getImports()); - } - - this.schema = cs; - - writeHeader(); - - writeClass(); - - for(ClassSchema sub : subclasses) { - this.schema = sub; - writeSubclass(); - } - - writeFooter(); - - this.schema = null; - writer.flush(); - } - - private void findSubclassSchema(List dst, Schema s) { - if(s instanceof ClassSchema) { - ClassSchema cs = (ClassSchema)s; - if(!dst.contains(cs)) { dst.add(cs); } - for(FieldSchema f : cs.getFields()) { - findSubclassSchema(dst, f.getSchema()); - } - } else if(s instanceof ArraySchema) { - ArraySchema as = (ArraySchema)s; - findSubclassSchema(dst, as.getElementSchema(0)); - } else if(s instanceof MapSchema) { - MapSchema as = (MapSchema)s; - findSubclassSchema(dst, as.getKeySchema()); - findSubclassSchema(dst, as.getValueSchema()); - } - } - - private void writeHeader() throws IOException { - if(schema.getNamespace() != null) { - line("package "+schema.getNamespace()+";"); - line(); - } - line("import java.util.*;"); - line("import java.io.*;"); - line("import org.msgpack.*;"); - line("import org.msgpack.schema.ClassSchema;"); - line("import org.msgpack.schema.FieldSchema;"); - } - - private void writeFooter() throws IOException { - line(); - } - - private void writeClass() throws IOException { - line(); - line("public final class "+schema.getName()+" implements MessagePackable, MessageConvertable"); - line("{"); - pushIndent(); - writeSchema(); - writeMemberVariables(); - writeMemberFunctions(); - popIndent(); - line("}"); - } - - private void writeSubclass() throws IOException { - line(); - line("final class "+schema.getName()+" implements MessagePackable, MessageConvertable"); - line("{"); - pushIndent(); - writeSchema(); - writeMemberVariables(); - writeMemberFunctions(); - popIndent(); - line("}"); - } - - private void writeSchema() throws IOException { - line("private static final ClassSchema _SCHEMA = (ClassSchema)Schema.load(\""+schema.getExpression()+"\");"); - line("public static ClassSchema getSchema() { return _SCHEMA; }"); - } - - private void writeMemberVariables() throws IOException { - line(); - for(FieldSchema f : schema.getFields()) { - line("public "+f.getSchema().getFullName()+" "+f.getName()+";"); - } - } - - private void writeMemberFunctions() throws IOException { - // void messagePack(Packer pk) - // boolean equals(Object obj) - // int hashCode() - // void set(int _index, Object _value) - // Object get(int _index); - // getXxx() - // setXxx(Xxx xxx) - writeConstructors(); - writeAccessors(); - writePackFunction(); - writeConvertFunction(); - writeFactoryFunction(); - } - - private void writeConstructors() throws IOException { - line(); - line("public "+schema.getName()+"() { }"); - } - - private void writeAccessors() throws IOException { - // FIXME - //line(); - //for(FieldSchema f : schema.getFields()) { - // line(""); - //} - } - - private void writePackFunction() throws IOException { - line(); - line("@Override"); - line("public void messagePack(Packer _pk) throws IOException"); - line("{"); - pushIndent(); - line("_pk.packArray("+schema.getFields().length+");"); - line("FieldSchema[] _fields = _SCHEMA.getFields();"); - int i = 0; - for(FieldSchema f : schema.getFields()) { - line("_fields["+i+"].getSchema().pack(_pk, "+f.getName()+");"); - ++i; - } - popIndent(); - line("}"); - } - - private void writeConvertFunction() throws IOException { - line(); - line("@Override"); - line("@SuppressWarnings(\"unchecked\")"); - line("public void messageConvert(Object obj) throws MessageTypeException"); - line("{"); - pushIndent(); - line("Object[] _source = ((List)obj).toArray();"); - line("FieldSchema[] _fields = _SCHEMA.getFields();"); - int i = 0; - for(FieldSchema f : schema.getFields()) { - line("if(_source.length <= "+i+") { return; } this."+f.getName()+" = ("+f.getSchema().getFullName()+")_fields["+i+"].getSchema().convert(_source["+i+"]);"); - ++i; - } - popIndent(); - line("}"); - } - - private void writeFactoryFunction() throws IOException { - line(); - line("@SuppressWarnings(\"unchecked\")"); - line("public static "+schema.getName()+" createFromMessage(Object[] _message)"); - line("{"); - pushIndent(); - line(schema.getName()+" _self = new "+schema.getName()+"();"); - int i = 0; - for(FieldSchema f : schema.getFields()) { - line("if(_message.length <= "+i+") { return _self; } _self."+f.getName()+" = ("+f.getSchema().getFullName()+")_message["+i+"];"); - ++i; - } - line("return _self;"); - popIndent(); - line("}"); - } - - private void line(String str) throws IOException { - for(int i=0; i < indent; ++i) { - writer.write("\t"); - } - writer.write(str+"\n"); - } - - private void line() throws IOException { - writer.write("\n"); - } - - private void pushIndent() { - indent += 1; - } - - private void popIndent() { - indent -= 1; - } -} - diff --git a/java/src/main/java/org/msgpack/schema/ClassSchema.java b/java/src/main/java/org/msgpack/schema/ClassSchema.java deleted file mode 100644 index cd5c008e..00000000 --- a/java/src/main/java/org/msgpack/schema/ClassSchema.java +++ /dev/null @@ -1,95 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.util.Arrays; -import java.util.List; -import org.msgpack.*; - -public abstract class ClassSchema extends Schema implements IArraySchema { - protected FieldSchema[] fields; - protected List imports; - protected String namespace; - protected String fqdn; - - public ClassSchema( - String name, String namespace, - List imports, List fields) { - super(name); - this.namespace = namespace; - this.imports = imports; // FIXME clone? - this.fields = new FieldSchema[fields.size()]; - System.arraycopy(fields.toArray(), 0, this.fields, 0, fields.size()); - if(namespace == null) { - this.fqdn = name; - } else { - this.fqdn = namespace+"."+name; - } - } - - public final FieldSchema[] getFields() { - return fields; - } - - String getNamespace() { - return namespace; - } - - List getImports() { - return imports; - } - - void setNamespace(String namespace) { - this.namespace = namespace; - } - - void setImports(List imports) { - this.imports = imports; // FIXME clone? - } - - //@Override - //public String getFullName() - //{ - // if(namespace == null) { - // return getName(); - // } else { - // return namespace+"."+getName(); - // } - //} - - @Override - public String getExpression() { - StringBuffer b = new StringBuffer(); - b.append("(class "); - b.append(getName()); - if(namespace != null) { - b.append(" (package "+namespace+")"); - } - for(FieldSchema f : fields) { - b.append(" "+f.getExpression()); - } - b.append(")"); - return b.toString(); - } - - public boolean equals(SpecificClassSchema o) { - return (namespace != null ? namespace.equals(o.getNamespace()) : o.getNamespace() == null) && - getName().equals(o.getName()); - } -} - diff --git a/java/src/main/java/org/msgpack/schema/DoubleSchema.java b/java/src/main/java/org/msgpack/schema/DoubleSchema.java deleted file mode 100644 index d53e47d5..00000000 --- a/java/src/main/java/org/msgpack/schema/DoubleSchema.java +++ /dev/null @@ -1,84 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.io.IOException; -import org.msgpack.*; - -public class DoubleSchema extends Schema { - public DoubleSchema() { - super("Double"); - } - - @Override - public String getExpression() { - return "double"; - } - - @Override - public void pack(Packer pk, Object obj) throws IOException { - if(obj instanceof Number) { - pk.packDouble( ((Number)obj).doubleValue() ); - - } else if(obj == null) { - pk.packNil(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object convert(Object obj) throws MessageTypeException { - if(obj instanceof Double) { - return obj; - - } else if(obj instanceof Number) { - return ((Number)obj).doubleValue(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object createFromByte(byte v) { - return (double)v; - } - - @Override - public Object createFromShort(short v) { - return (double)v; - } - - @Override - public Object createFromInt(int v) { - return (double)v; - } - - @Override - public Object createFromFloat(float v) { - return (double)v; - } - - @Override - public Object createFromDouble(double v) { - return (double)v; - } -} - diff --git a/java/src/main/java/org/msgpack/schema/FloatSchema.java b/java/src/main/java/org/msgpack/schema/FloatSchema.java deleted file mode 100644 index 27775218..00000000 --- a/java/src/main/java/org/msgpack/schema/FloatSchema.java +++ /dev/null @@ -1,84 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.io.IOException; -import org.msgpack.*; - -public class FloatSchema extends Schema { - public FloatSchema() { - super("Float"); - } - - @Override - public String getExpression() { - return "float"; - } - - @Override - public void pack(Packer pk, Object obj) throws IOException { - if(obj instanceof Number) { - pk.packFloat( ((Number)obj).floatValue() ); - - } else if(obj == null) { - pk.packNil(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object convert(Object obj) throws MessageTypeException { - if(obj instanceof Float) { - return obj; - - } else if(obj instanceof Number) { - return ((Number)obj).floatValue(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object createFromByte(byte v) { - return (float)v; - } - - @Override - public Object createFromShort(short v) { - return (float)v; - } - - @Override - public Object createFromInt(int v) { - return (float)v; - } - - @Override - public Object createFromFloat(float v) { - return (float)v; - } - - @Override - public Object createFromDouble(double v) { - return (float)v; - } -} - diff --git a/java/src/main/java/org/msgpack/schema/GenericClassSchema.java b/java/src/main/java/org/msgpack/schema/GenericClassSchema.java deleted file mode 100644 index ffdd4aba..00000000 --- a/java/src/main/java/org/msgpack/schema/GenericClassSchema.java +++ /dev/null @@ -1,91 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.HashMap; -import java.io.IOException; -import org.msgpack.*; - -public class GenericClassSchema extends ClassSchema { - public GenericClassSchema( - String name, String namespace, - List imports, List fields) { - super(name, namespace, imports, fields); - } - - @Override - @SuppressWarnings("unchecked") - public void pack(Packer pk, Object obj) throws IOException { - if(obj instanceof Map) { - Map d = (Map)obj; - pk.packArray(fields.length); - for(int i=0; i < fields.length; ++i) { - FieldSchema f = fields[i]; - f.getSchema().pack(pk, d.get(f.getName())); - } - - } else if(obj == null) { - pk.packNil(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object convert(Object obj) throws MessageTypeException { - if(obj instanceof Collection) { - // FIXME optimize - return createFromArray( ((Collection)obj).toArray() ); - - } else if(obj instanceof Map) { - HashMap m = new HashMap(fields.length); - Map d = (Map)obj; - for(int i=0; i < fields.length; ++i) { - FieldSchema f = fields[i]; - String fieldName = f.getName(); - m.put(fieldName, f.getSchema().convert(d.get(fieldName))); - } - return m; - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - public Schema getElementSchema(int index) { - // FIXME check index < fields.length - return fields[index].getSchema(); - } - - public Object createFromArray(Object[] obj) { - HashMap m = new HashMap(fields.length); - int i=0; - for(; i < obj.length; ++i) { - m.put(fields[i].getName(), obj[i]); - } - for(; i < fields.length; ++i) { - m.put(fields[i].getName(), null); - } - return m; - } -} - diff --git a/java/src/main/java/org/msgpack/schema/GenericSchema.java b/java/src/main/java/org/msgpack/schema/GenericSchema.java deleted file mode 100644 index 0adf8980..00000000 --- a/java/src/main/java/org/msgpack/schema/GenericSchema.java +++ /dev/null @@ -1,192 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.util.Arrays; -import java.util.List; -import java.util.HashMap; -import java.io.IOException; -import org.msgpack.*; -//import org.msgpack.generic.*; - -public class GenericSchema extends Schema implements IArraySchema, IMapSchema { - public GenericSchema() { - super("Object"); - } - - @Override - public String getExpression() { - return "object"; - } - - @Override - public void pack(Packer pk, Object obj) throws IOException { - pk.pack(obj); - } - - @Override - public Object convert(Object obj) throws MessageTypeException { - return obj; - } - - @Override - public Schema getElementSchema(int index) { - return this; - } - - @Override - public Schema getKeySchema() { - return this; - } - - @Override - public Schema getValueSchema() { - return this; - } - - @Override - public Object createFromNil() { - return null; - } - - @Override - public Object createFromBoolean(boolean v) { - return v; - } - - @Override - public Object createFromByte(byte v) { - return v; - } - - @Override - public Object createFromShort(short v) { - return v; - } - - @Override - public Object createFromInt(int v) { - return v; - } - - @Override - public Object createFromLong(long v) { - return v; - } - - @Override - public Object createFromFloat(float v) { - return v; - } - - @Override - public Object createFromDouble(double v) { - return v; - } - - @Override - public Object createFromRaw(byte[] b, int offset, int length) { - byte[] bytes = new byte[length]; - System.arraycopy(b, offset, bytes, 0, length); - return bytes; - } - - @Override - public Object createFromArray(Object[] obj) { - return Arrays.asList(obj); - } - - @Override - @SuppressWarnings("unchecked") - public Object createFromMap(Object[] obj) { - HashMap m = new HashMap(obj.length / 2); - int i = 0; - while(i < obj.length) { - Object k = obj[i++]; - Object v = obj[i++]; - m.put(k, v); - } - return m; - } - - /* - @Override - public Object createFromNil() { - return null; - } - - @Override - public Object createFromBoolean(boolean v) { - return new GenericBoolean(v); - } - - @Override - public Object createFromFromByte(byte v) { - return new GenericByte(v); - } - - @Override - public Object createFromShort(short v) { - return new GenericShort(v); - } - - @Override - public Object createFromInt(int v) { - return new GenericInt(v); - } - - @Override - public Object createFromLong(long v) { - return new GenericLong(v); - } - - @Override - public Object createFromFloat(float v) { - return new GenericFloat(v); - } - - @Override - public Object createFromDouble(double v) { - return new GenericDouble(v); - } - - @Override - public Object createFromRaw(byte[] b, int offset, int length) { - return new GenericRaw(b, offset, length); - } - - @Override - public Object createFromArray(Object[] obj) { - // FIXME GenericArray - return Arrays.asList(obj); - } - - @Override - public Object createFromMap(Object[] obj) { - GenericMap m = new GenericMap(obj.length / 2); - int i = 0; - while(i < obj.length) { - Object k = obj[i++]; - Object v = obj[i++]; - m.put(k, v); - } - return m; - } - */ -} - diff --git a/java/src/main/java/org/msgpack/schema/IMapSchema.java b/java/src/main/java/org/msgpack/schema/IMapSchema.java deleted file mode 100644 index 3a2f556c..00000000 --- a/java/src/main/java/org/msgpack/schema/IMapSchema.java +++ /dev/null @@ -1,27 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import org.msgpack.Schema; - -public interface IMapSchema { - public Schema getKeySchema(); - public Schema getValueSchema(); - public Object createFromMap(Object[] obj); -} - diff --git a/java/src/main/java/org/msgpack/schema/IntSchema.java b/java/src/main/java/org/msgpack/schema/IntSchema.java deleted file mode 100644 index 5a7e2815..00000000 --- a/java/src/main/java/org/msgpack/schema/IntSchema.java +++ /dev/null @@ -1,89 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.io.IOException; -import org.msgpack.*; - -public class IntSchema extends Schema { - public IntSchema() { - super("Integer"); - } - - @Override - public String getExpression() { - return "int"; - } - - @Override - public void pack(Packer pk, Object obj) throws IOException { - if(obj instanceof Number) { - pk.packInt( ((Number)obj).intValue() ); - - } else if(obj == null) { - pk.packNil(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object convert(Object obj) throws MessageTypeException { - if(obj instanceof Integer) { - return obj; - - } else if(obj instanceof Number) { - return ((Number)obj).intValue(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object createFromByte(byte v) { - return (int)v; - } - - @Override - public Object createFromShort(short v) { - return (int)v; - } - - @Override - public Object createFromInt(int v) { - return (int)v; - } - - @Override - public Object createFromLong(long v) { - return (int)v; - } - - @Override - public Object createFromFloat(float v) { - return (int)v; - } - - @Override - public Object createFromDouble(double v) { - return (int)v; - } -} - diff --git a/java/src/main/java/org/msgpack/schema/LongSchema.java b/java/src/main/java/org/msgpack/schema/LongSchema.java deleted file mode 100644 index 83a30e37..00000000 --- a/java/src/main/java/org/msgpack/schema/LongSchema.java +++ /dev/null @@ -1,89 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.io.IOException; -import org.msgpack.*; - -public class LongSchema extends Schema { - public LongSchema() { - super("Long"); - } - - @Override - public String getExpression() { - return "long"; - } - - @Override - public void pack(Packer pk, Object obj) throws IOException { - if(obj instanceof Number) { - pk.packLong( ((Number)obj).longValue() ); - - } else if(obj == null) { - pk.packNil(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object convert(Object obj) throws MessageTypeException { - if(obj instanceof Long) { - return obj; - - } else if(obj instanceof Number) { - return ((Number)obj).longValue(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object createFromByte(byte v) { - return (long)v; - } - - @Override - public Object createFromShort(short v) { - return (long)v; - } - - @Override - public Object createFromInt(int v) { - return (long)v; - } - - @Override - public Object createFromLong(long v) { - return (long)v; - } - - @Override - public Object createFromFloat(float v) { - return (long)v; - } - - @Override - public Object createFromDouble(double v) { - return (long)v; - } -} - diff --git a/java/src/main/java/org/msgpack/schema/MapSchema.java b/java/src/main/java/org/msgpack/schema/MapSchema.java deleted file mode 100644 index ba759939..00000000 --- a/java/src/main/java/org/msgpack/schema/MapSchema.java +++ /dev/null @@ -1,103 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.util.Map; -import java.util.HashMap; -import java.io.IOException; -import org.msgpack.*; - -public class MapSchema extends Schema implements IMapSchema { - private Schema keySchema; - private Schema valueSchema; - - public MapSchema(Schema keySchema, Schema valueSchema) { - super("map"); - this.keySchema = keySchema; - this.valueSchema = valueSchema; - } - - @Override - public String getFullName() { - return "HashList<"+keySchema.getFullName()+", "+valueSchema.getFullName()+">"; - } - - @Override - public String getExpression() { - return "(map "+keySchema.getExpression()+" "+valueSchema.getExpression()+")"; - } - - @Override - @SuppressWarnings("unchecked") - public void pack(Packer pk, Object obj) throws IOException { - if(obj instanceof Map) { - Map d = (Map)obj; - pk.packMap(d.size()); - for(Map.Entry e : d.entrySet()) { - keySchema.pack(pk, e.getKey()); - valueSchema.pack(pk, e.getValue()); - } - - } else if(obj == null) { - pk.packNil(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - @SuppressWarnings("unchecked") - public Object convert(Object obj) throws MessageTypeException { - if(obj instanceof Map) { - Map d = (Map)obj; - Map m = new HashMap(); - for(Map.Entry e : d.entrySet()) { - m.put(keySchema.convert(e.getKey()), valueSchema.convert(e.getValue())); - } - return m; - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Schema getKeySchema() { - return keySchema; - } - - @Override - public Schema getValueSchema() { - return valueSchema; - } - - @Override - @SuppressWarnings("unchecked") - public Object createFromMap(Object[] obj) { - HashMap m = new HashMap(obj.length / 2); - int i = 0; - while(i < obj.length) { - Object k = obj[i++]; - Object v = obj[i++]; - m.put(k, v); - } - return m; - } -} - diff --git a/java/src/main/java/org/msgpack/schema/RawSchema.java b/java/src/main/java/org/msgpack/schema/RawSchema.java deleted file mode 100644 index f621e4c4..00000000 --- a/java/src/main/java/org/msgpack/schema/RawSchema.java +++ /dev/null @@ -1,105 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.nio.ByteBuffer; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import org.msgpack.*; - -public class RawSchema extends Schema { - public RawSchema() { - super("raw"); - } - - public String getFullName() { - return "byte[]"; - } - - @Override - public void pack(Packer pk, Object obj) throws IOException { - // FIXME instanceof GenericObject - if(obj instanceof byte[]) { - byte[] d = (byte[])obj; - pk.packRaw(d.length); - pk.packRawBody(d); - - } else if(obj instanceof ByteBuffer) { - ByteBuffer d = (ByteBuffer)obj; - if(!d.hasArray()) { - throw MessageTypeException.invalidConvert(obj, this); - } - pk.packRaw(d.capacity()); - pk.packRawBody(d.array(), d.position(), d.capacity()); - - } else if(obj instanceof String) { - try { - byte[] d = ((String)obj).getBytes("UTF-8"); - pk.packRaw(d.length); - pk.packRawBody(d); - } catch (UnsupportedEncodingException e) { - throw MessageTypeException.invalidConvert(obj, this); - } - - } else if(obj == null) { - pk.packNil(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object convert(Object obj) throws MessageTypeException { - // FIXME instanceof GenericObject - if(obj instanceof byte[]) { - // FIXME copy? - //byte[] d = (byte[])obj; - //byte[] v = new byte[d.length]; - //System.arraycopy(d, 0, v, 0, d.length); - //return v; - return obj; - - } else if(obj instanceof ByteBuffer) { - ByteBuffer d = (ByteBuffer)obj; - byte[] v = new byte[d.capacity()]; - int pos = d.position(); - d.get(v); - d.position(pos); - return v; - - } else if(obj instanceof String) { - try { - return ((String)obj).getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - throw MessageTypeException.invalidConvert(obj, this); - } - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object createFromRaw(byte[] b, int offset, int length) { - byte[] d = new byte[length]; - System.arraycopy(b, offset, d, 0, length); - return d; - } -} - diff --git a/java/src/main/java/org/msgpack/schema/ReflectionClassSchema.java b/java/src/main/java/org/msgpack/schema/ReflectionClassSchema.java deleted file mode 100644 index fb94adfa..00000000 --- a/java/src/main/java/org/msgpack/schema/ReflectionClassSchema.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.msgpack.schema; - -import java.util.Arrays; -import java.util.List; -import java.lang.reflect.*; -import org.msgpack.*; - -// FIXME -public abstract class ReflectionClassSchema extends ClassSchema { - private Constructor constructorCache; - - public ReflectionClassSchema(String name, List fields, String namespace, List imports) { - super(name, namespace, imports, fields); - } - - /* - Schema getElementSchema(int index) - { - // FIXME check index < fields.length - fields[index].getSchema(); - } - - Object createFromArray(Object[] obj) - { - Object o = newInstance(); - ((MessageConvertable)o).messageConvert(obj); - return o; - } - - Object newInstance() - { - if(constructorCache == null) { - cacheConstructor(); - } - try { - return constructorCache.newInstance((Object[])null); - } catch (InvocationTargetException e) { - throw new RuntimeException("can't instantiate "+fqdn+": "+e.getMessage()); - } catch (InstantiationException e) { - throw new RuntimeException("can't instantiate "+fqdn+": "+e.getMessage()); - } catch (IllegalAccessException e) { - throw new RuntimeException("can't instantiate "+fqdn+": "+e.getMessage()); - } - } - - private void cacheConstructor() - { - try { - Class c = Class.forName(fqdn); - int index = 0; - for(SpecificFieldSchema f : fields) { - f.cacheField(c, index++); - } - constructorCache = c.getDeclaredConstructor((Class[])null); - constructorCache.setAccessible(true); - } catch(ClassNotFoundException e) { - throw new RuntimeException("class not found: "+fqdn); - } catch (NoSuchMethodException e) { - throw new RuntimeException("class not found: "+fqdn+": "+e.getMessage()); - } - } - */ -} - diff --git a/java/src/main/java/org/msgpack/schema/SSchemaParser.java b/java/src/main/java/org/msgpack/schema/SSchemaParser.java deleted file mode 100644 index 4ae8a4b5..00000000 --- a/java/src/main/java/org/msgpack/schema/SSchemaParser.java +++ /dev/null @@ -1,254 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Stack; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import org.msgpack.*; - -// FIXME exception class - -public class SSchemaParser { - public static Schema parse(String source) { - return new SSchemaParser(false).run(source); - } - - public static Schema load(String source) { - return new SSchemaParser(true).run(source); - } - - private static abstract class SExp { - boolean isAtom() { return false; } - public String getAtom() { return null; } - - boolean isTuple() { return false; } - public SExp getTuple(int i) { return null; } - public int size() { return 0; } - public boolean empty() { return size() == 0; } - Iterator iterator(int offset) { return null; } - } - - private static class SAtom extends SExp { - private String atom; - - SAtom(String atom) { this.atom = atom; } - - boolean isAtom() { return true; } - public String getAtom() { return atom; } - - public String toString() { return atom; } - } - - private static class STuple extends SExp { - private List tuple; - - STuple() { this.tuple = new ArrayList(); } - - public void add(SExp e) { tuple.add(e); } - - boolean isTuple() { return true; } - public SExp getTuple(int i) { return tuple.get(i); } - public int size() { return tuple.size(); } - - Iterator iterator(int skip) { - Iterator i = tuple.iterator(); - for(int s=0; s < skip; ++s) { i.next(); } - return i; - } - - public String toString() { - if(tuple.isEmpty()) { return "()"; } - Iterator i = tuple.iterator(); - StringBuffer o = new StringBuffer(); - o.append("(").append(i.next()); - while(i.hasNext()) { o.append(" ").append(i.next()); } - o.append(")"); - return o.toString(); - } - } - - boolean specificClass; - - private SSchemaParser(boolean specificClass) { - this.specificClass = specificClass; - } - - private static Pattern pattern = Pattern.compile( - "(?:\\s+)|([\\(\\)]|[\\d\\w\\.]+)"); - - private Schema run(String source) { - Matcher m = pattern.matcher(source); - - Stack stack = new Stack(); - String token; - - while(true) { - while(true) { - if(!m.find()) { throw new RuntimeException("unexpected end of file"); } - token = m.group(1); - if(token != null) { break; } - } - - if(token.equals("(")) { - stack.push(new STuple()); - } else if(token.equals(")")) { - STuple top = stack.pop(); - if(stack.empty()) { - stack.push(top); - break; - } - stack.peek().add(top); - } else { - if(stack.empty()) { - throw new RuntimeException("unexpected token '"+token+"'"); - } - stack.peek().add(new SAtom(token)); - } - } - - while(true) { - if(!m.find()) { break; } - token = m.group(1); - if(token != null) { throw new RuntimeException("unexpected token '"+token+"'"); } - } - - return readType( stack.pop() ); - } - - private Schema readType(SExp exp) { - if(exp.isAtom()) { - String type = exp.getAtom(); - if(type.equals("string")) { - return new StringSchema(); - } else if(type.equals("raw")) { - return new RawSchema(); - } else if(type.equals("byte")) { - return new ByteSchema(); - } else if(type.equals("short")) { - return new ShortSchema(); - } else if(type.equals("int")) { - return new IntSchema(); - } else if(type.equals("long")) { - return new LongSchema(); - } else if(type.equals("float")) { - return new FloatSchema(); - } else if(type.equals("double")) { - return new DoubleSchema(); - } else if(type.equals("object")) { - return new GenericSchema(); - } else { - throw new RuntimeException("byte, short, int, long, float, double, raw, string or object is expected but got '"+type+"': "+exp); - } - } else { - String type = exp.getTuple(0).getAtom(); - if(type.equals("class")) { - return parseClass(exp); - } else if(type.equals("array")) { - return parseArray(exp); - } else if(type.equals("map")) { - return parseMap(exp); - } else { - throw new RuntimeException("class, array or map is expected but got '"+type+"': "+exp); - } - } - } - - private ClassSchema parseClass(SExp exp) { - if(exp.size() < 3 || !exp.getTuple(1).isAtom()) { - throw new RuntimeException("class is (class NAME CLASS_BODY): "+exp); - } - - String namespace = null; - List imports = new ArrayList(); - String name = exp.getTuple(1).getAtom(); - List fields = new ArrayList(); - - for(Iterator i=exp.iterator(2); i.hasNext();) { - SExp subexp = i.next(); - if(!subexp.isTuple() || subexp.empty() || !subexp.getTuple(0).isAtom()) { - throw new RuntimeException("field, package or import is expected: "+subexp); - } - String type = subexp.getTuple(0).getAtom(); - if(type.equals("field")) { - fields.add( parseField(subexp) ); - } else if(type.equals("package")) { - if(namespace != null) { - throw new RuntimeException("duplicated package definition: "+subexp); - } - namespace = parseNamespace(subexp); - } else if(type.equals("import")) { - imports.add( parseImport(subexp) ); - } else { - throw new RuntimeException("field, package or import is expected but got '"+type+"': "+subexp); - } - } - - if(specificClass) { - return new SpecificClassSchema(name, namespace, imports, fields); - } else { - return new GenericClassSchema(name, namespace, imports, fields); - } - } - - private ArraySchema parseArray(SExp exp) { - if(exp.size() != 2) { - throw new RuntimeException("array is (array ELEMENT_TYPE): "+exp); - } - Schema elementType = readType(exp.getTuple(1)); - return new ArraySchema(elementType); - } - - private MapSchema parseMap(SExp exp) { - if(exp.size() != 3 || !exp.getTuple(1).isAtom()) { - throw new RuntimeException("map is (map KEY_TYPE VALUE_TYPE): "+exp); - } - Schema keyType = readType(exp.getTuple(1)); - Schema valueType = readType(exp.getTuple(2)); - return new MapSchema(keyType, valueType); - } - - private String parseNamespace(SExp exp) { - if(exp.size() != 2 || !exp.getTuple(1).isAtom()) { - throw new RuntimeException("package is (package NAME): "+exp); - } - String name = exp.getTuple(1).getAtom(); - return name; - } - - private String parseImport(SExp exp) { - if(exp.size() != 2 || !exp.getTuple(1).isAtom()) { - throw new RuntimeException("import is (import NAME): "+exp); - } - String name = exp.getTuple(1).getAtom(); - return name; - } - - private FieldSchema parseField(SExp exp) { - if(exp.size() != 3 || !exp.getTuple(1).isAtom()) { - throw new RuntimeException("field is (field NAME TYPE): "+exp); - } - String name = exp.getTuple(1).getAtom(); - Schema type = readType(exp.getTuple(2)); - return new FieldSchema(name, type); - } -} - diff --git a/java/src/main/java/org/msgpack/schema/ShortSchema.java b/java/src/main/java/org/msgpack/schema/ShortSchema.java deleted file mode 100644 index f32ab411..00000000 --- a/java/src/main/java/org/msgpack/schema/ShortSchema.java +++ /dev/null @@ -1,89 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.io.IOException; -import org.msgpack.*; - -public class ShortSchema extends Schema { - public ShortSchema() { - super("Short"); - } - - @Override - public String getExpression() { - return "short"; - } - - @Override - public void pack(Packer pk, Object obj) throws IOException { - if(obj instanceof Number) { - pk.packShort( ((Number)obj).shortValue() ); - - } else if(obj == null) { - pk.packNil(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object convert(Object obj) throws MessageTypeException { - if(obj instanceof Short) { - return obj; - - } else if(obj instanceof Number) { - return ((Number)obj).shortValue(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object createFromByte(byte v) { - return (short)v; - } - - @Override - public Object createFromShort(short v) { - return (short)v; - } - - @Override - public Object createFromInt(int v) { - return (short)v; - } - - @Override - public Object createFromLong(long v) { - return (short)v; - } - - @Override - public Object createFromFloat(float v) { - return (short)v; - } - - @Override - public Object createFromDouble(double v) { - return (short)v; - } -} - diff --git a/java/src/main/java/org/msgpack/schema/SpecificClassSchema.java b/java/src/main/java/org/msgpack/schema/SpecificClassSchema.java deleted file mode 100644 index 850f6218..00000000 --- a/java/src/main/java/org/msgpack/schema/SpecificClassSchema.java +++ /dev/null @@ -1,122 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.util.Collection; -import java.util.List; -import java.lang.reflect.*; -import java.io.IOException; -import org.msgpack.*; - -public class SpecificClassSchema extends ClassSchema { - private Class classCache; - private Method factoryCache; - private Constructor constructorCache; - - public SpecificClassSchema( - String name, String namespace, - List imports, List fields) { - super(name, namespace, imports, fields); - } - - @Override - @SuppressWarnings("unchecked") - public void pack(Packer pk, Object obj) throws IOException { - if(obj == null) { - pk.packNil(); - return; - } - if(classCache == null) { - cacheFactory(); - } - if(classCache.isInstance(obj)) { - ((MessagePackable)obj).messagePack(pk); - } else { - // FIXME Map - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object convert(Object obj) throws MessageTypeException { - if(obj instanceof Collection) { - if(constructorCache == null) { - cacheConstructor(); - } - try { - MessageConvertable o = (MessageConvertable)constructorCache.newInstance((Object[])null); - o.messageConvert(obj); - return o; - } catch (InvocationTargetException e) { - throw new RuntimeException("can't instantiate "+fqdn+": "+e.getMessage()); - } catch (InstantiationException e) { - throw new RuntimeException("can't instantiate "+fqdn+": "+e.getMessage()); - } catch (IllegalAccessException e) { - throw new RuntimeException("can't instantiate "+fqdn+": "+e.getMessage()); - } - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - public Schema getElementSchema(int index) { - // FIXME check index < fields.length - return fields[index].getSchema(); - } - - public Object createFromArray(Object[] obj) { - if(factoryCache == null) { - cacheFactory(); - } - try { - return factoryCache.invoke(null, new Object[]{obj}); - } catch (InvocationTargetException e) { - throw new RuntimeException("can't instantiate "+fqdn+": "+e.getCause()); - } catch (IllegalAccessException e) { - throw new RuntimeException("can't instantiate "+fqdn+": "+e.getMessage()); - } - } - - @SuppressWarnings("unchecked") - private void cacheFactory() { - try { - classCache = Class.forName(fqdn); - factoryCache = classCache.getDeclaredMethod("createFromMessage", new Class[]{Object[].class}); - factoryCache.setAccessible(true); - } catch(ClassNotFoundException e) { - throw new RuntimeException("class not found: "+fqdn); - } catch (NoSuchMethodException e) { - throw new RuntimeException("class not found: "+fqdn+": "+e.getMessage()); - } - } - - @SuppressWarnings("unchecked") - private void cacheConstructor() { - try { - classCache = Class.forName(fqdn); - constructorCache = classCache.getDeclaredConstructor((Class[])null); - constructorCache.setAccessible(true); - } catch(ClassNotFoundException e) { - throw new RuntimeException("class not found: "+fqdn); - } catch (NoSuchMethodException e) { - throw new RuntimeException("class not found: "+fqdn+": "+e.getMessage()); - } - } -} - diff --git a/java/src/main/java/org/msgpack/schema/StringSchema.java b/java/src/main/java/org/msgpack/schema/StringSchema.java deleted file mode 100644 index 46d515b4..00000000 --- a/java/src/main/java/org/msgpack/schema/StringSchema.java +++ /dev/null @@ -1,111 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -package org.msgpack.schema; - -import java.nio.ByteBuffer; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import org.msgpack.*; - -public class StringSchema extends Schema { - public StringSchema() { - super("string"); - } - - @Override - public String getFullName() { - return "String"; - } - - @Override - public void pack(Packer pk, Object obj) throws IOException { - // FIXME instanceof GenericObject - if(obj instanceof String) { - try { - byte[] d = ((String)obj).getBytes("UTF-8"); - pk.packRaw(d.length); - pk.packRawBody(d); - } catch (UnsupportedEncodingException e) { - throw MessageTypeException.invalidConvert(obj, this); - } - - } else if(obj instanceof byte[]) { - byte[] d = (byte[])obj; - pk.packRaw(d.length); - pk.packRawBody(d); - - } else if(obj instanceof ByteBuffer) { - ByteBuffer d = (ByteBuffer)obj; - if(!d.hasArray()) { - throw MessageTypeException.invalidConvert(obj, this); - } - pk.packRaw(d.capacity()); - pk.packRawBody(d.array(), d.position(), d.capacity()); - - } else if(obj == null) { - pk.packNil(); - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object convert(Object obj) throws MessageTypeException { - // FIXME instanceof GenericObject - if(obj instanceof String) { - return obj; - - } else if(obj instanceof byte[]) { - try { - return new String((byte[])obj, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw MessageTypeException.invalidConvert(obj, this); - } - - } else if(obj instanceof ByteBuffer) { - ByteBuffer d = (ByteBuffer)obj; - try { - if(d.hasArray()) { - return new String(d.array(), d.position(), d.capacity(), "UTF-8"); - } else { - byte[] v = new byte[d.capacity()]; - int pos = d.position(); - d.get(v); - d.position(pos); - return new String(v, "UTF-8"); - } - } catch (UnsupportedEncodingException e) { - throw MessageTypeException.invalidConvert(obj, this); - } - - } else { - throw MessageTypeException.invalidConvert(obj, this); - } - } - - @Override - public Object createFromRaw(byte[] b, int offset, int length) { - try { - return new String(b, offset, length, "UTF-8"); - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); - } - } -} - diff --git a/java/src/main/java/org/msgpack/util/annotation/MessagePackOptional.java b/java/src/main/java/org/msgpack/util/annotation/MessagePackOptional.java new file mode 100644 index 00000000..a565292d --- /dev/null +++ b/java/src/main/java/org/msgpack/util/annotation/MessagePackOptional.java @@ -0,0 +1,12 @@ +package org.msgpack.util.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.CLASS) +public @interface MessagePackOptional { + int value() default -1; +} \ No newline at end of file diff --git a/java/src/main/java/org/msgpack/util/annotation/MessagePackRequired.java b/java/src/main/java/org/msgpack/util/annotation/MessagePackRequired.java new file mode 100644 index 00000000..03e50cf3 --- /dev/null +++ b/java/src/main/java/org/msgpack/util/annotation/MessagePackRequired.java @@ -0,0 +1,12 @@ +package org.msgpack.util.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.CLASS) +public @interface MessagePackRequired { + int value() default -1; +} \ No newline at end of file diff --git a/java/src/main/java/org/msgpack/util/annotation/MessagePackUnpackable.java b/java/src/main/java/org/msgpack/util/annotation/MessagePackUnpackable.java new file mode 100644 index 00000000..77f6e6de --- /dev/null +++ b/java/src/main/java/org/msgpack/util/annotation/MessagePackUnpackable.java @@ -0,0 +1,11 @@ +package org.msgpack.util.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface MessagePackUnpackable { +} diff --git a/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java b/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java new file mode 100644 index 00000000..0aeb6d87 --- /dev/null +++ b/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java @@ -0,0 +1,1372 @@ +package org.msgpack.util.annotation; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtMethod; +import javassist.CtNewConstructor; +import javassist.CtNewMethod; +import javassist.NotFoundException; + +import org.msgpack.MessageConvertable; +import org.msgpack.MessagePackObject; +import org.msgpack.MessagePackable; +import org.msgpack.MessageTypeException; +import org.msgpack.MessageUnpackable; +import org.msgpack.Packer; +import org.msgpack.Unpacker; + +public class PackUnpackUtil { + static class Constants { + static final String POSTFIX_TYPE_NAME_ENHANCER = "_$$_Enhanced"; + + static final String KEYWORD_MODIFIER_PUBLIC = "public"; + + static final String KEYWORD_FOR = "for"; + + static final String KEYWORD_IF = "if"; + + static final String KEYWORD_THROWS = "throws"; + + static final String KEYWORD_NEW = "new"; + + static final String KEYWORD_NULL = "null"; + + static final String CHAR_NAME_SPACE = " "; + + static final String CHAR_NAME_COMMA = ","; + + static final String CHAR_NAME_EQUAL = "="; + + static final String CHAR_NAME_PLUS = "+"; + + static final String CHAR_NAME_LESSTHAN = "<"; + + static final String CHAR_NAME_RIGHT_PARENTHESIS = ")"; + + static final String CHAR_NAME_LEFT_PARENTHESIS = "("; + + static final String CHAR_NAME_RIGHT_CURLY_BRACHET = "}"; + + static final String CHAR_NAME_LEFT_CURLY_BRACHET = "{"; + + static final String CHAR_NAME_RIGHT_SQUARE_BRACKET = "]"; + + static final String CHAR_NAME_LEFT_SQUARE_BRACKET = "["; + + static final String CHAR_NAME_DOT = "."; + + static final String CHAR_NAME_SEMICOLON = ";"; + + static final String VARIABLE_NAME_PK = "_$$_pk"; + + static final String VARIABLE_NAME_SIZE = "_$$_len"; + + static final String VARIABLE_NAME_ARRAY = "_$$_ary"; + + static final String VARIABLE_NAME_LIST = "_$$_list"; + + static final String VARIABLE_NAME_MAP = "_$$_map"; + + static final String VARIABLE_NAME_KEY = "_$$_key"; + + static final String VARIABLE_NAME_VAL = "_$$_val"; + + static final String VARIABLE_NAME_ITER = "_$$_iter"; + + static final String VARIABLE_NAME_MPO = "_$$_mpo"; + + static final String VARIABLE_NAME_I = "i"; + + static final String METHOD_NAME_VALUEOF = "valueOf"; + + static final String METHOD_NAME_ADD = "add"; + + static final String METHOD_NAME_PUT = "put"; + + static final String METHOD_NAME_GET = "get"; + + static final String METHOD_NAME_SIZE = "size"; + + static final String METHOD_NAME_KEYSET = "keySet"; + + static final String METHOD_NAME_ITERATOR = "iterator"; + + static final String METHOD_NAME_HASNEXT = "hasNext"; + + static final String METHOD_NAME_NEXT = "next"; + + static final String METHOD_NAME_MSGPACK = "messagePack"; + + static final String METHOD_NAME_MSGUNPACK = "messageUnpack"; + + static final String METHOD_NAME_MSGCONVERT = "messageConvert"; + + static final String METHOD_NAME_PACK = "pack"; + + static final String METHOD_NAME_PACKARRAY = "packArray"; + + static final String METHOD_NAME_UNPACK = "unpack"; + + static final String METHOD_NAME_UNPACKBOOLEAN = "unpackBoolean"; + + static final String METHOD_NAME_UNPACKBYTE = "unpackByte"; + + static final String METHOD_NAME_UNPACKDOUBLE = "unpackDouble"; + + static final String METHOD_NAME_UNPACKFLOAT = "unpackFloat"; + + static final String METHOD_NAME_UNPACKINT = "unpackInt"; + + static final String METHOD_NAME_UNPACKLONG = "unpackLong"; + + static final String METHOD_NAME_UNPACKSHORT = "unpackShort"; + + static final String METHOD_NAME_UNPACKSTRING = "unpackString"; + + static final String METHOD_NAME_UNPACKBIGINTEGER = "unpackBigInteger"; + + static final String METHOD_NAME_UNPACKOBJECT = "unpackObject"; + + static final String METHOD_NAME_UNPACKBYTEARRAY = "unpackByteArray"; + + static final String METHOD_NAME_UNPACKARRAY = "unpackArray"; + + static final String METHOD_NAME_UNPACKMAP = "unpackMap"; + + static final String METHOD_NAME_ASARRAY = "asArray"; + + static final String METHOD_NAME_ASBOOLEAN = "asBoolean"; + + static final String METHOD_NAME_ASBYTE = "asByte"; + + static final String METHOD_NAME_ASSHORT = "asShort"; + + static final String METHOD_NAME_ASINT = "asInt"; + + static final String METHOD_NAME_ASFLOAT = "asFloat"; + + static final String METHOD_NAME_ASLONG = "asLong"; + + static final String METHOD_NAME_ASDOUBLE = "asDouble"; + + static final String METHOD_NAME_ASSTRING = "asString"; + + static final String METHOD_NAME_ASBYTEARRAY = "asByteArray"; + + static final String METHOD_NAME_ASBIGINTEGER = "asBigInteger"; + } + + public static class Enhancer { + + private ConcurrentHashMap> classCache; + + private ClassPool pool; + + protected Enhancer() { + classCache = new ConcurrentHashMap>(); + pool = ClassPool.getDefault(); + } + + protected Class getCache(String origName) { + return classCache.get(origName); + } + + protected void setCache(String origName, Class enhClass) { + classCache.putIfAbsent(origName, enhClass); + } + + protected Class generate(Class origClass, boolean packUnpackable) + throws NotFoundException, CannotCompileException { + String origName = origClass.getName(); + String enhName = origName + Constants.POSTFIX_TYPE_NAME_ENHANCER; + CtClass origCtClass = pool.get(origName); + checkClassValidation(origClass, packUnpackable); + checkDefaultConstructorValidation(origClass); + CtClass enhCtClass = pool.makeClass(enhName); + setSuperclass(enhCtClass, origCtClass); + setInterfaces(enhCtClass); + addConstructor(enhCtClass); + Field[] fields = getDeclaredFields(origClass); + addMessagePackMethod(enhCtClass, fields); + addMessageUnpackMethod(enhCtClass, fields); + addMessageConvertMethod(enhCtClass, fields); + return createClass(enhCtClass); + } + + private void checkClassValidation(Class origClass, + boolean packUnpackable) { + // not public, abstract, final + int mod = origClass.getModifiers(); + if ((!(Modifier.isPublic(mod) || Modifier.isProtected(mod))) + || Modifier.isAbstract(mod) || Modifier.isFinal(mod)) { + throwClassValidationException(origClass); + } + // interface, enum + if (origClass.isInterface() || origClass.isEnum()) { + throwClassValidationException(origClass); + } + // annotation + if (!packUnpackable) { + checkPackUnpackAnnotation(origClass); + } + } + + private static void throwClassValidationException(Class origClass) { + throw new PackUnpackUtilException( + "it must be a public class and have @" + + MessagePackUnpackable.class.getName() + ": " + + origClass.getName()); + } + + private void checkPackUnpackAnnotation(Class origClass) { + Annotation anno = origClass + .getAnnotation(MessagePackUnpackable.class); + if (anno == null) { + throwClassValidationException(origClass); + } + } + + private void checkDefaultConstructorValidation(Class origClass) { + Constructor cons = null; + try { + cons = origClass.getDeclaredConstructor(new Class[0]); + } catch (Exception e1) { + throwConstructoValidationException(origClass); + } + + int mod = cons.getModifiers(); + if (!(Modifier.isPublic(mod) || Modifier.isProtected(mod))) { + throwConstructoValidationException(origClass); + } + } + + private static void throwConstructoValidationException( + Class origClass) { + throw new PackUnpackUtilException( + "it must have a public zero-argument constructor: " + + origClass.getName()); + } + + private void setSuperclass(CtClass enhCtClass, CtClass origCtClass) + throws CannotCompileException { + enhCtClass.setSuperclass(origCtClass); + } + + private void setInterfaces(CtClass enhCtClass) throws NotFoundException { + CtClass pacCtClass = pool.get(MessagePackable.class.getName()); + enhCtClass.addInterface(pacCtClass); + CtClass unpacCtClass = pool.get(MessageUnpackable.class.getName()); + enhCtClass.addInterface(unpacCtClass); + CtClass convCtClass = pool.get(MessageConvertable.class.getName()); + enhCtClass.addInterface(convCtClass); + } + + private void addConstructor(CtClass enhCtClass) + throws CannotCompileException { + CtConstructor newCtCons = CtNewConstructor + .defaultConstructor(enhCtClass); + enhCtClass.addConstructor(newCtCons); + } + + private Field[] getDeclaredFields(Class origClass) { + ArrayList allFields = new ArrayList(); + Class nextClass = origClass; + while (nextClass != Object.class) { + Field[] fields = nextClass.getDeclaredFields(); + for (Field field : fields) { + try { + checkFieldValidation(field, allFields); + allFields.add(field); + } catch (PackUnpackUtilException e) { // ignore + } + } + nextClass = nextClass.getSuperclass(); + } + return allFields.toArray(new Field[0]); + } + + private void checkFieldValidation(Field field, List fields) { + // check modifiers (public or protected) + int mod = field.getModifiers(); + if ((!(Modifier.isPublic(mod) || Modifier.isProtected(mod))) + || Modifier.isStatic(mod) || Modifier.isFinal(mod) + || Modifier.isTransient(mod) || field.isSynthetic()) { + throwFieldValidationException(field); + } + // check same name + for (Field f : fields) { + if (f.getName().equals(field.getName())) { + throwFieldValidationException(field); + } + } + } + + private static void throwFieldValidationException(Field field) { + throw new PackUnpackUtilException("it must be a public field: " + + field.getName()); + } + + private void addMessagePackMethod(CtClass enhCtClass, Field[] fields) + throws CannotCompileException, NotFoundException { + StringBuilder sb = new StringBuilder(); + sb.append(Constants.KEYWORD_MODIFIER_PUBLIC); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(void.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.METHOD_NAME_MSGPACK); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Packer.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_PK); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.KEYWORD_THROWS); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(IOException.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_PK); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_PACKARRAY); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(fields.length); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + for (Field field : fields) { + insertCodeOfMessagePackCall(sb, field); + } + sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET); + // System.out.println("messagePack method: " + sb.toString()); + CtMethod newCtMethod = CtNewMethod.make(sb.toString(), enhCtClass); + enhCtClass.addMethod(newCtMethod); + } + + private void insertCodeOfMessagePackCall(StringBuilder sb, Field field) { + sb.append(Constants.VARIABLE_NAME_PK); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_PACK); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(field.getName()); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + } + + private void addMessageUnpackMethod(CtClass enhCtClass, Field[] fields) + throws CannotCompileException, NotFoundException { + StringBuilder sb = new StringBuilder(); + sb.append(Constants.KEYWORD_MODIFIER_PUBLIC); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(void.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.METHOD_NAME_MSGUNPACK); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Unpacker.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_PK); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.KEYWORD_THROWS); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(MessageTypeException.class.getName()); + sb.append(Constants.CHAR_NAME_COMMA); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(IOException.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_PK); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_UNPACKARRAY); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + insertCodeOfMessageUnpackCalls(sb, fields); + sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET); + // System.out.println("messageUnpack method: " + sb.toString()); + CtMethod newCtMethod = CtNewMethod.make(sb.toString(), enhCtClass); + enhCtClass.addMethod(newCtMethod); + } + + private void insertCodeOfMessageUnpackCalls(StringBuilder sb, + Field[] fields) throws NotFoundException { + for (Field field : fields) { + insertCodeOfMessageUnpackCall(sb, field, field.getType()); + } + } + + private void insertCodeOfMessageUnpackCall(StringBuilder sb, Field f, + Class c) throws NotFoundException { + if (c.isPrimitive()) { + // primitive type + insertCodeOfMessageUnpackCallForPrimTypes(sb, f, c); + } else if (c.equals(Boolean.class) || // Boolean + c.equals(Byte.class) || // Byte + c.equals(Double.class) || // Double + c.equals(Float.class) || // Float + c.equals(Integer.class) || // Integer + c.equals(Long.class) || // Long + c.equals(Short.class)) { // Short + // reference type (wrapper type) + insertCodeOfMessageUnpackCallForWrapTypes(sb, f, c); + } else if (c.equals(BigInteger.class) || // BigInteger + c.equals(String.class) || // String + c.equals(byte[].class)) { // byte[] + // reference type (other type) + insertCodeOfMessageUnpackCallForPrimTypes(sb, f, c); + } else if (List.class.isAssignableFrom(c)) { + // List + insertCodeOfMessageUnpackCallForListType(sb, f, c); + } else if (Map.class.isAssignableFrom(c)) { + // Map + insertCodeOfMessageUnpackCallForMapType(sb, f, c); + } else if (getCache(c.getName()) != null) { + // cached + insertCodeOfMessageUnpackCallForEnhancedType(sb, f, c); + } else if (MessageUnpackable.class.isAssignableFrom(c)) { + // MessageUnpackable + insertCodeOfMessageUnpackCallForMsgUnpackableType(sb, f, c); + } else { + throw new NotFoundException("unknown type: " + c.getName()); + } + } + + private void insertCodeOfMessageUnpackCallForPrimTypes( + StringBuilder sb, Field field, Class type) + throws NotFoundException { + // insert a right variable + if (field != null) { + sb.append(field.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + } + sb.append(Constants.VARIABLE_NAME_PK); + sb.append(Constants.CHAR_NAME_DOT); + // insert unpack method + if (type.equals(boolean.class)) { // boolean + sb.append(Constants.METHOD_NAME_UNPACKBOOLEAN); + } else if (type.equals(byte.class)) { // byte + sb.append(Constants.METHOD_NAME_UNPACKBYTE); + } else if (type.equals(double.class)) { // double + sb.append(Constants.METHOD_NAME_UNPACKDOUBLE); + } else if (type.equals(float.class)) { // float + sb.append(Constants.METHOD_NAME_UNPACKFLOAT); + } else if (type.equals(int.class)) { // int + sb.append(Constants.METHOD_NAME_UNPACKINT); + } else if (type.equals(long.class)) { // long + sb.append(Constants.METHOD_NAME_UNPACKLONG); + } else if (type.equals(short.class)) { // short + sb.append(Constants.METHOD_NAME_UNPACKSHORT); + } else { // reference type + if (type.equals(BigInteger.class)) { // BigInteger + sb.append(Constants.METHOD_NAME_UNPACKBIGINTEGER); + } else if (type.equals(String.class)) { // String + sb.append(Constants.METHOD_NAME_UNPACKSTRING); + } else if (type.equals(byte[].class)) { // byte[] + sb.append(Constants.METHOD_NAME_UNPACKBYTEARRAY); + } else { + throw new NotFoundException("unknown type: " + + type.getName()); + } + } + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + if (field != null) { + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + } + } + + private void insertCodeOfMessageUnpackCallForWrapTypes( + StringBuilder sb, Field field, Class type) + throws NotFoundException { + // insert a right variable + if (field != null) { + sb.append(field.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + } + sb.append(type.getName()); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_VALUEOF); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.VARIABLE_NAME_PK); + sb.append(Constants.CHAR_NAME_DOT); + // insert the name of a unpack method + if (type.equals(Boolean.class)) { // Boolean + sb.append(Constants.METHOD_NAME_UNPACKBOOLEAN); + } else if (type.equals(Byte.class)) { // Byte + sb.append(Constants.METHOD_NAME_UNPACKBYTE); + } else if (type.equals(Double.class)) { // Double + sb.append(Constants.METHOD_NAME_UNPACKDOUBLE); + } else if (type.equals(Float.class)) { // Float + sb.append(Constants.METHOD_NAME_UNPACKFLOAT); + } else if (type.equals(Integer.class)) { // Integer + sb.append(Constants.METHOD_NAME_UNPACKINT); + } else if (type.equals(Long.class)) { // Long + sb.append(Constants.METHOD_NAME_UNPACKLONG); + } else if (type.equals(Short.class)) { // Short + sb.append(Constants.METHOD_NAME_UNPACKSHORT); + } else { + throw new NotFoundException("unknown type: " + type.getName()); + } + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + if (field != null) { + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + } + } + + private void insertCodeOfMessageUnpackCallForListType(StringBuilder sb, + Field field, Class type) throws NotFoundException { + ParameterizedType generic = (ParameterizedType) field + .getGenericType(); + Class genericType = (Class) generic.getActualTypeArguments()[0]; + + // len + sb.append(int.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_SIZE); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_PK); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_UNPACKARRAY); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + // field initializer + sb.append(field.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.KEYWORD_NEW); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(ArrayList.class.getName()); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + // for loop + sb.append(Constants.KEYWORD_FOR); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(int.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_I); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(0); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_I); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LESSTHAN); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_SIZE); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_PLUS); + sb.append(Constants.CHAR_NAME_PLUS); + sb.append(Constants.VARIABLE_NAME_I); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + + // block + sb.append(field.getName()); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_ADD); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + insertCodeOfMessageUnpackCall(sb, null, genericType); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + } + + private void insertCodeOfMessageUnpackCallForMapType(StringBuilder sb, + Field field, Class type) throws NotFoundException { + ParameterizedType generic = (ParameterizedType) field + .getGenericType(); + Class genericType0 = (Class) generic.getActualTypeArguments()[0]; + Class genericType1 = (Class) generic.getActualTypeArguments()[1]; + + // len + sb.append(int.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_SIZE); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_PK); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_UNPACKMAP); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + // field initializer + sb.append(field.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.KEYWORD_NEW); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(HashMap.class.getName()); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + // for loop + sb.append(Constants.KEYWORD_FOR); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(int.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_I); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(0); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_I); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LESSTHAN); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_SIZE); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_PLUS); + sb.append(Constants.CHAR_NAME_PLUS); + sb.append(Constants.VARIABLE_NAME_I); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + + // block map. + sb.append(field.getName()); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_PUT); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + insertCodeOfMessageUnpackCall(sb, null, genericType0); + sb.append(Constants.CHAR_NAME_COMMA); + sb.append(Constants.CHAR_NAME_SPACE); + insertCodeOfMessageUnpackCall(sb, null, genericType1); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + } + + private void insertCodeOfMessageUnpackCallForEnhancedType( + StringBuilder sb, Field f, Class c) { + c = this.getCache(c.getName()); + insertCodeOfMessageUnpackCallForMsgUnpackableType(sb, f, c); + } + + private void insertCodeOfMessageUnpackCallForMsgUnpackableType( + StringBuilder sb, Field f, Class c) { + // if (fi == null) { fi = new Foo_$$_Enhanced(); } + sb.append(Constants.KEYWORD_IF); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(f.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.KEYWORD_NULL); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(f.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.KEYWORD_NEW); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(c.getName()); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + + // insert a right variable // ignore + sb.append(Constants.VARIABLE_NAME_PK); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_UNPACK); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(MessageUnpackable.class.getName()); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(f.getName()); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + } + + private void addMessageConvertMethod(CtClass enhCtClass, Field[] fields) + throws CannotCompileException { + // messageConvert(MessagePackObject obj) throws MessageTypeException + StringBuilder sb = new StringBuilder(); + sb.append(Constants.KEYWORD_MODIFIER_PUBLIC); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(void.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.METHOD_NAME_MSGCONVERT); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(MessagePackObject.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_MPO); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.KEYWORD_THROWS); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(MessageTypeException.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + insertCodeOfMessagePackObjectArrayGet(sb); + insertCodeOfMesageConvertCalls(sb, fields); + sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET); + // System.out.println("messageConvert method: " + sb.toString()); + CtMethod newCtMethod = CtNewMethod.make(sb.toString(), enhCtClass); + enhCtClass.addMethod(newCtMethod); + } + + private void insertCodeOfMessagePackObjectArrayGet(StringBuilder sb) { + // MessagePackObject[] ary = obj.asArray(); + sb.append(MessagePackObject.class.getName()); + sb.append(Constants.CHAR_NAME_LEFT_SQUARE_BRACKET); + sb.append(Constants.CHAR_NAME_RIGHT_SQUARE_BRACKET); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_ARRAY); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_MPO); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_ASARRAY); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + } + + private void insertCodeOfMesageConvertCalls(StringBuilder sb, + Field[] fields) { + for (int i = 0; i < fields.length; ++i) { + insertCodeOfMessageConvertCall(sb, fields[i], fields[i] + .getType(), i, null); + } + } + + private void insertCodeOfMessageConvertCall(StringBuilder sb, Field f, + Class c, int i, String v) { + if (c.isPrimitive()) { // primitive type + insertCodeOfMessageConvertCallForPrimTypes(sb, f, c, i, v); + } else { // reference type + if (c.equals(Boolean.class) || c.equals(Byte.class) + || c.equals(Short.class) || c.equals(Integer.class) + || c.equals(Float.class) || c.equals(Long.class) + || c.equals(Double.class)) { + // wrapper type + insertCodeOfMessageConvertCallForWrapTypes(sb, f, c, i, v); + } else if (c.equals(String.class) || c.equals(byte[].class) + || c.equals(BigInteger.class)) { + insertCodeOfMessageConvertCallForPrimTypes(sb, f, c, i, v); + } else if (List.class.isAssignableFrom(c)) { + insertCodeOfMessageConvertCallForList(sb, f, c, i); + } else if (Map.class.isAssignableFrom(c)) { + insertCodeOfMessageConvertCallForMap(sb, f, c, i); + } else if ((getCache(c.getName()) != null)) { + insertCodeOfMessageConvertCallForEnhancedType(sb, f, c, i); + } else if (MessageConvertable.class.isAssignableFrom(c)) { + insertCodeOfMessageConvertCallForMsgConvtblType(sb, f, c, i); + } else { + throw new MessageTypeException("Type error: " + c.getName()); + } + } + } + + private void insertCodeOfMessageConvertCallForEnhancedType( + StringBuilder sb, Field f, Class c, int i) { + c = getCache(c.getName()); + insertCodeOfMessageConvertCallForMsgConvtblType(sb, f, c, i); + } + + private void insertCodeOfMessageConvertCallForMsgConvtblType( + StringBuilder sb, Field f, Class c, int i) { + // if (fi == null) { fi = new Foo_$$_Enhanced(); } + sb.append(Constants.KEYWORD_IF); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(f.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.KEYWORD_NULL); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(f.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.KEYWORD_NEW); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(c.getName()); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + + // ((MessageConvertable)f_i).messageConvert(ary[i]); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(MessageConvertable.class.getName()); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(f.getName()); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_MSGCONVERT); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.VARIABLE_NAME_ARRAY); + sb.append(Constants.CHAR_NAME_LEFT_SQUARE_BRACKET); + sb.append(i); + sb.append(Constants.CHAR_NAME_RIGHT_SQUARE_BRACKET); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + } + + private void insertCodeOfMessageConvertCallForPrimTypes( + StringBuilder sb, Field f, Class c, int i, String name) { + // f0 = objs[0].intValue(); + if (f != null) { + sb.append(f.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_ARRAY); + sb.append(Constants.CHAR_NAME_LEFT_SQUARE_BRACKET); + sb.append(i); + sb.append(Constants.CHAR_NAME_RIGHT_SQUARE_BRACKET); + } else { + sb.append(name); + } + sb.append(Constants.CHAR_NAME_DOT); + if (c.equals(boolean.class)) { + sb.append(Constants.METHOD_NAME_ASBOOLEAN); + } else if (c.equals(byte.class)) { + sb.append(Constants.METHOD_NAME_ASBYTE); + } else if (c.equals(short.class)) { + sb.append(Constants.METHOD_NAME_ASSHORT); + } else if (c.equals(int.class)) { + sb.append(Constants.METHOD_NAME_ASINT); + } else if (c.equals(float.class)) { + sb.append(Constants.METHOD_NAME_ASFLOAT); + } else if (c.equals(long.class)) { + sb.append(Constants.METHOD_NAME_ASLONG); + } else if (c.equals(double.class)) { + sb.append(Constants.METHOD_NAME_ASDOUBLE); + } else if (c.equals(String.class)) { + sb.append(Constants.METHOD_NAME_ASSTRING); + } else if (c.equals(byte[].class)) { + sb.append(Constants.METHOD_NAME_ASBYTEARRAY); + } else if (c.equals(BigInteger.class)) { + sb.append(Constants.METHOD_NAME_ASBIGINTEGER); + } else { + throw new MessageTypeException("Type error: " + c.getName()); + } + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + if (f != null) { + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + } + } + + private void insertCodeOfMessageConvertCallForWrapTypes( + StringBuilder sb, Field f, Class c, int i, String v) { + if (f != null) { + sb.append(f.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + } + sb.append(Constants.KEYWORD_NEW); + sb.append(Constants.CHAR_NAME_SPACE); + if (c.equals(Boolean.class)) { + sb.append(Boolean.class.getName()); + } else if (c.equals(Byte.class)) { + sb.append(Byte.class.getName()); + } else if (c.equals(Short.class)) { + sb.append(Short.class.getName()); + } else if (c.equals(Integer.class)) { + sb.append(Integer.class.getName()); + } else if (c.equals(Float.class)) { + sb.append(Float.class.getName()); + } else if (c.equals(Long.class)) { + sb.append(Long.class.getName()); + } else if (c.equals(Double.class)) { + sb.append(Double.class.getName()); + } else { + throw new MessageTypeException("Type error: " + c.getName()); + } + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + if (f != null) { + sb.append(Constants.VARIABLE_NAME_ARRAY); + sb.append(Constants.CHAR_NAME_LEFT_SQUARE_BRACKET); + sb.append(i); + sb.append(Constants.CHAR_NAME_RIGHT_SQUARE_BRACKET); + } else { + sb.append(v); + } + sb.append(Constants.CHAR_NAME_DOT); + if (c.equals(Boolean.class)) { + sb.append(Constants.METHOD_NAME_ASBOOLEAN); + } else if (c.equals(Byte.class)) { + sb.append(Constants.METHOD_NAME_ASBYTE); + } else if (c.equals(Short.class)) { + sb.append(Constants.METHOD_NAME_ASSHORT); + } else if (c.equals(Integer.class)) { + sb.append(Constants.METHOD_NAME_ASINT); + } else if (c.equals(Float.class)) { + sb.append(Constants.METHOD_NAME_ASFLOAT); + } else if (c.equals(Long.class)) { + sb.append(Constants.METHOD_NAME_ASLONG); + } else if (c.equals(Double.class)) { + sb.append(Constants.METHOD_NAME_ASDOUBLE); + } else { + throw new MessageTypeException("Type error: " + c.getName()); + } + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + if (f != null) { + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + } + } + + private void insertCodeOfMessageConvertCallForList(StringBuilder sb, + Field field, Class type, int i) { + ParameterizedType generic = (ParameterizedType) field + .getGenericType(); + Class genericType = (Class) generic.getActualTypeArguments()[0]; + + // List list = ary[i].asList(); + sb.append(List.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_LIST); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_ARRAY); + sb.append(Constants.CHAR_NAME_LEFT_SQUARE_BRACKET); + sb.append(i); + sb.append(Constants.CHAR_NAME_RIGHT_SQUARE_BRACKET); + sb.append(Constants.CHAR_NAME_DOT); + sb.append("asList"); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + // int size = list.size(); + sb.append(int.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_SIZE); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_LIST); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_SIZE); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + // field initializer + sb.append(field.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.KEYWORD_NEW); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(ArrayList.class.getName()); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + // for loop + sb.append(Constants.KEYWORD_FOR); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(int.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_I); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(0); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_I); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LESSTHAN); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_SIZE); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_PLUS); + sb.append(Constants.CHAR_NAME_PLUS); + sb.append(Constants.VARIABLE_NAME_I); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SPACE); + + // block begin + sb.append(Constants.CHAR_NAME_LEFT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(MessagePackObject.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_VAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(MessagePackObject.class.getName()); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.VARIABLE_NAME_LIST); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_GET); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.VARIABLE_NAME_I); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + sb.append(field.getName()); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_ADD); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + insertCodeOfMessageConvertCall(sb, null, genericType, -1, + Constants.VARIABLE_NAME_VAL); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + // block end + sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + } + + private void insertCodeOfMessageConvertCallForMap(StringBuilder sb, + Field f, Class c, int i) { + ParameterizedType generic = (ParameterizedType) f.getGenericType(); + Class genericType0 = (Class) generic.getActualTypeArguments()[0]; + Class genericType1 = (Class) generic.getActualTypeArguments()[1]; + + // Map map = ary[i].asMap(); + sb.append(Map.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_MAP); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_ARRAY); + sb.append(Constants.CHAR_NAME_LEFT_SQUARE_BRACKET); + sb.append(i); + sb.append(Constants.CHAR_NAME_RIGHT_SQUARE_BRACKET); + sb.append(Constants.CHAR_NAME_DOT); + sb.append("asMap"); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + // int size = list.size(); + sb.append(int.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_SIZE); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_MAP); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_SIZE); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + // field initializer + sb.append(f.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.KEYWORD_NEW); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(HashMap.class.getName()); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + // for loop + sb.append(Constants.KEYWORD_FOR); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Iterator.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_ITER); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_MAP); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_KEYSET); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_ITERATOR); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_ITER); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_HASNEXT); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + + // block map. + sb.append(MessagePackObject.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_KEY); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(MessagePackObject.class.getName()); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.VARIABLE_NAME_ITER); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_NEXT); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(MessagePackObject.class.getName()); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.VARIABLE_NAME_VAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_EQUAL); + sb.append(Constants.CHAR_NAME_SPACE); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(MessagePackObject.class.getName()); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.VARIABLE_NAME_MAP); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_GET); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Constants.VARIABLE_NAME_KEY); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + sb.append(f.getName()); + sb.append(Constants.CHAR_NAME_DOT); + sb.append(Constants.METHOD_NAME_PUT); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + insertCodeOfMessageConvertCall(sb, null, genericType0, -1, + Constants.VARIABLE_NAME_KEY); + sb.append(Constants.CHAR_NAME_COMMA); + sb.append(Constants.CHAR_NAME_SPACE); + insertCodeOfMessageConvertCall(sb, null, genericType1, -1, + Constants.VARIABLE_NAME_VAL); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_SEMICOLON); + sb.append(Constants.CHAR_NAME_SPACE); + + sb.append(Constants.CHAR_NAME_RIGHT_CURLY_BRACHET); + sb.append(Constants.CHAR_NAME_SPACE); + } + + private Class createClass(CtClass enhCtClass) + throws CannotCompileException { + return enhCtClass.toClass(null, null); + } + } + + private static Enhancer enhancer; + + public static void registerEnhancedClass(Class origClass, + boolean packUnpackable) { + if (enhancer == null) { + enhancer = new Enhancer(); + } + String origName = origClass.getName(); + Class enhClass = enhancer.getCache(origName); + if (enhClass == null) { + // generate a class object related to the original class + try { + enhClass = enhancer.generate(origClass, packUnpackable); + } catch (NotFoundException e) { + throw new PackUnpackUtilException(e.getMessage(), e); + } catch (CannotCompileException e) { + throw new PackUnpackUtilException(e.getMessage(), e); + } + // set the generated class to the cache + enhancer.setCache(origName, enhClass); + } + } + + public static void registerEnhancedClass(Class origClass) { + registerEnhancedClass(origClass, false); + } + + public static boolean isRegistered(Class origClass) { + if (enhancer == null) { + enhancer = new Enhancer(); + } + return enhancer.getCache(origClass.getName()) != null; + } + + public static Class getEnhancedClass(Class origClass, + boolean packUnpackable) { + if (!isRegistered(origClass)) { + registerEnhancedClass(origClass, packUnpackable); + } + return enhancer.getCache(origClass.getName()); + } + + public static Class getEnhancedClass(Class origClass) { + return getEnhancedClass(origClass, false); + } + + public static Object newEnhancedInstance(Class origClass, + boolean packUnpackable) { + try { + Class enhClass = getEnhancedClass(origClass, packUnpackable); + // create a new object of the generated class + return enhClass.newInstance(); + } catch (InstantiationException e) { + throw new PackUnpackUtilException(e.getMessage(), e); + } catch (IllegalAccessException e) { + throw new PackUnpackUtilException(e.getMessage(), e); + } + } + + public static Object newEnhancedInstance(Class origClass) { + return newEnhancedInstance(origClass, false); + } + + public static Object initEnhancedInstance(MessagePackObject obj, + Class origClass, boolean packUnpackable) { + Object ret = newEnhancedInstance(origClass, packUnpackable); + ((MessageConvertable) ret).messageConvert(obj); + return ret; + } + + public static Object initEnhancedInstance(MessagePackObject obj, + Class origClass) { + return initEnhancedInstance(obj, origClass, false); + } + + @MessagePackUnpackable + public static class Image { + public String uri = ""; + + public String title = ""; + + public int width = 0; + + public int height = 0; + + public int size = 0; + + public boolean equals(Image obj) { + return uri.equals(obj.uri) && title.equals(obj.title) + && width == obj.width && height == obj.height + && size == obj.size; + } + + public boolean equals(Object obj) { + if (obj.getClass() != Image.class) { + return false; + } + return equals((Image) obj); + } + } + + public static void main(final String[] args) throws Exception { + Image src = (Image) PackUnpackUtil.newEnhancedInstance(Image.class); + src.title = "msgpack"; + src.uri = "http://msgpack.org/"; + src.width = 2560; + src.height = 1600; + src.size = 4096000; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + Image dst = (Image) PackUnpackUtil.newEnhancedInstance(Image.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + pac.unpack((MessageUnpackable) dst); + // System.out.println(src.equals(dst)); + } +} diff --git a/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtilException.java b/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtilException.java new file mode 100644 index 00000000..df3af058 --- /dev/null +++ b/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtilException.java @@ -0,0 +1,12 @@ +package org.msgpack.util.annotation; + +public class PackUnpackUtilException extends RuntimeException { + + public PackUnpackUtilException(String reason) { + super(reason); + } + + public PackUnpackUtilException(String reason, Throwable t) { + super(reason, t); + } +} diff --git a/java/src/test/java/org/msgpack/Image.java b/java/src/test/java/org/msgpack/Image.java new file mode 100644 index 00000000..3fcfe388 --- /dev/null +++ b/java/src/test/java/org/msgpack/Image.java @@ -0,0 +1,50 @@ +package org.msgpack; + +import org.msgpack.*; +import java.io.*; +import java.util.*; + +public class Image implements MessagePackable, MessageUnpackable { + public String uri = ""; + public String title = ""; + public int width = 0; + public int height = 0; + public int size = 0; + + public void messagePack(Packer pk) throws IOException { + pk.packArray(5); + pk.pack(uri); + pk.pack(title); + pk.pack(width); + pk.pack(height); + pk.pack(size); + } + + public void messageUnpack(Unpacker pac) throws IOException, MessageTypeException { + int length = pac.unpackArray(); + if(length != 5) { + throw new MessageTypeException(); + } + uri = pac.unpackString(); + title = pac.unpackString(); + width = pac.unpackInt(); + height = pac.unpackInt(); + size = pac.unpackInt(); + } + + public boolean equals(Image obj) { + return uri.equals(obj.uri) && + title.equals(obj.title) && + width == obj.width && + height == obj.height && + size == obj.size; + } + + public boolean equals(Object obj) { + if(obj.getClass() != Image.class) { + return false; + } + return equals((Image)obj); + } +} + diff --git a/java/src/test/java/org/msgpack/TestCases.java b/java/src/test/java/org/msgpack/TestCases.java new file mode 100644 index 00000000..c368972f --- /dev/null +++ b/java/src/test/java/org/msgpack/TestCases.java @@ -0,0 +1,40 @@ +package org.msgpack; + +import java.io.*; +import java.util.*; + +import org.junit.Test; +import static org.junit.Assert.*; + +public class TestCases { + public void feedFile(Unpacker pac, String path) throws Exception { + FileInputStream input = new FileInputStream(path); + byte[] buffer = new byte[32*1024]; + while(true) { + int count = input.read(buffer); + if(count < 0) { + break; + } + pac.feed(buffer, 0, count); + } + } + + @Test + public void testCases() throws Exception { + Unpacker pac = new Unpacker(); + Unpacker pac_compact = new Unpacker(); + + feedFile(pac, "src/test/resources/cases.mpac"); + feedFile(pac_compact, "src/test/resources/cases_compact.mpac"); + + UnpackResult result = new UnpackResult(); + while(pac.next(result)) { + UnpackResult result_compact = new UnpackResult(); + assertTrue( pac_compact.next(result_compact) ); + assertTrue( result.getData().equals(result_compact.getData()) ); + } + + assertFalse( pac_compact.next(result) ); + } +}; + diff --git a/java/src/test/java/org/msgpack/TestDirectConversion.java b/java/src/test/java/org/msgpack/TestDirectConversion.java index 77bbc585..1822ecb4 100644 --- a/java/src/test/java/org/msgpack/TestDirectConversion.java +++ b/java/src/test/java/org/msgpack/TestDirectConversion.java @@ -3,146 +3,277 @@ package org.msgpack; import org.msgpack.*; import java.io.*; import java.util.*; +import java.math.BigInteger; import org.junit.Test; import static org.junit.Assert.*; public class TestDirectConversion { - @Test - public void testInt() throws Exception { - testInt(0); - testInt(-1); - testInt(1); - testInt(Integer.MIN_VALUE); - testInt(Integer.MAX_VALUE); - Random rand = new Random(); - for (int i = 0; i < 1000; i++) - testInt(rand.nextInt()); - } - public void testInt(int val) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).pack(val); - ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - Unpacker upk = new Unpacker(in); - assertEquals(val, upk.unpackInt()); - } + @Test + public void testInt() throws Exception { + testInt(0); + testInt(-1); + testInt(1); + testInt(Integer.MIN_VALUE); + testInt(Integer.MAX_VALUE); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testInt(rand.nextInt()); + } + public void testInt(int val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + assertEquals(val, pac.unpackInt()); + } - @Test - public void testFloat() throws Exception { - testFloat((float)0.0); - testFloat((float)-0.0); - testFloat((float)1.0); - testFloat((float)-1.0); - testFloat((float)Float.MAX_VALUE); - testFloat((float)Float.MIN_VALUE); - testFloat((float)Float.NaN); - testFloat((float)Float.NEGATIVE_INFINITY); - testFloat((float)Float.POSITIVE_INFINITY); - Random rand = new Random(); - for (int i = 0; i < 1000; i++) - testFloat(rand.nextFloat()); - } - public void testFloat(float val) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).pack(val); - ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - Unpacker upk = new Unpacker(in); - float f = upk.unpackFloat(); - if(Float.isNaN(val)) { - assertTrue(Float.isNaN(f)); - } else { - assertEquals(val, f, 10e-10); - } - } + @Test + public void testLong() throws Exception { + testLong(0); + testLong(-1); + testLong(1); + testLong(Integer.MIN_VALUE); + testLong(Integer.MAX_VALUE); + testLong(Long.MIN_VALUE); + testLong(Long.MAX_VALUE); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testLong(rand.nextLong()); + } + public void testLong(long val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + assertEquals(val, pac.unpackLong()); + } - @Test - public void testDouble() throws Exception { - testDouble((double)0.0); - testDouble((double)-0.0); - testDouble((double)1.0); - testDouble((double)-1.0); - testDouble((double)Double.MAX_VALUE); - testDouble((double)Double.MIN_VALUE); - testDouble((double)Double.NaN); - testDouble((double)Double.NEGATIVE_INFINITY); - testDouble((double)Double.POSITIVE_INFINITY); - Random rand = new Random(); - for (int i = 0; i < 1000; i++) - testDouble(rand.nextDouble()); - } - public void testDouble(double val) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).pack(val); - ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - Unpacker upk = new Unpacker(in); - double f = upk.unpackDouble(); - if(Double.isNaN(val)) { - assertTrue(Double.isNaN(f)); - } else { - assertEquals(val, f, 10e-10); - } - } + @Test + public void testBigInteger() throws Exception { + testBigInteger(BigInteger.valueOf(0)); + testBigInteger(BigInteger.valueOf(-1)); + testBigInteger(BigInteger.valueOf(1)); + testBigInteger(BigInteger.valueOf(Integer.MIN_VALUE)); + testBigInteger(BigInteger.valueOf(Integer.MAX_VALUE)); + testBigInteger(BigInteger.valueOf(Long.MIN_VALUE)); + testBigInteger(BigInteger.valueOf(Long.MAX_VALUE)); + BigInteger max = BigInteger.valueOf(Long.MAX_VALUE).setBit(63); + testBigInteger(max); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testBigInteger( max.subtract(BigInteger.valueOf( Math.abs(rand.nextLong()) )) ); + } + public void testBigInteger(BigInteger val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + assertEquals(val, pac.unpackBigInteger()); + } - @Test - public void testNil() throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).packNil(); - ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - Unpacker upk = new Unpacker(in); - assertEquals(null, upk.unpackNull()); - } + @Test + public void testFloat() throws Exception { + testFloat((float)0.0); + testFloat((float)-0.0); + testFloat((float)1.0); + testFloat((float)-1.0); + testFloat((float)Float.MAX_VALUE); + testFloat((float)Float.MIN_VALUE); + testFloat((float)Float.NaN); + testFloat((float)Float.NEGATIVE_INFINITY); + testFloat((float)Float.POSITIVE_INFINITY); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testFloat(rand.nextFloat()); + } + public void testFloat(float val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + assertEquals(val, pac.unpackFloat(), 10e-10); + } - @Test - public void testBoolean() throws Exception { - testBoolean(false); - testBoolean(true); - } - public void testBoolean(boolean val) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).pack(val); - ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - Unpacker upk = new Unpacker(in); - assertEquals(val, upk.unpackBoolean()); - } + @Test + public void testDouble() throws Exception { + testDouble((double)0.0); + testDouble((double)-0.0); + testDouble((double)1.0); + testDouble((double)-1.0); + testDouble((double)Double.MAX_VALUE); + testDouble((double)Double.MIN_VALUE); + testDouble((double)Double.NaN); + testDouble((double)Double.NEGATIVE_INFINITY); + testDouble((double)Double.POSITIVE_INFINITY); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testDouble(rand.nextDouble()); + } + public void testDouble(double val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + assertEquals(val, pac.unpackDouble(), 10e-10); + } - @Test - public void testString() throws Exception { - testString(""); - testString("a"); - testString("ab"); - testString("abc"); - // small size string - for (int i = 0; i < 100; i++) { - StringBuilder sb = new StringBuilder(); - int len = (int)Math.random() % 31 + 1; - for (int j = 0; j < len; j++) - sb.append('a' + ((int)Math.random()) & 26); - testString(sb.toString()); - } - // medium size string - for (int i = 0; i < 100; i++) { - StringBuilder sb = new StringBuilder(); - int len = (int)Math.random() % 100 + (1 << 15); - for (int j = 0; j < len; j++) - sb.append('a' + ((int)Math.random()) & 26); - testString(sb.toString()); - } - // large size string - for (int i = 0; i < 10; i++) { - StringBuilder sb = new StringBuilder(); - int len = (int)Math.random() % 100 + (1 << 31); - for (int j = 0; j < len; j++) - sb.append('a' + ((int)Math.random()) & 26); - testString(sb.toString()); - } - } - public void testString(String val) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).pack(val); - ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - Unpacker upk = new Unpacker(in); - assertEquals(val, upk.unpackString()); - } + @Test + public void testNil() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).packNil(); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + assertEquals(null, pac.unpackNull()); + } - // FIXME container types + @Test + public void testBoolean() throws Exception { + testBoolean(false); + testBoolean(true); + } + public void testBoolean(boolean val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + assertEquals(val, pac.unpackBoolean()); + } + + @Test + public void testString() throws Exception { + testString(""); + testString("a"); + testString("ab"); + testString("abc"); + + // small size string + for (int i = 0; i < 100; i++) { + StringBuilder sb = new StringBuilder(); + int len = (int)Math.random() % 31 + 1; + for (int j = 0; j < len; j++) + sb.append('a' + ((int)Math.random()) & 26); + testString(sb.toString()); + } + + // medium size string + for (int i = 0; i < 100; i++) { + StringBuilder sb = new StringBuilder(); + int len = (int)Math.random() % 100 + (1 << 15); + for (int j = 0; j < len; j++) + sb.append('a' + ((int)Math.random()) & 26); + testString(sb.toString()); + } + + // large size string + for (int i = 0; i < 10; i++) { + StringBuilder sb = new StringBuilder(); + int len = (int)Math.random() % 100 + (1 << 31); + for (int j = 0; j < len; j++) + sb.append('a' + ((int)Math.random()) & 26); + testString(sb.toString()); + } + } + public void testString(String val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + assertEquals(val, pac.unpackString()); + } + + @Test + public void testArray() throws Exception { + List emptyList = new ArrayList(); + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(emptyList); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + int ulen = pac.unpackArray(); + assertEquals(0, ulen); + } + + for (int i = 0; i < 1000; i++) { + List l = new ArrayList(); + int len = (int)Math.random() % 1000 + 1; + for (int j = 0; j < len; j++) + l.add(j); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(l); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + int ulen = pac.unpackArray(); + assertEquals(len, ulen); + for (int j = 0; j < len; j++) { + assertEquals(l.get(j).intValue(), pac.unpackInt()); + } + } + + for (int i = 0; i < 1000; i++) { + List l = new ArrayList(); + int len = (int)Math.random() % 1000 + 1; + for (int j = 0; j < len; j++) + l.add(Integer.toString(j)); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(l); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + int ulen = pac.unpackArray(); + assertEquals(len, ulen); + for (int j = 0; j < len; j++) { + assertEquals(l.get(j), pac.unpackString()); + } + } + } + + @Test + public void testMap() throws Exception { + Map emptyMap = new HashMap(); + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(emptyMap); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + int ulen = pac.unpackMap(); + assertEquals(0, ulen); + } + + for (int i = 0; i < 1000; i++) { + Map m = new HashMap(); + int len = (int)Math.random() % 1000 + 1; + for (int j = 0; j < len; j++) + m.put(j, j); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(m); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + int ulen = pac.unpackMap(); + assertEquals(len, ulen); + for (int j = 0; j < len; j++) { + Integer val = m.get(pac.unpackInt()); + assertNotNull(val); + assertEquals(val.intValue(), pac.unpackInt()); + } + } + + for (int i = 0; i < 1000; i++) { + Map m = new HashMap(); + int len = (int)Math.random() % 1000 + 1; + for (int j = 0; j < len; j++) + m.put(Integer.toString(j), j); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(m); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + int ulen = pac.unpackMap(); + assertEquals(len, ulen); + for (int j = 0; j < len; j++) { + Integer val = m.get(pac.unpackString()); + assertNotNull(val); + assertEquals(val.intValue(), pac.unpackInt()); + } + } + } }; + diff --git a/java/src/test/java/org/msgpack/TestMessageUnpackable.java b/java/src/test/java/org/msgpack/TestMessageUnpackable.java new file mode 100644 index 00000000..32917c7a --- /dev/null +++ b/java/src/test/java/org/msgpack/TestMessageUnpackable.java @@ -0,0 +1,32 @@ +package org.msgpack; + +import org.msgpack.*; +import java.io.*; +import java.util.*; +import java.math.BigInteger; + +import org.junit.Test; +import static org.junit.Assert.*; + +public class TestMessageUnpackable { + @Test + public void testImage() throws Exception { + Image src = new Image(); + src.title = "msgpack"; + src.uri = "http://msgpack.org/"; + src.width = 2560; + src.height = 1600; + src.size = 4096000; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + src.messagePack(new Packer(out)); + + Image dst = new Image(); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + dst.messageUnpack(new Unpacker(in)); + + assertEquals(src, dst); + } +} + diff --git a/java/src/test/java/org/msgpack/TestObjectEquals.java b/java/src/test/java/org/msgpack/TestObjectEquals.java new file mode 100644 index 00000000..25fe9271 --- /dev/null +++ b/java/src/test/java/org/msgpack/TestObjectEquals.java @@ -0,0 +1,96 @@ +package org.msgpack; + +import org.msgpack.*; +import org.msgpack.object.*; +import java.math.BigInteger; +import java.util.*; + +import org.junit.Test; +import static org.junit.Assert.*; + +public class TestObjectEquals { + @Test + public void testInt() throws Exception { + testInt(0); + testInt(-1); + testInt(1); + testInt(Integer.MIN_VALUE); + testInt(Integer.MAX_VALUE); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testInt(rand.nextInt()); + } + public void testInt(int val) throws Exception { + MessagePackObject objInt = IntegerType.create(val); + MessagePackObject objLong = IntegerType.create((long)val); + MessagePackObject objBigInt = IntegerType.create(BigInteger.valueOf((long)val)); + assertTrue(objInt.equals(objInt)); + assertTrue(objInt.equals(objLong)); + assertTrue(objInt.equals(objBigInt)); + assertTrue(objLong.equals(objInt)); + assertTrue(objLong.equals(objLong)); + assertTrue(objLong.equals(objBigInt)); + assertTrue(objBigInt.equals(objInt)); + assertTrue(objBigInt.equals(objLong)); + assertTrue(objBigInt.equals(objBigInt)); + } + + @Test + public void testLong() throws Exception { + testLong(0); + testLong(-1); + testLong(1); + testLong(Integer.MIN_VALUE); + testLong(Integer.MAX_VALUE); + testLong(Long.MIN_VALUE); + testLong(Long.MAX_VALUE); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testLong(rand.nextLong()); + } + public void testLong(long val) throws Exception { + MessagePackObject objInt = IntegerType.create((int)val); + MessagePackObject objLong = IntegerType.create(val); + MessagePackObject objBigInt = IntegerType.create(BigInteger.valueOf(val)); + if(val > (long)Integer.MAX_VALUE || val < (long)Integer.MIN_VALUE) { + assertTrue(objInt.equals(objInt)); + assertFalse(objInt.equals(objLong)); + assertFalse(objInt.equals(objBigInt)); + assertFalse(objLong.equals(objInt)); + assertTrue(objLong.equals(objLong)); + assertTrue(objLong.equals(objBigInt)); + assertFalse(objBigInt.equals(objInt)); + assertTrue(objBigInt.equals(objLong)); + assertTrue(objBigInt.equals(objBigInt)); + } else { + assertTrue(objInt.equals(objInt)); + assertTrue(objInt.equals(objLong)); + assertTrue(objInt.equals(objBigInt)); + assertTrue(objLong.equals(objInt)); + assertTrue(objLong.equals(objLong)); + assertTrue(objLong.equals(objBigInt)); + assertTrue(objBigInt.equals(objInt)); + assertTrue(objBigInt.equals(objLong)); + assertTrue(objBigInt.equals(objBigInt)); + } + } + + @Test + public void testNil() throws Exception { + assertTrue(NilType.create().equals(NilType.create())); + assertFalse(NilType.create().equals(IntegerType.create(0))); + assertFalse(NilType.create().equals(BooleanType.create(false))); + } + + @Test + public void testString() throws Exception { + testString(""); + testString("a"); + testString("ab"); + testString("abc"); + } + public void testString(String str) throws Exception { + assertTrue(RawType.create(str).equals(RawType.create(str))); + } +} + diff --git a/java/src/test/java/org/msgpack/TestPackUnpack.java b/java/src/test/java/org/msgpack/TestPackUnpack.java index b02bbb40..494c8a8a 100644 --- a/java/src/test/java/org/msgpack/TestPackUnpack.java +++ b/java/src/test/java/org/msgpack/TestPackUnpack.java @@ -3,245 +3,273 @@ package org.msgpack; import org.msgpack.*; import java.io.*; import java.util.*; +import java.math.BigInteger; import org.junit.Test; import static org.junit.Assert.*; public class TestPackUnpack { - protected Object unpackOne(ByteArrayOutputStream out) { - return unpackOne(out, null); - } - protected Object unpackOne(ByteArrayOutputStream out, Schema schema) { - ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - Unpacker upk = new Unpacker(in); - if (schema != null) - upk = upk.useSchema(schema); - Iterator it = upk.iterator(); - assertEquals(true, it.hasNext()); - Object obj = it.next(); - assertEquals(false, it.hasNext()); - return obj; - } + public MessagePackObject unpackOne(ByteArrayOutputStream out) { + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + Iterator it = pac.iterator(); + assertEquals(true, it.hasNext()); + MessagePackObject obj = it.next(); + assertEquals(false, it.hasNext()); + return obj; + } - @Test - public void testInt() throws Exception { - testInt(0); - testInt(-1); - testInt(1); - testInt(Integer.MIN_VALUE); - testInt(Integer.MAX_VALUE); - Random rand = new Random(); - for (int i = 0; i < 1000; i++) - testInt(rand.nextInt()); - } - public void testInt(int val) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).pack(val); - Object obj = unpackOne(out); - if (obj instanceof Byte) - assertEquals(val, ((Byte)obj).intValue()); - else if (obj instanceof Integer) - assertEquals(val, ((Integer)obj).intValue()); - else if (obj instanceof Short) - assertEquals(val, ((Short)obj).intValue()); - else if (obj instanceof Long) - assertEquals(val, ((Long)obj).intValue()); - else { - System.out.println("Got unexpected class: " + obj.getClass()); - assertTrue(false); - } - } + @Test + public void testInt() throws Exception { + testInt(0); + testInt(-1); + testInt(1); + testInt(Integer.MIN_VALUE); + testInt(Integer.MAX_VALUE); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testInt(rand.nextInt()); + } + public void testInt(int val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + MessagePackObject obj = unpackOne(out); + assertEquals(val, obj.asInt()); + } - @Test - public void testFloat() throws Exception { - testFloat((float)0.0); - testFloat((float)-0.0); - testFloat((float)1.0); - testFloat((float)-1.0); - testFloat((float)Float.MAX_VALUE); - testFloat((float)Float.MIN_VALUE); - testFloat((float)Float.NaN); - testFloat((float)Float.NEGATIVE_INFINITY); - testFloat((float)Float.POSITIVE_INFINITY); - Random rand = new Random(); - for (int i = 0; i < 1000; i++) - testFloat(rand.nextFloat()); - } - public void testFloat(float val) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).pack(val); - Object obj = unpackOne(out); - if (obj instanceof Float) - assertEquals(val, ((Float)obj).floatValue(), 10e-10); - else { - System.out.println("Got unexpected class: " + obj.getClass()); - assertTrue(false); - } - } + @Test + public void testLong() throws Exception { + testLong(0); + testLong(-1); + testLong(1); + testLong(Integer.MIN_VALUE); + testLong(Integer.MAX_VALUE); + testLong(Long.MIN_VALUE); + testLong(Long.MAX_VALUE); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testLong(rand.nextLong()); + } + public void testLong(long val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + MessagePackObject obj = unpackOne(out); + assertEquals(val, obj.asLong()); + } - @Test - public void testDouble() throws Exception { - testDouble((double)0.0); - testDouble((double)-0.0); - testDouble((double)1.0); - testDouble((double)-1.0); - testDouble((double)Double.MAX_VALUE); - testDouble((double)Double.MIN_VALUE); - testDouble((double)Double.NaN); - testDouble((double)Double.NEGATIVE_INFINITY); - testDouble((double)Double.POSITIVE_INFINITY); - Random rand = new Random(); - for (int i = 0; i < 1000; i++) - testDouble(rand.nextDouble()); - } - public void testDouble(double val) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).pack(val); - Object obj = unpackOne(out); - if (obj instanceof Double) - assertEquals(val, ((Double)obj).doubleValue(), 10e-10); - else { - System.out.println("Got unexpected class: " + obj.getClass()); - assertTrue(false); - } - } + @Test + public void testBigInteger() throws Exception { + testBigInteger(BigInteger.valueOf(0)); + testBigInteger(BigInteger.valueOf(-1)); + testBigInteger(BigInteger.valueOf(1)); + testBigInteger(BigInteger.valueOf(Integer.MIN_VALUE)); + testBigInteger(BigInteger.valueOf(Integer.MAX_VALUE)); + testBigInteger(BigInteger.valueOf(Long.MIN_VALUE)); + testBigInteger(BigInteger.valueOf(Long.MAX_VALUE)); + BigInteger max = BigInteger.valueOf(Long.MAX_VALUE).setBit(63); + testBigInteger(max); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testBigInteger( max.subtract(BigInteger.valueOf( Math.abs(rand.nextLong()) )) ); + } + public void testBigInteger(BigInteger val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + MessagePackObject obj = unpackOne(out); + assertEquals(val, obj.asBigInteger()); + } - @Test - public void testNil() throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).packNil(); - Object obj = unpackOne(out); - assertEquals(null, obj); - } + @Test + public void testFloat() throws Exception { + testFloat((float)0.0); + testFloat((float)-0.0); + testFloat((float)1.0); + testFloat((float)-1.0); + testFloat((float)Float.MAX_VALUE); + testFloat((float)Float.MIN_VALUE); + testFloat((float)Float.NaN); + testFloat((float)Float.NEGATIVE_INFINITY); + testFloat((float)Float.POSITIVE_INFINITY); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testFloat(rand.nextFloat()); + } + public void testFloat(float val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + MessagePackObject obj = unpackOne(out); + float f = obj.asFloat(); + assertEquals(val, f, 10e-10); + } - @Test - public void testBoolean() throws Exception { - testBoolean(false); - testBoolean(true); - } - public void testBoolean(boolean val) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).pack(val); - Object obj = unpackOne(out); - if (obj instanceof Boolean) - assertEquals(val, ((Boolean)obj).booleanValue()); - else { - System.out.println("Got unexpected class: " + obj.getClass()); - assertTrue(false); - } - } + @Test + public void testDouble() throws Exception { + testDouble((double)0.0); + testDouble((double)-0.0); + testDouble((double)1.0); + testDouble((double)-1.0); + testDouble((double)Double.MAX_VALUE); + testDouble((double)Double.MIN_VALUE); + testDouble((double)Double.NaN); + testDouble((double)Double.NEGATIVE_INFINITY); + testDouble((double)Double.POSITIVE_INFINITY); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testDouble(rand.nextDouble()); + } + public void testDouble(double val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + MessagePackObject obj = unpackOne(out); + double f = obj.asDouble(); + assertEquals(val, f, 10e-10); + } - @Test - public void testString() throws Exception { - testString(""); - testString("a"); - testString("ab"); - testString("abc"); - // small size string - for (int i = 0; i < 100; i++) { - StringBuilder sb = new StringBuilder(); - int len = (int)Math.random() % 31 + 1; - for (int j = 0; j < len; j++) - sb.append('a' + ((int)Math.random()) & 26); - testString(sb.toString()); - } - // medium size string - for (int i = 0; i < 100; i++) { - StringBuilder sb = new StringBuilder(); - int len = (int)Math.random() % 100 + (1 << 15); - for (int j = 0; j < len; j++) - sb.append('a' + ((int)Math.random()) & 26); - testString(sb.toString()); - } - // large size string - for (int i = 0; i < 10; i++) { - StringBuilder sb = new StringBuilder(); - int len = (int)Math.random() % 100 + (1 << 31); - for (int j = 0; j < len; j++) - sb.append('a' + ((int)Math.random()) & 26); - testString(sb.toString()); - } - } - public void testString(String val) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).pack(val); - Object obj = unpackOne(out); - if (obj instanceof byte[]) - assertEquals(val, new String((byte[])obj)); - else { - System.out.println("obj=" + obj); - System.out.println("Got unexpected class: " + obj.getClass()); - assertTrue(false); - } - } + @Test + public void testNil() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).packNil(); + MessagePackObject obj = unpackOne(out); + assertTrue(obj.isNil()); + } - @Test - public void testArray() throws Exception { - List emptyList = new ArrayList(); - testArray(emptyList, Schema.parse("(array int)")); + @Test + public void testBoolean() throws Exception { + testBoolean(false); + testBoolean(true); + } + public void testBoolean(boolean val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + MessagePackObject obj = unpackOne(out); + assertEquals(val, obj.asBoolean()); + } - for (int i = 0; i < 1000; i++) { - Schema schema = Schema.parse("(array int)"); - List l = new ArrayList(); - int len = (int)Math.random() % 1000 + 1; - for (int j = 0; j < len; j++) - l.add(j); - testArray(l, schema); - } - for (int i = 0; i < 1000; i++) { - Schema schema = Schema.parse("(array string)"); - List l = new ArrayList(); - int len = (int)Math.random() % 1000 + 1; - for (int j = 0; j < len; j++) - l.add(Integer.toString(j)); - testArray(l, schema); - } - } - public void testArray(List val, Schema schema) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).pack(val); - Object obj = unpackOne(out, schema); - if (obj instanceof List) - assertTrue(val.equals(obj)); - else { - System.out.println("obj=" + obj); - System.out.println("Got unexpected class: " + obj.getClass()); - assertTrue(false); - } - } + @Test + public void testString() throws Exception { + testString(""); + testString("a"); + testString("ab"); + testString("abc"); - @Test - public void testMap() throws Exception { - Map emptyMap = new HashMap(); - testMap(emptyMap, Schema.parse("(map int int)")); + // small size string + for (int i = 0; i < 100; i++) { + StringBuilder sb = new StringBuilder(); + int len = (int)Math.random() % 31 + 1; + for (int j = 0; j < len; j++) + sb.append('a' + ((int)Math.random()) & 26); + testString(sb.toString()); + } - for (int i = 0; i < 1000; i++) { - Schema schema = Schema.parse("(map int int)"); - Map m = new HashMap(); - int len = (int)Math.random() % 1000 + 1; - for (int j = 0; j < len; j++) - m.put(j, j); - testMap(m, schema); - } - for (int i = 0; i < 1000; i++) { - Schema schema = Schema.parse("(map string int)"); - Map m = new HashMap(); - int len = (int)Math.random() % 1000 + 1; - for (int j = 0; j < len; j++) - m.put(Integer.toString(j), j); - testMap(m, schema); - } - } - public void testMap(Map val, Schema schema) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - new Packer(out).pack(val); - Object obj = unpackOne(out, schema); - if (obj instanceof Map) - assertTrue(val.equals(obj)); - else { - System.out.println("obj=" + obj); - System.out.println("Got unexpected class: " + obj.getClass()); - assertTrue(false); - } - } + // medium size string + for (int i = 0; i < 100; i++) { + StringBuilder sb = new StringBuilder(); + int len = (int)Math.random() % 100 + (1 << 15); + for (int j = 0; j < len; j++) + sb.append('a' + ((int)Math.random()) & 26); + testString(sb.toString()); + } + + // large size string + for (int i = 0; i < 10; i++) { + StringBuilder sb = new StringBuilder(); + int len = (int)Math.random() % 100 + (1 << 31); + for (int j = 0; j < len; j++) + sb.append('a' + ((int)Math.random()) & 26); + testString(sb.toString()); + } + } + public void testString(String val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + MessagePackObject obj = unpackOne(out); + assertEquals(val, obj.asString()); + } + + @Test + public void testArray() throws Exception { + List emptyList = new ArrayList(); + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(emptyList); + MessagePackObject obj = unpackOne(out); + assertEquals(emptyList, obj.asList()); + } + + for (int i = 0; i < 1000; i++) { + List l = new ArrayList(); + int len = (int)Math.random() % 1000 + 1; + for (int j = 0; j < len; j++) + l.add(j); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(l); + MessagePackObject obj = unpackOne(out); + List list = obj.asList(); + assertEquals(l.size(), list.size()); + for (int j = 0; j < len; j++) { + assertEquals(l.get(j).intValue(), list.get(j).asInt()); + } + } + + for (int i = 0; i < 1000; i++) { + List l = new ArrayList(); + int len = (int)Math.random() % 1000 + 1; + for (int j = 0; j < len; j++) + l.add(Integer.toString(j)); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(l); + MessagePackObject obj = unpackOne(out); + List list = obj.asList(); + assertEquals(l.size(), list.size()); + for (int j = 0; j < len; j++) { + assertEquals(l.get(j), list.get(j).asString()); + } + } + } + + @Test + public void testMap() throws Exception { + Map emptyMap = new HashMap(); + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(emptyMap); + MessagePackObject obj = unpackOne(out); + assertEquals(emptyMap, obj.asMap()); + } + + for (int i = 0; i < 1000; i++) { + Map m = new HashMap(); + int len = (int)Math.random() % 1000 + 1; + for (int j = 0; j < len; j++) + m.put(j, j); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(m); + MessagePackObject obj = unpackOne(out); + Map map = obj.asMap(); + assertEquals(m.size(), map.size()); + for (Map.Entry pair : map.entrySet()) { + Integer val = m.get(pair.getKey().asInt()); + assertNotNull(val); + assertEquals(val.intValue(), pair.getValue().asInt()); + } + } + + for (int i = 0; i < 1000; i++) { + Map m = new HashMap(); + int len = (int)Math.random() % 1000 + 1; + for (int j = 0; j < len; j++) + m.put(Integer.toString(j), j); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(m); + MessagePackObject obj = unpackOne(out); + Map map = obj.asMap(); + assertEquals(m.size(), map.size()); + for (Map.Entry pair : map.entrySet()) { + Integer val = m.get(pair.getKey().asString()); + assertNotNull(val); + assertEquals(val.intValue(), pair.getValue().asInt()); + } + } + } }; + diff --git a/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java b/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java new file mode 100644 index 00000000..08c4fa82 --- /dev/null +++ b/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java @@ -0,0 +1,700 @@ +package org.msgpack.util.annotation; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; + +import org.junit.Test; +import org.msgpack.MessagePackObject; +import org.msgpack.MessageUnpackable; +import org.msgpack.Packer; +import org.msgpack.Unpacker; + +public class TestMessagePackUnpackable extends TestCase { + + @Test + public void testPrimitiveTypeFields01() throws Exception { + PrimitiveTypeFieldsClass src = (PrimitiveTypeFieldsClass) PackUnpackUtil + .newEnhancedInstance(PrimitiveTypeFieldsClass.class); + src.f0 = (byte) 0; + src.f1 = 1; + src.f2 = 2; + src.f3 = 3; + src.f4 = 4; + src.f5 = 5; + src.f6 = false; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + PrimitiveTypeFieldsClass dst = (PrimitiveTypeFieldsClass) PackUnpackUtil + .newEnhancedInstance(PrimitiveTypeFieldsClass.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + pac.unpack((MessageUnpackable) dst); + assertEquals(src.f0, dst.f0); + assertEquals(src.f1, dst.f1); + assertEquals(src.f2, dst.f2); + assertEquals(src.f3, dst.f3); + assertEquals(src.f4, dst.f4); + assertEquals(src.f5, dst.f5); + assertEquals(src.f6, dst.f6); + } + + @Test + public void testPrimitiveTypeFields02() throws Exception { + PrimitiveTypeFieldsClass src = (PrimitiveTypeFieldsClass) PackUnpackUtil + .newEnhancedInstance(PrimitiveTypeFieldsClass.class); + src.f0 = (byte) 0; + src.f1 = 1; + src.f2 = 2; + src.f3 = 3; + src.f4 = 4; + src.f5 = 5; + src.f6 = false; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + Iterator it = pac.iterator(); + assertTrue(it.hasNext()); + MessagePackObject mpo = it.next(); + PrimitiveTypeFieldsClass dst = (PrimitiveTypeFieldsClass) PackUnpackUtil + .initEnhancedInstance(mpo, PrimitiveTypeFieldsClass.class); + assertEquals(src.f0, dst.f0); + assertEquals(src.f1, dst.f1); + assertEquals(src.f2, dst.f2); + assertEquals(src.f3, dst.f3); + assertEquals(src.f4, dst.f4); + assertEquals(src.f5, dst.f5); + assertEquals(src.f6, dst.f6); + assertFalse(it.hasNext()); + } + + @MessagePackUnpackable + public static class PrimitiveTypeFieldsClass { + public byte f0; + public short f1; + public int f2; + public long f3; + public float f4; + public double f5; + public boolean f6; + + public PrimitiveTypeFieldsClass() { + } + } + + @Test + public void testGeneralReferenceTypeFieldsClass01() throws Exception { + GeneralReferenceTypeFieldsClass src = (GeneralReferenceTypeFieldsClass) PackUnpackUtil + .newEnhancedInstance(GeneralReferenceTypeFieldsClass.class); + src.f0 = 0; + src.f1 = 1; + src.f2 = 2; + src.f3 = (long) 3; + src.f4 = (float) 4; + src.f5 = (double) 5; + src.f6 = false; + src.f7 = new BigInteger("7"); + src.f8 = "8"; + src.f9 = new byte[] { 0x01, 0x02 }; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + GeneralReferenceTypeFieldsClass dst = (GeneralReferenceTypeFieldsClass) PackUnpackUtil + .newEnhancedInstance(GeneralReferenceTypeFieldsClass.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + pac.unpack((MessageUnpackable) dst); + assertEquals(src.f0, dst.f0); + assertEquals(src.f1, dst.f1); + assertEquals(src.f2, dst.f2); + assertEquals(src.f3, dst.f3); + assertEquals(src.f4, dst.f4); + assertEquals(src.f5, dst.f5); + assertEquals(src.f6, dst.f6); + assertEquals(src.f7, dst.f7); + assertEquals(src.f8, dst.f8); + assertEquals(src.f9[0], dst.f9[0]); + assertEquals(src.f9[1], dst.f9[1]); + } + + @Test + public void testGeneralReferenceTypeFieldsClass02() throws Exception { + GeneralReferenceTypeFieldsClass src = (GeneralReferenceTypeFieldsClass) PackUnpackUtil + .newEnhancedInstance(GeneralReferenceTypeFieldsClass.class); + src.f0 = 0; + src.f1 = 1; + src.f2 = 2; + src.f3 = (long) 3; + src.f4 = (float) 4; + src.f5 = (double) 5; + src.f6 = false; + src.f7 = new BigInteger("7"); + src.f8 = "8"; + src.f9 = new byte[] { 0x01, 0x02 }; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + Iterator it = pac.iterator(); + assertTrue(it.hasNext()); + MessagePackObject mpo = it.next(); + GeneralReferenceTypeFieldsClass dst = (GeneralReferenceTypeFieldsClass) PackUnpackUtil + .initEnhancedInstance(mpo, + GeneralReferenceTypeFieldsClass.class); + assertEquals(src.f0, dst.f0); + assertEquals(src.f1, dst.f1); + assertEquals(src.f2, dst.f2); + assertEquals(src.f3, dst.f3); + assertEquals(src.f4, dst.f4); + assertEquals(src.f5, dst.f5); + assertEquals(src.f6, dst.f6); + assertEquals(src.f7, dst.f7); + assertEquals(src.f8, dst.f8); + assertEquals(src.f9[0], dst.f9[0]); + assertEquals(src.f9[1], dst.f9[1]); + assertFalse(it.hasNext()); + } + + @MessagePackUnpackable + public static class GeneralReferenceTypeFieldsClass { + public Byte f0; + public Short f1; + public Integer f2; + public Long f3; + public Float f4; + public Double f5; + public Boolean f6; + public BigInteger f7; + public String f8; + public byte[] f9; + + public GeneralReferenceTypeFieldsClass() { + } + } + + public void testListTypes01() throws Exception { + SampleListTypes src = (SampleListTypes) PackUnpackUtil + .newEnhancedInstance(SampleListTypes.class); + src.f0 = new ArrayList(); + src.f1 = new ArrayList(); + src.f1.add(1); + src.f1.add(2); + src.f1.add(3); + src.f2 = new ArrayList(); + src.f2.add("e1"); + src.f2.add("e2"); + src.f2.add("e3"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + SampleListTypes dst = (SampleListTypes) PackUnpackUtil + .newEnhancedInstance(SampleListTypes.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + pac.unpack((MessageUnpackable) dst); + assertEquals(src.f0.size(), dst.f0.size()); + assertEquals(src.f1.size(), dst.f1.size()); + for (int i = 0; i < src.f1.size(); ++i) { + assertEquals(src.f1.get(i), dst.f1.get(i)); + } + assertEquals(src.f2.size(), dst.f2.size()); + for (int i = 0; i < src.f2.size(); ++i) { + assertEquals(src.f2.get(i), dst.f2.get(i)); + } + } + + public void testListTypes02() throws Exception { + SampleListTypes src = (SampleListTypes) PackUnpackUtil + .newEnhancedInstance(SampleListTypes.class); + src.f0 = new ArrayList(); + src.f1 = new ArrayList(); + src.f1.add(1); + src.f1.add(2); + src.f1.add(3); + src.f2 = new ArrayList(); + src.f2.add("e1"); + src.f2.add("e2"); + src.f2.add("e3"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + Iterator it = pac.iterator(); + assertTrue(it.hasNext()); + MessagePackObject mpo = it.next(); + SampleListTypes dst = (SampleListTypes) PackUnpackUtil + .initEnhancedInstance(mpo, SampleListTypes.class); + assertEquals(src.f0.size(), dst.f0.size()); + assertEquals(src.f1.size(), dst.f1.size()); + for (int i = 0; i < src.f1.size(); ++i) { + assertEquals(src.f1.get(i), dst.f1.get(i)); + } + assertEquals(src.f2.size(), dst.f2.size()); + for (int i = 0; i < src.f2.size(); ++i) { + assertEquals(src.f2.get(i), dst.f2.get(i)); + } + assertFalse(it.hasNext()); + } + + @MessagePackUnpackable + public static class SampleListTypes { + public List f0; + public List f1; + public List f2; + + public SampleListTypes() { + } + } + + public void testMapTypes01() throws Exception { + SampleMapTypes src = (SampleMapTypes) PackUnpackUtil + .newEnhancedInstance(SampleMapTypes.class); + src.f0 = new HashMap(); + src.f1 = new HashMap(); + src.f1.put(1, 1); + src.f1.put(2, 2); + src.f1.put(3, 3); + src.f2 = new HashMap(); + src.f2.put("k1", 1); + src.f2.put("k2", 2); + src.f2.put("k3", 3); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + SampleMapTypes dst = (SampleMapTypes) PackUnpackUtil + .newEnhancedInstance(SampleMapTypes.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + pac.unpack((MessageUnpackable) dst); + assertEquals(src.f0.size(), dst.f0.size()); + assertEquals(src.f1.size(), dst.f1.size()); + Iterator srcf1 = src.f1.keySet().iterator(); + Iterator dstf1 = dst.f1.keySet().iterator(); + while (srcf1.hasNext()) { + Integer s1 = srcf1.next(); + Integer d1 = dstf1.next(); + assertEquals(s1, d1); + assertEquals(src.f1.get(s1), dst.f1.get(d1)); + } + assertEquals(src.f2.size(), dst.f2.size()); + Iterator srcf2 = src.f2.keySet().iterator(); + Iterator dstf2 = dst.f2.keySet().iterator(); + while (srcf2.hasNext()) { + String s2 = srcf2.next(); + String d2 = dstf2.next(); + assertEquals(s2, d2); + assertEquals(src.f2.get(s2), dst.f2.get(d2)); + } + } + + public void testMapTypes02() throws Exception { + SampleMapTypes src = (SampleMapTypes) PackUnpackUtil + .newEnhancedInstance(SampleMapTypes.class); + src.f0 = new HashMap(); + src.f1 = new HashMap(); + src.f1.put(1, 1); + src.f1.put(2, 2); + src.f1.put(3, 3); + src.f2 = new HashMap(); + src.f2.put("k1", 1); + src.f2.put("k2", 2); + src.f2.put("k3", 3); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + Iterator it = pac.iterator(); + assertTrue(it.hasNext()); + MessagePackObject mpo = it.next(); + SampleMapTypes dst = (SampleMapTypes) PackUnpackUtil + .initEnhancedInstance(mpo, SampleMapTypes.class); + assertEquals(src.f0.size(), dst.f0.size()); + assertEquals(src.f1.size(), dst.f1.size()); + Iterator srcf1 = src.f1.keySet().iterator(); + Iterator dstf1 = dst.f1.keySet().iterator(); + while (srcf1.hasNext()) { + Integer s1 = srcf1.next(); + Integer d1 = dstf1.next(); + assertEquals(s1, d1); + assertEquals(src.f1.get(s1), dst.f1.get(d1)); + } + assertEquals(src.f2.size(), dst.f2.size()); + Iterator srcf2 = src.f2.keySet().iterator(); + Iterator dstf2 = dst.f2.keySet().iterator(); + while (srcf2.hasNext()) { + String s2 = srcf2.next(); + String d2 = dstf2.next(); + assertEquals(s2, d2); + assertEquals(src.f2.get(s2), dst.f2.get(d2)); + } + assertFalse(it.hasNext()); + } + + @MessagePackUnpackable + public static class SampleMapTypes { + public Map f0; + public Map f1; + public Map f2; + + public SampleMapTypes() { + } + } + + @Test + public void testDefaultConstructorModifiers() throws Exception { + try { + PackUnpackUtil.newEnhancedInstance(NoDefaultConstructorClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + try { + PackUnpackUtil + .newEnhancedInstance(PrivateDefaultConstructorClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + try { + PackUnpackUtil + .newEnhancedInstance(ProtectedDefaultConstructorClass.class); + assertTrue(true); + } catch (PackUnpackUtilException e) { + fail(); + } + assertTrue(true); + try { + PackUnpackUtil + .newEnhancedInstance(PackageDefaultConstructorClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + } + + @MessagePackUnpackable + public static class NoDefaultConstructorClass { + public NoDefaultConstructorClass(int i) { + } + } + + @MessagePackUnpackable + public static class PrivateDefaultConstructorClass { + private PrivateDefaultConstructorClass() { + } + } + + @MessagePackUnpackable + public static class ProtectedDefaultConstructorClass { + protected ProtectedDefaultConstructorClass() { + } + } + + @MessagePackUnpackable + public static class PackageDefaultConstructorClass { + PackageDefaultConstructorClass() { + } + } + + @Test + public void testClassModifiers() throws Exception { + try { + PackUnpackUtil.newEnhancedInstance(PrivateModifierClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + try { + PackUnpackUtil.newEnhancedInstance(ProtectedModifierClass.class); + assertTrue(true); + } catch (PackUnpackUtilException e) { + fail(); + } + assertTrue(true); + try { + PackUnpackUtil.newEnhancedInstance(PackageModifierClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + } + + @MessagePackUnpackable + private static class PrivateModifierClass { + } + + @MessagePackUnpackable + protected static class ProtectedModifierClass { + protected ProtectedModifierClass() { + } + } + + @MessagePackUnpackable + static class PackageModifierClass { + } + + @Test + public void testFinalClassAndAbstractClass() throws Exception { + try { + PackUnpackUtil.newEnhancedInstance(FinalModifierClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + try { + PackUnpackUtil.newEnhancedInstance(AbstractModifierClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + } + + @MessagePackUnpackable + public final static class FinalModifierClass { + } + + @MessagePackUnpackable + public abstract static class AbstractModifierClass { + } + + @Test + public void testInterfaceAndEnumType() throws Exception { + try { + PackUnpackUtil.newEnhancedInstance(SampleInterface.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + try { + PackUnpackUtil.newEnhancedInstance(SampleEnum.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + } + + @MessagePackUnpackable + public interface SampleInterface { + } + + @MessagePackUnpackable + public enum SampleEnum { + } + + @Test + public void testFieldModifiers01() throws Exception { + FieldModifiersClass src = (FieldModifiersClass) PackUnpackUtil + .newEnhancedInstance(FieldModifiersClass.class); + src.f0 = 0; + src.f2 = 2; + src.f3 = 3; + src.f4 = 4; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + FieldModifiersClass dst = (FieldModifiersClass) PackUnpackUtil + .newEnhancedInstance(FieldModifiersClass.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + pac.unpack((MessageUnpackable) dst); + assertTrue(src.f0 == dst.f0); + assertTrue(src.f1 == dst.f1); + assertTrue(src.f2 != dst.f2); + assertTrue(src.f3 == dst.f3); + assertTrue(src.f4 != dst.f4); + } + + @Test + public void testFieldModifiers02() throws Exception { + FieldModifiersClass src = (FieldModifiersClass) PackUnpackUtil + .newEnhancedInstance(FieldModifiersClass.class); + src.f0 = 0; + src.f2 = 2; + src.f3 = 3; + src.f4 = 4; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + Iterator it = pac.iterator(); + assertTrue(it.hasNext()); + MessagePackObject mpo = it.next(); + FieldModifiersClass dst = (FieldModifiersClass) PackUnpackUtil + .initEnhancedInstance(mpo, FieldModifiersClass.class); + assertTrue(src.f0 == dst.f0); + assertTrue(src.f1 == dst.f1); + assertTrue(src.f2 != dst.f2); + assertTrue(src.f3 == dst.f3); + assertTrue(src.f4 != dst.f4); + assertFalse(it.hasNext()); + } + + @MessagePackUnpackable + public static class FieldModifiersClass { + public int f0; + public final int f1 = 1; + private int f2; + protected int f3; + int f4; + + public FieldModifiersClass() { + } + } + + @Test + public void testNestedAnnotatedFieldClass01() throws Exception { + NestedClass src2 = (NestedClass) PackUnpackUtil + .newEnhancedInstance(NestedClass.class); + BaseClass src = (BaseClass) PackUnpackUtil + .newEnhancedInstance(BaseClass.class); + src.f0 = 0; + src2.f2 = 2; + src.f1 = src2; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + BaseClass dst = (BaseClass) PackUnpackUtil + .newEnhancedInstance(BaseClass.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + pac.unpack((MessageUnpackable) dst); + assertTrue(src.f0 == dst.f0); + assertTrue(src2.f2 == dst.f1.f2); + } + + @Test + public void testNestedAnnotatedFieldClass02() throws Exception { + NestedClass src2 = (NestedClass) PackUnpackUtil + .newEnhancedInstance(NestedClass.class); + BaseClass src = (BaseClass) PackUnpackUtil + .newEnhancedInstance(BaseClass.class); + src.f0 = 0; + src2.f2 = 2; + src.f1 = src2; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + Iterator it = pac.iterator(); + assertTrue(it.hasNext()); + MessagePackObject mpo = it.next(); + BaseClass dst = (BaseClass) PackUnpackUtil.initEnhancedInstance(mpo, + BaseClass.class); + assertTrue(src.f0 == dst.f0); + assertTrue(src2.f2 == dst.f1.f2); + assertFalse(it.hasNext()); + } + + @MessagePackUnpackable + public static class BaseClass { + public int f0; + public NestedClass f1; + + public BaseClass() { + } + } + + @MessagePackUnpackable + public static class NestedClass { + public int f2; + + public NestedClass() { + } + } + + @Test + public void testExtendedClass01() throws Exception { + SampleSubClass src = (SampleSubClass) PackUnpackUtil + .newEnhancedInstance(SampleSubClass.class); + src.f0 = 0; + src.f2 = 2; + src.f3 = 3; + src.f4 = 4; + src.f5 = 5; + src.f8 = 8; + src.f9 = 9; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + SampleSubClass dst = (SampleSubClass) PackUnpackUtil + .newEnhancedInstance(SampleSubClass.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + pac.unpack((MessageUnpackable) dst); + assertTrue(src.f0 == dst.f0); + assertTrue(src.f1 == dst.f1); + assertTrue(src.f2 != dst.f2); + assertTrue(src.f3 == dst.f3); + assertTrue(src.f4 != dst.f4); + assertTrue(src.f5 == dst.f5); + assertTrue(src.f6 == dst.f6); + assertTrue(src.f8 == dst.f8); + assertTrue(src.f9 != dst.f9); + } + + @Test + public void testExtendedClass02() throws Exception { + SampleSubClass src = (SampleSubClass) PackUnpackUtil + .newEnhancedInstance(SampleSubClass.class); + src.f0 = 0; + src.f2 = 2; + src.f3 = 3; + src.f4 = 4; + src.f5 = 5; + src.f8 = 8; + src.f9 = 9; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + Iterator it = pac.iterator(); + assertTrue(it.hasNext()); + MessagePackObject mpo = it.next(); + SampleSubClass dst = (SampleSubClass) PackUnpackUtil + .initEnhancedInstance(mpo, SampleSubClass.class); + assertTrue(src.f0 == dst.f0); + assertTrue(src.f1 == dst.f1); + assertTrue(src.f2 != dst.f2); + assertTrue(src.f3 == dst.f3); + assertTrue(src.f4 != dst.f4); + assertTrue(src.f5 == dst.f5); + assertTrue(src.f6 == dst.f6); + assertTrue(src.f8 == dst.f8); + assertTrue(src.f9 != dst.f9); + assertFalse(it.hasNext()); + } + + @MessagePackUnpackable + public static class SampleSubClass extends SampleSuperClass { + public int f0; + public final int f1 = 1; + private int f2; + protected int f3; + int f4; + + public SampleSubClass() { + } + } + + public static class SampleSuperClass { + public int f5; + public final int f6 = 2; + private int f7; + protected int f8; + int f9; + + public SampleSuperClass() { + } + } +} diff --git a/java/src/test/resources/cases.json b/java/src/test/resources/cases.json new file mode 100644 index 00000000..fd390d48 --- /dev/null +++ b/java/src/test/resources/cases.json @@ -0,0 +1 @@ +[false,true,null,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,127,127,255,65535,4294967295,-32,-32,-128,-32768,-2147483648,0.0,-0.0,1.0,-1.0,"a","a","a","","","",[0],[0],[0],[],[],[],{},{},{},{"a":97},{"a":97},{"a":97},[[]],[["a"]]] \ No newline at end of file diff --git a/java/src/test/resources/cases.mpac b/java/src/test/resources/cases.mpac new file mode 100644 index 00000000..5ec08c6a Binary files /dev/null and b/java/src/test/resources/cases.mpac differ diff --git a/java/src/test/resources/cases_compact.mpac b/java/src/test/resources/cases_compact.mpac new file mode 100644 index 00000000..8812442d Binary files /dev/null and b/java/src/test/resources/cases_compact.mpac differ diff --git a/msgpack/pack_template.h b/msgpack/pack_template.h index daa8f810..da54c364 100644 --- a/msgpack/pack_template.h +++ b/msgpack/pack_template.h @@ -69,7 +69,7 @@ do { \ } else { \ /* unsigned 16 */ \ unsigned char buf[3]; \ - buf[0] = 0xcd; _msgpack_store16(&buf[1], d); \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ msgpack_pack_append_buffer(x, buf, 3); \ } \ } while(0) @@ -89,12 +89,12 @@ do { \ if(d < (1<<16)) { \ /* unsigned 16 */ \ unsigned char buf[3]; \ - buf[0] = 0xcd; _msgpack_store16(&buf[1], d); \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ msgpack_pack_append_buffer(x, buf, 3); \ } else { \ /* unsigned 32 */ \ unsigned char buf[5]; \ - buf[0] = 0xce; _msgpack_store32(&buf[1], d); \ + buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ msgpack_pack_append_buffer(x, buf, 5); \ } \ } \ @@ -103,7 +103,7 @@ do { \ #define msgpack_pack_real_uint64(x, d) \ do { \ if(d < (1ULL<<8)) { \ - if(d < (1<<7)) { \ + if(d < (1ULL<<7)) { \ /* fixnum */ \ msgpack_pack_append_buffer(x, &TAKE8_64(d), 1); \ } else { \ @@ -115,12 +115,12 @@ do { \ if(d < (1ULL<<16)) { \ /* unsigned 16 */ \ unsigned char buf[3]; \ - buf[0] = 0xcd; _msgpack_store16(&buf[1], d); \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ msgpack_pack_append_buffer(x, buf, 3); \ } else if(d < (1ULL<<32)) { \ /* unsigned 32 */ \ unsigned char buf[5]; \ - buf[0] = 0xce; _msgpack_store32(&buf[1], d); \ + buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ msgpack_pack_append_buffer(x, buf, 5); \ } else { \ /* unsigned 64 */ \ @@ -149,7 +149,7 @@ do { \ if(d < -(1<<7)) { \ /* signed 16 */ \ unsigned char buf[3]; \ - buf[0] = 0xd1; _msgpack_store16(&buf[1], d); \ + buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \ msgpack_pack_append_buffer(x, buf, 3); \ } else { \ /* signed 8 */ \ @@ -167,7 +167,7 @@ do { \ } else { \ /* unsigned 16 */ \ unsigned char buf[3]; \ - buf[0] = 0xcd; _msgpack_store16(&buf[1], d); \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ msgpack_pack_append_buffer(x, buf, 3); \ } \ } \ @@ -179,12 +179,12 @@ do { \ if(d < -(1<<15)) { \ /* signed 32 */ \ unsigned char buf[5]; \ - buf[0] = 0xd2; _msgpack_store32(&buf[1], d); \ + buf[0] = 0xd2; _msgpack_store32(&buf[1], (int32_t)d); \ msgpack_pack_append_buffer(x, buf, 5); \ } else if(d < -(1<<7)) { \ /* signed 16 */ \ unsigned char buf[3]; \ - buf[0] = 0xd1; _msgpack_store16(&buf[1], d); \ + buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \ msgpack_pack_append_buffer(x, buf, 3); \ } else { \ /* signed 8 */ \ @@ -202,12 +202,12 @@ do { \ } else if(d < (1<<16)) { \ /* unsigned 16 */ \ unsigned char buf[3]; \ - buf[0] = 0xcd; _msgpack_store16(&buf[1], d); \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ msgpack_pack_append_buffer(x, buf, 3); \ } else { \ /* unsigned 32 */ \ unsigned char buf[5]; \ - buf[0] = 0xce; _msgpack_store32(&buf[1], d); \ + buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ msgpack_pack_append_buffer(x, buf, 5); \ } \ } \ @@ -225,14 +225,14 @@ do { \ } else { \ /* signed 32 */ \ unsigned char buf[5]; \ - buf[0] = 0xd2; _msgpack_store32(&buf[1], d); \ + buf[0] = 0xd2; _msgpack_store32(&buf[1], (int32_t)d); \ msgpack_pack_append_buffer(x, buf, 5); \ } \ } else { \ if(d < -(1<<7)) { \ /* signed 16 */ \ unsigned char buf[3]; \ - buf[0] = 0xd1; _msgpack_store16(&buf[1], d); \ + buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \ msgpack_pack_append_buffer(x, buf, 3); \ } else { \ /* signed 8 */ \ @@ -252,14 +252,14 @@ do { \ } else { \ /* unsigned 16 */ \ unsigned char buf[3]; \ - buf[0] = 0xcd; _msgpack_store16(&buf[1], d); \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ msgpack_pack_append_buffer(x, buf, 3); \ } \ } else { \ if(d < (1LL<<32)) { \ /* unsigned 32 */ \ unsigned char buf[5]; \ - buf[0] = 0xce; _msgpack_store32(&buf[1], d); \ + buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ msgpack_pack_append_buffer(x, buf, 5); \ } else { \ /* unsigned 64 */ \ @@ -272,63 +272,63 @@ do { \ } while(0) -#ifdef msgpack_pack_inline_func_fastint +#ifdef msgpack_pack_inline_func_fixint -msgpack_pack_inline_func_fastint(_uint8)(msgpack_pack_user x, uint8_t d) +msgpack_pack_inline_func_fixint(_uint8)(msgpack_pack_user x, uint8_t d) { unsigned char buf[2] = {0xcc, TAKE8_8(d)}; msgpack_pack_append_buffer(x, buf, 2); } -msgpack_pack_inline_func_fastint(_uint16)(msgpack_pack_user x, uint16_t d) +msgpack_pack_inline_func_fixint(_uint16)(msgpack_pack_user x, uint16_t d) { unsigned char buf[3]; buf[0] = 0xcd; _msgpack_store16(&buf[1], d); msgpack_pack_append_buffer(x, buf, 3); } -msgpack_pack_inline_func_fastint(_uint32)(msgpack_pack_user x, uint32_t d) +msgpack_pack_inline_func_fixint(_uint32)(msgpack_pack_user x, uint32_t d) { unsigned char buf[5]; buf[0] = 0xce; _msgpack_store32(&buf[1], d); msgpack_pack_append_buffer(x, buf, 5); } -msgpack_pack_inline_func_fastint(_uint64)(msgpack_pack_user x, uint64_t d) +msgpack_pack_inline_func_fixint(_uint64)(msgpack_pack_user x, uint64_t d) { unsigned char buf[9]; buf[0] = 0xcf; _msgpack_store64(&buf[1], d); msgpack_pack_append_buffer(x, buf, 9); } -msgpack_pack_inline_func_fastint(_int8)(msgpack_pack_user x, int8_t d) +msgpack_pack_inline_func_fixint(_int8)(msgpack_pack_user x, int8_t d) { unsigned char buf[2] = {0xd0, TAKE8_8(d)}; msgpack_pack_append_buffer(x, buf, 2); } -msgpack_pack_inline_func_fastint(_int16)(msgpack_pack_user x, int16_t d) +msgpack_pack_inline_func_fixint(_int16)(msgpack_pack_user x, int16_t d) { unsigned char buf[3]; buf[0] = 0xd1; _msgpack_store16(&buf[1], d); msgpack_pack_append_buffer(x, buf, 3); } -msgpack_pack_inline_func_fastint(_int32)(msgpack_pack_user x, int32_t d) +msgpack_pack_inline_func_fixint(_int32)(msgpack_pack_user x, int32_t d) { unsigned char buf[5]; buf[0] = 0xd2; _msgpack_store32(&buf[1], d); msgpack_pack_append_buffer(x, buf, 5); } -msgpack_pack_inline_func_fastint(_int64)(msgpack_pack_user x, int64_t d) +msgpack_pack_inline_func_fixint(_int64)(msgpack_pack_user x, int64_t d) { unsigned char buf[9]; buf[0] = 0xd3; _msgpack_store64(&buf[1], d); msgpack_pack_append_buffer(x, buf, 9); } -#undef msgpack_pack_inline_func_fastint +#undef msgpack_pack_inline_func_fixint #endif @@ -690,11 +690,11 @@ msgpack_pack_inline_func(_array)(msgpack_pack_user x, unsigned int n) msgpack_pack_append_buffer(x, &d, 1); } else if(n < 65536) { unsigned char buf[3]; - buf[0] = 0xdc; _msgpack_store16(&buf[1], n); + buf[0] = 0xdc; _msgpack_store16(&buf[1], (uint16_t)n); msgpack_pack_append_buffer(x, buf, 3); } else { unsigned char buf[5]; - buf[0] = 0xdd; _msgpack_store32(&buf[1], n); + buf[0] = 0xdd; _msgpack_store32(&buf[1], (uint32_t)n); msgpack_pack_append_buffer(x, buf, 5); } } @@ -711,11 +711,11 @@ msgpack_pack_inline_func(_map)(msgpack_pack_user x, unsigned int n) msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); } else if(n < 65536) { unsigned char buf[3]; - buf[0] = 0xde; _msgpack_store16(&buf[1], n); + buf[0] = 0xde; _msgpack_store16(&buf[1], (uint16_t)n); msgpack_pack_append_buffer(x, buf, 3); } else { unsigned char buf[5]; - buf[0] = 0xdf; _msgpack_store32(&buf[1], n); + buf[0] = 0xdf; _msgpack_store32(&buf[1], (uint32_t)n); msgpack_pack_append_buffer(x, buf, 5); } } @@ -732,11 +732,11 @@ msgpack_pack_inline_func(_raw)(msgpack_pack_user x, size_t l) msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); } else if(l < 65536) { unsigned char buf[3]; - buf[0] = 0xda; _msgpack_store16(&buf[1], l); + buf[0] = 0xda; _msgpack_store16(&buf[1], (uint16_t)l); msgpack_pack_append_buffer(x, buf, 3); } else { unsigned char buf[5]; - buf[0] = 0xdb; _msgpack_store32(&buf[1], l); + buf[0] = 0xdb; _msgpack_store32(&buf[1], (uint32_t)l); msgpack_pack_append_buffer(x, buf, 5); } } diff --git a/perl/.gitignore b/perl/.gitignore index f060908d..16568472 100644 --- a/perl/.gitignore +++ b/perl/.gitignore @@ -1,4 +1,5 @@ META.yml +MYMETA.yml Makefile Makefile.old MessagePack.bs @@ -6,8 +7,11 @@ MessagePack.o blib/ inc/ msgpack/ +t/std/ pack.o pm_to_blib unpack.o MANIFEST ppport.h +.testenv/ +xshelper.h diff --git a/perl/Changes b/perl/Changes index b03a9d0c..b717a828 100644 --- a/perl/Changes +++ b/perl/Changes @@ -1,3 +1,71 @@ +0.26 + + - fixed a serious code typo in PP(makamaka) + +0.25 + + (NO FEATURE CHANGES) + - oops. I failed releng. + +0.24 + - Fixed a lot of streaming unpacking issues (tokuhirom, gfx) + - Fixed unpacking issues for 64 bit integers on 32 bit perls (gfx) + - Improved performance, esp. in unpacking (gfx) + +0.23 + + (NO FEATURE CHANGES) + - fixed english docs(hanekomu++) + +0.22 + + - fixed issue on ithreads(broken from 0.21) + +0.21 + + - doc enhancments + - micro performance tuning. + +0.20 + + - first production ready release with PP driver. + +0.16_04 + + - no feature changes + +0.16_02 + + - document enhancement(tokuhirom) + - M::I::XSUtil 0.26 is broken. use 0.27. + +0.16_01 + + - added PP version (used in cases PERL_DATA_MESSAGEPACK=pp or fail to load XS). + - made Makefile.PL PP configurable. + - test_pp in author's test + - modified t/05_preferred_int.t for Win32 + (makamaka) + +0.16 + + - tests on 64bit machines with -Duselongdouble + (reported by andk) + +0.15 + + - better argument validation. + (Dan Kogai) + +0.14 + + - fixed segv on serializing cyclic reference + (Dan Kogai) + +0.13 + + - clearly specify requires_c99(), because msgpack C header requires C99. + 0.12 - PERL_NO_GET_CONTEXT makes horrible dTHXs. remove it. diff --git a/perl/MANIFEST.SKIP b/perl/MANIFEST.SKIP index f6340354..1d2192f5 100644 --- a/perl/MANIFEST.SKIP +++ b/perl/MANIFEST.SKIP @@ -2,6 +2,7 @@ \bCVS\b ^MANIFEST\. ^Makefile$ +^MYMETA\.yml$ ~$ ^# \.old$ @@ -23,3 +24,5 @@ \.o$ \.bs$ ^Data-MessagePack-[0-9.]+/ +^\.testenv/test_pp.pl +^ppport.h$ diff --git a/perl/Makefile.PL b/perl/Makefile.PL index 58ab7c79..219400f2 100644 --- a/perl/Makefile.PL +++ b/perl/Makefile.PL @@ -1,15 +1,47 @@ +# Usage: Makefile.PL --pp # disable XS +# Makefile.PL -g # add -g to the compiler and disable optimization flags use inc::Module::Install; +use Module::Install::XSUtil 0.32; +use Config; + name 'Data-MessagePack'; all_from 'lib/Data/MessagePack.pm'; readme_from('lib/Data/MessagePack.pm'); -perl_version '5.008005'; +perl_version '5.008000'; license 'perl'; -can_cc or die "This module requires a C compiler"; tests 't/*.t'; recursive_author_tests('xt'); -use_ppport 3.19; + + +if ( $] >= 5.008005 and want_xs() ) { + my $has_c99 = c99_available(); # msgpack C library requires C99. + + if ( $has_c99 ) { + requires_c99(); + use_xshelper(); + cc_warnings; + cc_src_paths('xs-src'); + + if($Module::Install::AUTHOR) { + postamble qq{test :: test_pp\n\n}; + } + } + else { + print < 1.89; # old versions of BigInt were broken +} clean_files qw{ *.stackdump @@ -19,10 +51,6 @@ clean_files qw{ cover_db }; -if ($ENV{DEBUG}) { - cc_append_to_ccflags '-g'; -} - # copy modules if ($Module::Install::AUTHOR && -d File::Spec->catfile('..', 'msgpack')) { mkdir 'msgpack' unless -d 'msgpack'; @@ -30,12 +58,56 @@ if ($Module::Install::AUTHOR && -d File::Spec->catfile('..', 'msgpack')) { for my $src (<../msgpack/*.h>) { File::Copy::copy($src, 'msgpack/') or die "copy failed: $!"; } + + mkdir 't/std'; + for my $data(<../test/*.{json,mpac}>) { + File::Copy::copy($data, 't/std') or die "copy failed: $!"; + } } requires 'Test::More' => 0.94; # done_testing test_requires('Test::Requires'); -auto_set_repository(); +test_with_env( test_pp => PERL_DATA_MESSAGEPACK => 'pp' ); + +repository('http://github.com/msgpack/msgpack'); auto_include; WriteAll; +# copied from Makefile.PL in Text::Xslate. +sub test_with_env { + my($name, %env) = @_; + + my $dir = '.testenv'; + if(not -e $dir) { + mkdir $dir or die "Cannot mkdir '.testenv': $!"; + } + clean_files($dir); + + { + open my $out, '>', "$dir/$name.pl" + or die "Cannot open '$dir/$name.pl' for writing: $!"; + print $out "# This file sets the env for 'make $name', \n"; + print $out "# generated by $0 at ", scalar(localtime), ".\n"; + print $out "# DO NOT EDIT THIS FILE DIRECTLY.\n"; + print $out "\n"; + + while(my($name, $value) = each %env) { + printf $out '$ENV{q{%s}} = q{%s};'."\n", $name, $value; + } + } + + # repeat testing for pure Perl mode + # see also ExtUtils::MM_Any::test_via_harness() + + my $t = q{$(FULLPERLRUN) -MExtUtils::Command::MM -e} + .q{ "do q[%s]; test_harness($(TEST_VERBOSE), '$(INST_LIB)', '$(INST_ARCHLIB)')"} + .q{ $(TEST_FILES)}; + + postamble qq{$name :: pure_all\n} + . qq{\t} . q{$(NOECHO) $(ECHO) TESTING: } . $name . qq{\n} + . qq{\t} . sprintf($t, "$dir/$name.pl") . qq{\n\n} + + . qq{testall :: $name\n\n}; + return; +} diff --git a/perl/README b/perl/README index 31aae992..224ff08c 100644 --- a/perl/README +++ b/perl/README @@ -1,34 +1,132 @@ NAME - Data::MessagePack - messagepack + Data::MessagePack - MessagePack serialising/deserialising SYNOPSIS - my $packed = Data::MessagePack->pack($dat); + use Data::MessagePack; + + my $packed = Data::MessagePack->pack($dat); my $unpacked = Data::MessagePack->unpack($dat); DESCRIPTION - Data::MessagePack is a binary packer for perl. + This module converts Perl data structures to MessagePack and vice versa. + +ABOUT MESSAGEPACK FORMAT + MessagePack is a binary-based efficient object serialization format. It + enables to exchange structured objects between many languages like JSON. + But unlike JSON, it is very fast and small. + + ADVANTAGES + PORTABLE + The MessagePack format does not depend on language nor byte order. + + SMALL IN SIZE + say length(JSON::XS::encode_json({a=>1, b=>2})); # => 13 + say length(Storable::nfreeze({a=>1, b=>2})); # => 21 + say length(Data::MessagePack->pack({a=>1, b=>2})); # => 7 + + The MessagePack format saves memory than JSON and Storable format. + + STREAMING DESERIALIZER + MessagePack supports streaming deserializer. It is useful for + networking such as RPC. See Data::MessagePack::Unpacker for details. + + If you want to get more information about the MessagePack format, please + visit to . METHODS - my $packed = Data::MessagePack->pack($data); - pack the $data to messagepack format string. + my $packed = Data::MessagePack->pack($data[, $max_depth]); + Pack the $data to messagepack format string. + + This method throws an exception when the perl structure is nested + more than $max_depth levels(default: 512) in order to detect + circular references. + + Data::MessagePack->pack() throws an exception when encountering + blessed object, because MessagePack is language-independent format. my $unpacked = Data::MessagePack->unpack($msgpackstr); - unpack the $msgpackstr to messagepack format string. + unpack the $msgpackstr to a MessagePack format string. Configuration Variables $Data::MessagePack::PreferInteger - Pack the string as int when the value looks like int(EXPERIMENTAL). + Packs a string as an integer, when it looks like an integer. + +SPEED + This is a result of benchmark/serialize.pl and benchmark/deserialize.pl + on my SC440(Linux 2.6.32-23-server #37-Ubuntu SMP). (You should + benchmark them with your data if the speed matters, of course.) + + -- serialize + JSON::XS: 2.3 + Data::MessagePack: 0.24 + Storable: 2.21 + Benchmark: running json, mp, storable for at least 1 CPU seconds... + json: 1 wallclock secs ( 1.00 usr + 0.01 sys = 1.01 CPU) @ 141939.60/s (n=143359) + mp: 1 wallclock secs ( 1.06 usr + 0.00 sys = 1.06 CPU) @ 355500.94/s (n=376831) + storable: 1 wallclock secs ( 1.12 usr + 0.00 sys = 1.12 CPU) @ 38399.11/s (n=43007) + Rate storable json mp + storable 38399/s -- -73% -89% + json 141940/s 270% -- -60% + mp 355501/s 826% 150% -- + + -- deserialize + JSON::XS: 2.3 + Data::MessagePack: 0.24 + Storable: 2.21 + Benchmark: running json, mp, storable for at least 1 CPU seconds... + json: 0 wallclock secs ( 1.05 usr + 0.00 sys = 1.05 CPU) @ 179442.86/s (n=188415) + mp: 0 wallclock secs ( 1.01 usr + 0.00 sys = 1.01 CPU) @ 212909.90/s (n=215039) + storable: 2 wallclock secs ( 1.14 usr + 0.00 sys = 1.14 CPU) @ 114974.56/s (n=131071) + Rate storable json mp + storable 114975/s -- -36% -46% + json 179443/s 56% -- -16% + mp 212910/s 85% 19% -- + +CAVEAT + Unpacking 64 bit integers + This module can unpack 64 bit integers even if your perl does not + support them (i.e. where "perl -V:ivsize" is 4), but you cannot + calculate these values unless you use "Math::BigInt". + +TODO + Error handling + MessagePack cannot deal with complex scalars such as object + references, filehandles, and code references. We should report the + errors more kindly. + + Streaming deserializer + The current implementation of the streaming deserializer does not + have internal buffers while some other bindings (such as Ruby + binding) does. This limitation will astonish those who try to unpack + byte streams with an arbitrary buffer size (e.g. + "while(read($socket, $buffer, $arbitrary_buffer_size)) { ... }"). We + should implement the internal buffer for the unpacker. AUTHORS Tokuhiro Matsuno + Makamaka Hannyaharamitu + + gfx + THANKS TO Jun Kuriyama + Dan Kogai + + FURUHASHI Sadayuki + + hanekomu + LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. SEE ALSO - + is the official web site for the MessagePack + format. + + Data::MessagePack::Unpacker + + AnyEvent::MPRPC diff --git a/perl/benchmark/data.pl b/perl/benchmark/data.pl new file mode 100755 index 00000000..6908d1cc --- /dev/null +++ b/perl/benchmark/data.pl @@ -0,0 +1,6 @@ ++{ + "method" => "handleMessage", + "params" => [ "user1", "we were just talking", "foo\nbar\nbaz\nqux" ], + "id" => undef, + "array" => [ 1, 1024, 70000, -5, 1e5, 1e7, 1, 0, 3.14, sqrt(2), 1 .. 100 ], +}; diff --git a/perl/benchmark/deserialize.pl b/perl/benchmark/deserialize.pl index fd21f086..faa2582f 100644 --- a/perl/benchmark/deserialize.pl +++ b/perl/benchmark/deserialize.pl @@ -1,20 +1,27 @@ use strict; use warnings; use Data::MessagePack; -use JSON::XS; +use JSON; +use Storable; use Benchmark ':all'; -my $a = [0..2**24]; -my $j = JSON::XS::encode_json($a); +#$Data::MessagePack::PreferInteger = 1; + +my $a = do 'benchmark/data.pl'; + +my $j = JSON::encode_json($a); my $m = Data::MessagePack->pack($a); +my $s = Storable::freeze($a); print "-- deserialize\n"; -print "JSON::XS: $JSON::XS::VERSION\n"; +print "$JSON::Backend: ", $JSON::Backend->VERSION, "\n"; print "Data::MessagePack: $Data::MessagePack::VERSION\n"; -cmpthese( +print "Storable: $Storable::VERSION\n"; +cmpthese timethese( -1 => { - json => sub { JSON::XS::decode_json($j) }, - mp => sub { Data::MessagePack->unpack($m) }, + json => sub { JSON::decode_json($j) }, + mp => sub { Data::MessagePack->unpack($m) }, + storable => sub { Storable::thaw($s) }, } ); diff --git a/perl/benchmark/serialize.pl b/perl/benchmark/serialize.pl index 626ae039..4982ff61 100644 --- a/perl/benchmark/serialize.pl +++ b/perl/benchmark/serialize.pl @@ -1,18 +1,21 @@ use strict; use warnings; use Data::MessagePack; -use JSON::XS; +use JSON; +use Storable; use Benchmark ':all'; -my $a = [0..2**24]; +my $a = do 'benchmark/data.pl'; print "-- serialize\n"; -print "JSON::XS: $JSON::XS::VERSION\n"; +print "$JSON::Backend: ", $JSON::Backend->VERSION, "\n"; print "Data::MessagePack: $Data::MessagePack::VERSION\n"; -cmpthese( +print "Storable: $Storable::VERSION\n"; +cmpthese timethese( -1 => { - json => sub { JSON::XS::encode_json($a) }, - mp => sub { Data::MessagePack->pack($a) }, + json => sub { JSON::encode_json($a) }, + storable => sub { Storable::freeze($a) }, + mp => sub { Data::MessagePack->pack($a) }, } ); diff --git a/perl/lib/Data/MessagePack.pm b/perl/lib/Data/MessagePack.pm index dcc713d5..f6e68eb3 100644 --- a/perl/lib/Data/MessagePack.pm +++ b/perl/lib/Data/MessagePack.pm @@ -1,46 +1,105 @@ package Data::MessagePack; use strict; use warnings; -use XSLoader; use 5.008001; -our $VERSION = '0.12'; +our $VERSION = '0.26'; our $PreferInteger = 0; -our $true = do { bless \(my $dummy = 1), "Data::MessagePack::Boolean" }; -our $false = do { bless \(my $dummy = 0), "Data::MessagePack::Boolean" }; -sub true () { $true } -sub false () { $false } +sub true () { + require Data::MessagePack::Boolean; + no warnings 'once', 'redefine'; + my $t = $Data::MessagePack::Boolean::true; + *true = sub (){ $t }; + return $t; +} -XSLoader::load(__PACKAGE__, $VERSION); +sub false () { + require Data::MessagePack::Boolean; + no warnings 'once', 'redefine'; + my $f = $Data::MessagePack::Boolean::false; + *false = sub (){ $f }; + return $f; +} + +if ( !__PACKAGE__->can('pack') ) { # this idea comes from Text::Xslate + my $backend = $ENV{PERL_DATA_MESSAGEPACK} || ($ENV{PERL_ONLY} ? 'pp' : ''); + if ( $backend !~ /\b pp \b/xms ) { + eval { + require XSLoader; + XSLoader::load(__PACKAGE__, $VERSION); + }; + die $@ if $@ && $backend =~ /\b xs \b/xms; # force XS + } + if ( !__PACKAGE__->can('pack') ) { + require 'Data/MessagePack/PP.pm'; + } +} 1; __END__ =head1 NAME -Data::MessagePack - messagepack +Data::MessagePack - MessagePack serialising/deserialising =head1 SYNOPSIS - my $packed = Data::MessagePack->pack($dat); + use Data::MessagePack; + + my $packed = Data::MessagePack->pack($dat); my $unpacked = Data::MessagePack->unpack($dat); =head1 DESCRIPTION -Data::MessagePack is a binary packer for perl. +This module converts Perl data structures to MessagePack and vice versa. + +=head1 ABOUT MESSAGEPACK FORMAT + +MessagePack is a binary-based efficient object serialization format. +It enables to exchange structured objects between many languages like JSON. +But unlike JSON, it is very fast and small. + +=head2 ADVANTAGES + +=over 4 + +=item PORTABLE + +The MessagePack format does not depend on language nor byte order. + +=item SMALL IN SIZE + + say length(JSON::XS::encode_json({a=>1, b=>2})); # => 13 + say length(Storable::nfreeze({a=>1, b=>2})); # => 21 + say length(Data::MessagePack->pack({a=>1, b=>2})); # => 7 + +The MessagePack format saves memory than JSON and Storable format. + +=item STREAMING DESERIALIZER + +MessagePack supports streaming deserializer. It is useful for networking such as RPC. +See L for details. + +=back + +If you want to get more information about the MessagePack format, please visit to L. =head1 METHODS =over 4 -=item my $packed = Data::MessagePack->pack($data); +=item my $packed = Data::MessagePack->pack($data[, $max_depth]); -pack the $data to messagepack format string. +Pack the $data to messagepack format string. + +This method throws an exception when the perl structure is nested more than $max_depth levels(default: 512) in order to detect circular references. + +Data::MessagePack->pack() throws an exception when encountering blessed object, because MessagePack is language-independent format. =item my $unpacked = Data::MessagePack->unpack($msgpackstr); -unpack the $msgpackstr to messagepack format string. +unpack the $msgpackstr to a MessagePack format string. =back @@ -50,7 +109,65 @@ unpack the $msgpackstr to messagepack format string. =item $Data::MessagePack::PreferInteger -Pack the string as int when the value looks like int(EXPERIMENTAL). +Packs a string as an integer, when it looks like an integer. + +=back + +=head1 SPEED + +This is a result of benchmark/serialize.pl and benchmark/deserialize.pl on my SC440(Linux 2.6.32-23-server #37-Ubuntu SMP). +(You should benchmark them with B data if the speed matters, of course.) + + -- serialize + JSON::XS: 2.3 + Data::MessagePack: 0.24 + Storable: 2.21 + Benchmark: running json, mp, storable for at least 1 CPU seconds... + json: 1 wallclock secs ( 1.00 usr + 0.01 sys = 1.01 CPU) @ 141939.60/s (n=143359) + mp: 1 wallclock secs ( 1.06 usr + 0.00 sys = 1.06 CPU) @ 355500.94/s (n=376831) + storable: 1 wallclock secs ( 1.12 usr + 0.00 sys = 1.12 CPU) @ 38399.11/s (n=43007) + Rate storable json mp + storable 38399/s -- -73% -89% + json 141940/s 270% -- -60% + mp 355501/s 826% 150% -- + + -- deserialize + JSON::XS: 2.3 + Data::MessagePack: 0.24 + Storable: 2.21 + Benchmark: running json, mp, storable for at least 1 CPU seconds... + json: 0 wallclock secs ( 1.05 usr + 0.00 sys = 1.05 CPU) @ 179442.86/s (n=188415) + mp: 0 wallclock secs ( 1.01 usr + 0.00 sys = 1.01 CPU) @ 212909.90/s (n=215039) + storable: 2 wallclock secs ( 1.14 usr + 0.00 sys = 1.14 CPU) @ 114974.56/s (n=131071) + Rate storable json mp + storable 114975/s -- -36% -46% + json 179443/s 56% -- -16% + mp 212910/s 85% 19% -- + +=head1 CAVEAT + +=head2 Unpacking 64 bit integers + +This module can unpack 64 bit integers even if your perl does not support them +(i.e. where C<< perl -V:ivsize >> is 4), but you cannot calculate these values +unless you use C. + +=head1 TODO + +=over + +=item Error handling + +MessagePack cannot deal with complex scalars such as object references, +filehandles, and code references. We should report the errors more kindly. + +=item Streaming deserializer + +The current implementation of the streaming deserializer does not have internal +buffers while some other bindings (such as Ruby binding) does. This limitation +will astonish those who try to unpack byte streams with an arbitrary buffer size +(e.g. C<< while(read($socket, $buffer, $arbitrary_buffer_size)) { ... } >>). +We should implement the internal buffer for the unpacker. =back @@ -58,17 +175,31 @@ Pack the string as int when the value looks like int(EXPERIMENTAL). Tokuhiro Matsuno +Makamaka Hannyaharamitu + +gfx + =head1 THANKS TO Jun Kuriyama +Dan Kogai + +FURUHASHI Sadayuki + +hanekomu + =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. - =head1 SEE ALSO -L +L is the official web site for the MessagePack format. +L + +L + +=cut diff --git a/perl/lib/Data/MessagePack/Boolean.pm b/perl/lib/Data/MessagePack/Boolean.pm new file mode 100644 index 00000000..2bb3ecad --- /dev/null +++ b/perl/lib/Data/MessagePack/Boolean.pm @@ -0,0 +1,14 @@ +package Data::MessagePack::Boolean; +use strict; +use overload + 'bool' => sub { ${ $_[0] } }, + '0+' => sub { ${ $_[0] } }, + '""' => sub { ${ $_[0] } ? 'true' : 'false' }, + + fallback => 1, +; + +our $true = do { bless \(my $dummy = 1) }; +our $false = do { bless \(my $dummy = 0) }; + +1; diff --git a/perl/lib/Data/MessagePack/PP.pm b/perl/lib/Data/MessagePack/PP.pm new file mode 100644 index 00000000..5dccc0bb --- /dev/null +++ b/perl/lib/Data/MessagePack/PP.pm @@ -0,0 +1,586 @@ +package Data::MessagePack::PP; +use 5.008001; +use strict; +use warnings; +no warnings 'recursion'; + +use Carp (); +use B (); + +# See also +# http://redmine.msgpack.org/projects/msgpack/wiki/FormatSpec +# http://cpansearch.perl.org/src/YAPPO/Data-Model-0.00006/lib/Data/Model/Driver/Memcached.pm +# http://frox25.no-ip.org/~mtve/wiki/MessagePack.html : reference to using CORE::pack, CORE::unpack + +BEGIN { + my $unpack_int64_slow; + my $unpack_uint64_slow; + + if(!eval { pack 'Q', 1 }) { # don't have quad types + $unpack_int64_slow = sub { + require Math::BigInt; + my $high = unpack_uint32( $_[0], $_[1] ); + my $low = unpack_uint32( $_[0], $_[1] + 4); + + if($high < 0xF0000000) { # positive + $high = Math::BigInt->new( $high ); + $low = Math::BigInt->new( $low ); + return +($high << 32 | $low)->bstr; + } + else { # negative + $high = Math::BigInt->new( ~$high ); + $low = Math::BigInt->new( ~$low ); + return +( -($high << 32 | $low + 1) )->bstr; + } + }; + $unpack_uint64_slow = sub { + require Math::BigInt; + my $high = Math::BigInt->new( unpack_uint32( $_[0], $_[1]) ); + my $low = Math::BigInt->new( unpack_uint32( $_[0], $_[1] + 4) ); + return +($high << 32 | $low)->bstr; + }; + } + + *unpack_uint16 = sub { return unpack 'n', substr( $_[0], $_[1], 2 ) }; + *unpack_uint32 = sub { return unpack 'N', substr( $_[0], $_[1], 4 ) }; + + # for pack and unpack compatibility + if ( $] < 5.010 ) { + # require $Config{byteorder}; my $bo_is_le = ( $Config{byteorder} =~ /^1234/ ); + # which better? + my $bo_is_le = unpack ( 'd', "\x00\x00\x00\x00\x00\x00\xf0\x3f") == 1; # 1.0LE + + *unpack_int16 = sub { + my $v = unpack 'n', substr( $_[0], $_[1], 2 ); + return $v ? $v - 0x10000 : 0; + }; + *unpack_int32 = sub { + no warnings; # avoid for warning about Hexadecimal number + my $v = unpack 'N', substr( $_[0], $_[1], 4 ); + return $v ? $v - 0x100000000 : 0; + }; + + # In reality, since 5.9.2 '>' is introduced. but 'n!' and 'N!'? + if($bo_is_le) { + *pack_uint64 = sub { + my @v = unpack( 'V2', pack( 'Q', $_[0] ) ); + return pack 'CN2', 0xcf, @v[1,0]; + }; + *pack_int64 = sub { + my @v = unpack( 'V2', pack( 'q', $_[0] ) ); + return pack 'CN2', 0xd3, @v[1,0]; + }; + *pack_double = sub { + my @v = unpack( 'V2', pack( 'd', $_[0] ) ); + return pack 'CN2', 0xcb, @v[1,0]; + }; + + *unpack_float = sub { + my @v = unpack( 'v2', substr( $_[0], $_[1], 4 ) ); + return unpack( 'f', pack( 'n2', @v[1,0] ) ); + }; + *unpack_double = sub { + my @v = unpack( 'V2', substr( $_[0], $_[1], 8 ) ); + return unpack( 'd', pack( 'N2', @v[1,0] ) ); + }; + + *unpack_int64 = $unpack_int64_slow || sub { + my @v = unpack( 'V*', substr( $_[0], $_[1], 8 ) ); + return unpack( 'q', pack( 'N2', @v[1,0] ) ); + }; + *unpack_uint64 = $unpack_uint64_slow || sub { + my @v = unpack( 'V*', substr( $_[0], $_[1], 8 ) ); + return unpack( 'Q', pack( 'N2', @v[1,0] ) ); + }; + } + else { # big endian + *pack_uint64 = sub { return pack 'CQ', 0xcf, $_[0]; }; + *pack_int64 = sub { return pack 'Cq', 0xd3, $_[0]; }; + *pack_double = sub { return pack 'Cd', 0xcb, $_[0]; }; + + *unpack_float = sub { return unpack( 'f', substr( $_[0], $_[1], 4 ) ); }; + *unpack_double = sub { return unpack( 'd', substr( $_[0], $_[1], 8 ) ); }; + *unpack_int64 = $unpack_int64_slow || sub { unpack 'q', substr( $_[0], $_[1], 8 ); }; + *unpack_uint64 = $unpack_uint64_slow || sub { unpack 'Q', substr( $_[0], $_[1], 8 ); }; + } + } + else { + *pack_uint64 = sub { return pack 'CQ>', 0xcf, $_[0]; }; + *pack_int64 = sub { return pack 'Cq>', 0xd3, $_[0]; }; + *pack_double = sub { return pack 'Cd>', 0xcb, $_[0]; }; + + *unpack_float = sub { return unpack( 'f>', substr( $_[0], $_[1], 4 ) ); }; + *unpack_double = sub { return unpack( 'd>', substr( $_[0], $_[1], 8 ) ); }; + *unpack_int16 = sub { return unpack( 'n!', substr( $_[0], $_[1], 2 ) ); }; + *unpack_int32 = sub { return unpack( 'N!', substr( $_[0], $_[1], 4 ) ); }; + + *unpack_int64 = $unpack_int64_slow || sub { return unpack( 'q>', substr( $_[0], $_[1], 8 ) ); }; + *unpack_uint64 = $unpack_uint64_slow || sub { return unpack( 'Q>', substr( $_[0], $_[1], 8 ) ); }; + } + + # fixin package symbols + no warnings 'once'; + sub pack :method; + sub unpack :method; + *Data::MessagePack::pack = \&pack; + *Data::MessagePack::unpack = \&unpack; + + @Data::MessagePack::Unpacker::ISA = qw(Data::MessagePack::PP::Unpacker); + + *true = \&Data::MessagePack::true; + *false = \&Data::MessagePack::false; +} + +sub _unexpected { + Carp::confess("Unexpected " . sprintf(shift, @_) . " found"); +} + +# +# PACK +# + +our $_max_depth; + +sub pack :method { + Carp::croak('Usage: Data::MessagePack->pack($dat [,$max_depth])') if @_ < 2; + $_max_depth = defined $_[2] ? $_[2] : 512; # init + return _pack( $_[1] ); +} + + +sub _pack { + my ( $value ) = @_; + + local $_max_depth = $_max_depth - 1; + + if ( $_max_depth < 0 ) { + Carp::croak("perl structure exceeds maximum nesting level (max_depth set too low?)"); + } + + return CORE::pack( 'C', 0xc0 ) if ( not defined $value ); + + if ( ref($value) eq 'ARRAY' ) { + my $num = @$value; + my $header = + $num < 16 ? CORE::pack( 'C', 0x90 + $num ) + : $num < 2 ** 16 - 1 ? CORE::pack( 'Cn', 0xdc, $num ) + : $num < 2 ** 32 - 1 ? CORE::pack( 'CN', 0xdd, $num ) + : _unexpected("number %d", $num) + ; + return join( '', $header, map { _pack( $_ ) } @$value ); + } + + elsif ( ref($value) eq 'HASH' ) { + my $num = keys %$value; + my $header = + $num < 16 ? CORE::pack( 'C', 0x80 + $num ) + : $num < 2 ** 16 - 1 ? CORE::pack( 'Cn', 0xde, $num ) + : $num < 2 ** 32 - 1 ? CORE::pack( 'CN', 0xdf, $num ) + : _unexpected("number %d", $num) + ; + return join( '', $header, map { _pack( $_ ) } %$value ); + } + + elsif ( ref( $value ) eq 'Data::MessagePack::Boolean' ) { + return CORE::pack( 'C', ${$value} ? 0xc3 : 0xc2 ); + } + + + my $b_obj = B::svref_2object( \$value ); + my $flags = $b_obj->FLAGS; + + if ( $flags & ( B::SVf_IOK | B::SVp_IOK ) ) { + + if ($value >= 0) { + return $value <= 127 ? CORE::pack 'C', $value + : $value < 2 ** 8 ? CORE::pack 'CC', 0xcc, $value + : $value < 2 ** 16 ? CORE::pack 'Cn', 0xcd, $value + : $value < 2 ** 32 ? CORE::pack 'CN', 0xce, $value + : pack_uint64( $value ); + } + else { + return -$value <= 32 ? CORE::pack 'C', ($value & 255) + : -$value <= 2 ** 7 ? CORE::pack 'Cc', 0xd0, $value + : -$value <= 2 ** 15 ? CORE::pack 'Cn', 0xd1, $value + : -$value <= 2 ** 31 ? CORE::pack 'CN', 0xd2, $value + : pack_int64( $value ); + } + + } + elsif ( $flags & B::SVf_POK ) { # raw / check needs before dboule + + if ( $Data::MessagePack::PreferInteger ) { + if ( $value =~ /^-?[0-9]+$/ ) { # ok? + my $value2 = 0 + $value; + if ( $value > 0xFFFFFFFF or $value < '-'.0x80000000 or # <- needless but for XS compat + 0 + $value != B::svref_2object( \$value2 )->int_value + ) { + local $Data::MessagePack::PreferInteger; # avoid for PV => NV + return _pack( "$value" ); + } + return _pack( $value + 0 ); + } + } + + utf8::encode( $value ) if utf8::is_utf8( $value ); + + my $num = length $value; + my $header = + $num < 32 ? CORE::pack( 'C', 0xa0 + $num ) + : $num < 2 ** 16 - 1 ? CORE::pack( 'Cn', 0xda, $num ) + : $num < 2 ** 32 - 1 ? CORE::pack( 'CN', 0xdb, $num ) + : _unexpected_number($num) + ; + + return $header . $value; + + } + elsif ( $flags & ( B::SVf_NOK | B::SVp_NOK ) ) { # double only + return pack_double( $value ); + } + else { + _unexpected("data type %s", $b_obj); + } + +} + +# +# UNPACK +# + +my $p; # position variables for speed. + +sub unpack :method { + $p = 0; # init + my $data = _unpack( $_[1] ); + if($p < length($_[1])) { + Carp::croak("Data::MessagePack->unpack: extra bytes"); + } + return $data; +} + + +sub _unpack { + my ( $value ) = @_; + my $byte = CORE::unpack( 'C', substr( $value, $p++, 1 ) ); # get header + + die "invalid data" unless defined $byte; + + if ( ( $byte >= 0x90 and $byte <= 0x9f ) or $byte == 0xdc or $byte == 0xdd ) { + my $num; + if ( $byte == 0xdc ) { # array 16 + $num = CORE::unpack 'n', substr( $value, $p, 2 ); + $p += 2; + } + elsif ( $byte == 0xdd ) { # array 32 + $num = CORE::unpack 'N', substr( $value, $p, 4 ); + $p += 4; + } + else { # fix array + $num = $byte & ~0x90; + } + my @array; + push @array, _unpack( $value ) while $num-- > 0; + return \@array; + } + + elsif ( ( $byte >= 0x80 and $byte <= 0x8f ) or $byte == 0xde or $byte == 0xdf ) { + my $num; + if ( $byte == 0xde ) { # map 16 + $num = CORE::unpack 'n', substr( $value, $p, 2 ); + $p += 2; + } + elsif ( $byte == 0xdf ) { # map 32 + $num = CORE::unpack 'N', substr( $value, $p, 4 ); + $p += 4; + } + else { # fix map + $num = $byte & ~0x80; + } + my %map; + for ( 0 .. $num - 1 ) { + no warnings; # for undef key case + my $key = _unpack( $value ); + my $val = _unpack( $value ); + $map{ $key } = $val; + } + return \%map; + } + + elsif ( $byte >= 0x00 and $byte <= 0x7f ) { # positive fixnum + return $byte; + } + elsif ( $byte == 0xcc ) { # uint8 + CORE::unpack( 'C', substr( $value, $p++, 1 ) ); + } + elsif ( $byte == 0xcd ) { # uint16 + $p += 2; + return unpack_uint16( $value, $p - 2 ); + } + elsif ( $byte == 0xce ) { # unit32 + $p += 4; + return unpack_uint32( $value, $p - 4 ); + } + elsif ( $byte == 0xcf ) { # unit64 + $p += 8; + return unpack_uint64( $value, $p - 8 ); + } + elsif ( $byte == 0xd3 ) { # int64 + $p += 8; + return unpack_int64( $value, $p - 8 ); + } + elsif ( $byte == 0xd2 ) { # int32 + $p += 4; + return unpack_int32( $value, $p - 4 ); + } + elsif ( $byte == 0xd1 ) { # int16 + $p += 2; + return unpack_int16( $value, $p - 2 ); + } + elsif ( $byte == 0xd0 ) { # int8 + return CORE::unpack 'c', substr( $value, $p++, 1 ); # c / C + } + elsif ( $byte >= 0xe0 and $byte <= 0xff ) { # negative fixnum + return $byte - 256; + } + + elsif ( ( $byte >= 0xa0 and $byte <= 0xbf ) or $byte == 0xda or $byte == 0xdb ) { # raw + my $num; + if ( $byte == 0xda ) { + $num = CORE::unpack 'n', substr( $value, $p, 2 ); + $p += 2 + $num; + } + elsif ( $byte == 0xdb ) { + $num = CORE::unpack 'N', substr( $value, $p, 4 ); + $p += 4 + $num; + } + else { # fix raw + $num = $byte & ~0xa0; + $p += $num; + } + return substr( $value, $p - $num, $num ); + } + + elsif ( $byte == 0xc0 ) { # nil + return undef; + } + elsif ( $byte == 0xc2 ) { # boolean + return false; + } + elsif ( $byte == 0xc3 ) { # boolean + return true; + } + + elsif ( $byte == 0xcb ) { # double + $p += 8; + return unpack_double( $value, $p - 8 ); + } + + elsif ( $byte == 0xca ) { # float + $p += 4; + return unpack_float( $value, $p - 4 ); + } + + else { + _unexpected("byte 0x%02x", $byte); + } + +} + + +# +# Data::MessagePack::Unpacker +# + +package + Data::MessagePack::PP::Unpacker; + +sub new { + bless { pos => 0 }, shift; +} + + +sub execute_limit { + execute( @_ ); +} + +sub execute { + my ( $self, $data, $offset, $limit ) = @_; + $offset ||= 0; + my $value = substr( $data, $offset, $limit ? $limit : length $data ); + my $len = length $value; + + $self->{data} .= $value; + local $self->{stack} = []; + + $p = 0; + + while ( length($self->{data}) > $p ) { + _count( $self, $self->{data} ) or last; + + while ( @{ $self->{stack} } > 0 && --$self->{stack}->[-1] == 0) { + pop @{ $self->{stack} }; + } + + if (@{$self->{stack}} == 0) { + $self->{is_finished}++; + last; + } + } + $self->{pos} = $p; + + return $p + $offset; +} + + +sub _count { + my ( $self, $value ) = @_; + my $byte = unpack( 'C', substr( $value, $p++, 1 ) ); # get header + + if ( ( $byte >= 0x90 and $byte <= 0x9f ) or $byte == 0xdc or $byte == 0xdd ) { + my $num; + if ( $byte == 0xdc ) { # array 16 + $num = unpack 'n', substr( $value, $p, 2 ); + $p += 2; + } + elsif ( $byte == 0xdd ) { # array 32 + $num = unpack 'N', substr( $value, $p, 4 ); + $p += 4; + } + else { # fix array + $num = $byte & ~0x90; + } + + if ( $num ) { + push @{ $self->{stack} }, $num + 1; + } + + return 1; + } + + elsif ( ( $byte >= 0x80 and $byte <= 0x8f ) or $byte == 0xde or $byte == 0xdf ) { + my $num; + if ( $byte == 0xde ) { # map 16 + $num = unpack 'n', substr( $value, $p, 2 ); + $p += 2; + } + elsif ( $byte == 0xdf ) { # map 32 + $num = unpack 'N', substr( $value, $p, 4 ); + $p += 4; + } + else { # fix map + $num = $byte & ~0x80; + } + + if ( $num ) { + push @{ $self->{stack} }, $num * 2 + 1; # a pair + } + + return 1; + } + + elsif ( $byte == 0xc0 or $byte == 0xc2 or $byte == 0xc3 ) { # nil, false, true + return 1; + } + + elsif ( $byte >= 0x00 and $byte <= 0x7f ) { # positive fixnum + return 1; + } + + elsif ( $byte >= 0xcc and $byte <= 0xcf ) { # uint + $p += $byte == 0xcc ? 1 + : $byte == 0xcd ? 2 + : $byte == 0xce ? 4 + : $byte == 0xcf ? 8 + : _unexpected("byte 0x%02x", $byte); + return 1; + } + + elsif ( $byte >= 0xd0 and $byte <= 0xd3 ) { # int + $p += $byte == 0xd0 ? 1 + : $byte == 0xd1 ? 2 + : $byte == 0xd2 ? 4 + : $byte == 0xd3 ? 8 + : _unexpected("byte 0x%02x", $byte); + return 1; + } + + elsif ( $byte >= 0xe0 and $byte <= 0xff ) { # negative fixnum + return 1; + } + + elsif ( $byte >= 0xca and $byte <= 0xcb ) { # float, double + $p += $byte == 0xca ? 4 : 8; + return 1; + } + + elsif ( ( $byte >= 0xa0 and $byte <= 0xbf ) or $byte == 0xda or $byte == 0xdb ) { + my $num; + if ( $byte == 0xda ) { + $num = unpack 'n', substr( $value, $p, 2 ); + $p += 2; + } + elsif ( $byte == 0xdb ) { + $num = unpack 'N', substr( $value, $p, 4 ); + $p += 4; + } + else { # fix raw + $num = $byte & ~0xa0; + } + $p += $num; + return 1; + } + + else { + _unexpected("byte 0x%02x", $byte); + } + + return 0; +} + + +sub data { + return Data::MessagePack->unpack( substr($_[0]->{ data }, 0, $_[0]->{pos}) ); +} + + +sub is_finished { + my ( $self ) = @_; + return $self->{is_finished}; +} + +sub reset :method { + $_[0]->{ data } = undef; + $_[0]->{ pos } = 0; + $_[0]->{ is_finished } = 0; +} + +1; +__END__ + +=pod + +=head1 NAME + +Data::MessagePack::PP - Pure Perl implementation of Data::MessagePack + +=head1 DESCRIPTION + +This module is used by L internally. + +=head1 SEE ALSO + +L, +L, +L, + +=head1 AUTHOR + +makamaka + +=head1 COPYRIGHT AND LICENSE + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut diff --git a/perl/lib/Data/MessagePack/Unpacker.pod b/perl/lib/Data/MessagePack/Unpacker.pod index c24eaf1d..2bc4549c 100644 --- a/perl/lib/Data/MessagePack/Unpacker.pod +++ b/perl/lib/Data/MessagePack/Unpacker.pod @@ -14,7 +14,7 @@ Data::MessagePack::Unpacker - messagepack streaming deserializer =head1 DESCRIPTION -This is an streaming deserializer for messagepack. +This is a streaming deserializer for messagepack. =head1 METHODS @@ -22,7 +22,7 @@ This is an streaming deserializer for messagepack. =item my $up = Data::MessagePack::Unpacker->new() -create new instance of stream deserializer. +creates a new instance of stream deserializer. =item my $ret = $up->execute($data, $offset); @@ -39,11 +39,11 @@ is this deserializer finished? =item my $data = $up->data(); -returns deserialized object. +returns the deserialized object. =item $up->reset(); -reset the stream deserializer, without memory zone. +resets the stream deserializer, without memory zone. =back diff --git a/perl/t/00_compile.t b/perl/t/00_compile.t index 66fe8f0e..d465f8dc 100644 --- a/perl/t/00_compile.t +++ b/perl/t/00_compile.t @@ -3,4 +3,5 @@ use warnings; use Test::More tests => 1; use_ok 'Data::MessagePack'; - +diag ( "Testing Data::MessagePack/$Data::MessagePack::VERSION (", + $INC{'Data/MessagePack/PP.pm'} ? 'PP' : 'XS', ")" ); diff --git a/perl/t/02_unpack.t b/perl/t/02_unpack.t index 4da69e91..657387a9 100644 --- a/perl/t/02_unpack.t +++ b/perl/t/02_unpack.t @@ -5,13 +5,14 @@ no warnings 'uninitialized'; # i need this. i need this. sub unpackit { my $v = $_[0]; - $v =~ s/ //g; + $v =~ s/ +//g; $v = pack 'H*', $v; return Data::MessagePack->unpack($v); } sub pis ($$) { - is_deeply unpackit($_[0]), $_[1], 'dump ' . $_[0]; + is_deeply unpackit($_[0]), $_[1], 'dump ' . $_[0] + or diag( explain(unpackit($_[0])) ); } my @dat = do 't/data.pl'; diff --git a/perl/t/03_stream_unpack.t b/perl/t/03_stream_unpack.t index a4ab4eba..646fc249 100644 --- a/perl/t/03_stream_unpack.t +++ b/perl/t/03_stream_unpack.t @@ -37,7 +37,7 @@ for (my $i=0; $iexecute("\xc0", 0); # nil } - ok $up->is_finished; - is_deeply $up->data, [undef, undef, undef, undef, undef]; + ok $up->is_finished, 'finished'; + is_deeply $up->data, [undef, undef, undef, undef, undef], 'array, is_deeply'; } diff --git a/perl/t/05_preferred_int.t b/perl/t/05_preferred_int.t index 9860711b..084df31c 100644 --- a/perl/t/05_preferred_int.t +++ b/perl/t/05_preferred_int.t @@ -12,10 +12,15 @@ sub packit { } sub pis ($$) { - is packit($_[0]), $_[1], 'dump ' . $_[1]; + if (ref $_[1]) { + like packit($_[0]), $_[1], 'dump ' . $_[1]; + } else { + is packit($_[0]), $_[1], 'dump ' . $_[1]; + } # is(Dumper(Data::MessagePack->unpack(Data::MessagePack->pack($_[0]))), Dumper($_[0])); } +my $is_win = $^O eq 'MSWin32'; my @dat = ( '', 'a0', '0', '00', @@ -29,12 +34,16 @@ my @dat = ( ''.0xFFFFFF => 'ce 00 ff ff ff', ''.0xFFFFFFFF => 'ce ff ff ff ff', ''.0xFFFFFFFFF => 'ab 36 38 37 31 39 34 37 36 37 33 35', - ''.0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFF => 'b4 38 2e 33 30 37 36 37 34 39 37 33 36 35 35 37 32 65 2b 33 34', + ''.0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFF => $is_win ? + qr{^(b5 38 2e 33 30 37 36 37 34 39 37 33 36 35 35 37 32 65 2b 30 33 34|b8 38 2e 33 30 37 36 37 34 39 37 33 36 35 35 37 32 34 32 31 65 2b 30 33 34)$} + : qr{^(b4 38 2e 33 30 37 36 37 34 39 37 33 36 35 35 37 32 65 2b 33 34|b7 38 2e 33 30 37 36 37 34 39 37 33 36 35 35 37 32 34 32 31 65 2b 33 34)$}, '-'.0x8000000 => 'd2 f8 00 00 00', '-'.0x80000000 => 'd2 80 00 00 00', '-'.0x800000000 => 'ac 2d 33 34 33 35 39 37 33 38 33 36 38', '-'.0x8000000000 => 'ad 2d 35 34 39 37 35 35 38 31 33 38 38 38', - '-'.0x800000000000000000000000000000 => 'b5 2d 36 2e 36 34 36 31 33 39 39 37 38 39 32 34 35 38 65 2b 33 35', + '-'.0x800000000000000000000000000000 => $is_win ? + qr{^(b6 2d 36 2e 36 34 36 31 33 39 39 37 38 39 32 34 35 38 65 2b 30 33 35|b9 2d 36 2e 36 34 36 31 33 39 39 37 38 39 32 34 35 37 39 33 36 65 2b 30 33 35)} + : qr{^(b5 2d 36 2e 36 34 36 31 33 39 39 37 38 39 32 34 35 38 65 2b 33 35|b8 2d 36 2e 36 34 36 31 33 39 39 37 38 39 32 34 35 37 39 33 36 65 2b 33 35)}, {'0' => '1'}, '81 00 01', {'abc' => '1'}, '81 a3 61 62 63 01', ); diff --git a/perl/t/06_stream_unpack2.t b/perl/t/06_stream_unpack2.t index eaf2cb4b..bb6fe93d 100644 --- a/perl/t/06_stream_unpack2.t +++ b/perl/t/06_stream_unpack2.t @@ -1,9 +1,17 @@ use strict; use warnings; use Data::MessagePack; -use Test::More tests => 6; +use Test::More tests => 9; +use t::Util; + +my $input = [ + false,true,null,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1, + 127,127,255,65535,4294967295,-32,-32,-128,-32768, + -2147483648,0.0,-0.0,1.0,-1.0,"a","a","a","","","", + [0],[0],[0],[],[],[],{},{},{}, + {"a" => 97},{"a" => 97},{"a" => 97},[[]],[["a"]] +]; -my $input = [(undef)x16]; my $packed = Data::MessagePack->pack($input); is_deeply(Data::MessagePack->unpack($packed), $input); @@ -16,10 +24,20 @@ is_deeply(Data::MessagePack->unpack($packed), $input); { my $up = Data::MessagePack::Unpacker->new(); - is $up->execute(substr($packed, 0, 3), 0), 3; - $up->execute($packed, 3); - ok $up->is_finished; - is_deeply $up->data, $input; + $packed x= 3; + + my $offset = 0; + for my $i(1 .. 3) { + note "block $i (offset: $offset/".length($packed).")"; + note "starting 3 bytes: ", join " ", map { unpack 'H2', $_ } + split //, substr($packed, $offset, 3); + + $offset = $up->execute($packed, $offset); + ok $up->is_finished, 'finished'; + my $data = $up->data; + is_deeply $data, $input; + $up->reset(); + } } diff --git a/perl/t/07_break.t b/perl/t/07_break.t index dd27ad25..cf3f1079 100644 --- a/perl/t/07_break.t +++ b/perl/t/07_break.t @@ -1,12 +1,23 @@ use Test::More; use Data::MessagePack; use t::Util; -no warnings 'uninitialized'; # i need this. i need this. -plan tests => 1; +plan tests => 4; -my $d = Data::MessagePack->unpack(Data::MessagePack->pack([{x => undef}])); -$d->[0]->{x} = 1; -ok delete $d->[0]->{x}; -$d->[0] = 4; +my $d = Data::MessagePack->unpack(Data::MessagePack->pack({ + nil => undef, + true => true, + false => false, + foo => [undef, true, false], +})); +$d->{nil} = 42; +is $d->{nil}, 42; + +$d->{true} = 43; +is $d->{true}, 43; + +$d->{false} = 44; +is $d->{false}, 44; + +is_deeply $d->{foo}, [undef, true, false]; diff --git a/perl/t/08_cycle.t b/perl/t/08_cycle.t new file mode 100644 index 00000000..2bd66c10 --- /dev/null +++ b/perl/t/08_cycle.t @@ -0,0 +1,28 @@ +use t::Util; +use Test::More; +use Data::MessagePack; + +plan tests => 6; + +my $aref = [0]; +$aref->[1] = $aref; +eval { Data::MessagePack->pack($aref) }; +ok $@, $@; + +my $href = {}; +$href->{cycle} = $href; +eval { Data::MessagePack->pack($aref) }; +ok $@, $@; + +$aref = [0,[1,2]]; +eval { Data::MessagePack->pack($aref) }; +ok !$@; + +eval { Data::MessagePack->pack($aref, 3) }; +ok !$@; + +eval { Data::MessagePack->pack($aref, 2) }; +ok $@, $@; + +eval { Data::MessagePack->pack($aref, -1) }; +ok $@, $@; diff --git a/perl/t/09_stddata.t b/perl/t/09_stddata.t new file mode 100644 index 00000000..f98d696b --- /dev/null +++ b/perl/t/09_stddata.t @@ -0,0 +1,42 @@ +#!perl -w +# Testing standard dataset in msgpack/test/*.{json,mpac}. +# Don't edit msgpack/perl/t/std/*, which are just copies. +use strict; +use Test::More; +use t::Util; + +use Data::MessagePack; + +sub slurp { + open my $fh, '<:raw', $_[0] or die "failed to open '$_[0]': $!"; + local $/; + return scalar <$fh>; +} + +my @data = do { + my $json = slurp("t/std/cases.json"); + $json =~ s/:/=>/g; + @{ eval $json }; +}; + +my $mpac1 = slurp("t/std/cases.mpac"); +my $mpac2 = slurp("t/std/cases_compact.mpac"); + +my $mps = Data::MessagePack::Unpacker->new(); + +my $t = 1; +for my $mpac($mpac1, $mpac2) { + note "mpac", $t++; + + my $offset = 0; + my $i = 0; + while($offset < length($mpac)) { + $offset = $mps->execute($mpac, $offset); + ok $mps->is_finished, "data[$i] : is_finished"; + is_deeply $mps->data, $data[$i], "data[$i]"; + $mps->reset; + $i++; + } +} + +done_testing; diff --git a/perl/t/10_splitted_bytes.t b/perl/t/10_splitted_bytes.t new file mode 100644 index 00000000..15598f4e --- /dev/null +++ b/perl/t/10_splitted_bytes.t @@ -0,0 +1,42 @@ +#!perl + +# This feature is not yet supported, but 0.23 (or former) caused SEGV in this code, +# so we put it here. + +use strict; +use warnings; +use Data::MessagePack; +use Test::More; +use t::Util; + +my $input = [ + false,true,null,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1, + 127,127,255,65535,4294967295,-32,-32,-128,-32768, + -2147483648,0.0,-0.0,1.0,-1.0,"a","a","a","","","", + [0],[0],[0],[],[],[],{},{},{}, + {"a" => 97},{"a" => 97},{"a" => 97},[[]],[["a"]] +]; + +my $packed = Data::MessagePack->pack($input); + +foreach my $size(1 .. 16) { + local $TODO = "Splitted byte streaming is not yet supported (bufer size: $size)"; + + my $up = Data::MessagePack::Unpacker->new(); + + open my $stream, '<:bytes :scalar', \$packed; + binmode $stream; + my $buff; + my $done = 0; + while( read($stream, $buff, $size) ) { + #note "buff: ", join " ", map { unpack 'H2', $_ } split //, $buff; + + $done = $up->execute($buff); + } + is $done, length($packed); + ok $up->is_finished, "is_finished: $size"; + my $data = $up->data; + is_deeply $data, $input; +} + +done_testing; diff --git a/perl/t/11_stream_unpack3.t b/perl/t/11_stream_unpack3.t new file mode 100644 index 00000000..0eb8bff7 --- /dev/null +++ b/perl/t/11_stream_unpack3.t @@ -0,0 +1,39 @@ +use strict; +use warnings; +use Test::More; +use Data::MessagePack; + +my @data = ( [ 1, 2, 3 ], [ 4, 5, 6 ] ); + +# serialize +my $buffer = ''; +for my $d (@data) { + $buffer .= Data::MessagePack->pack($d); +} + +# deserialize +my $cb = sub { + my ($data) = @_; + + my $d = shift @data; + is_deeply $data, $d; +}; +my $unpacker = Data::MessagePack::Unpacker->new(); +my $nread = 0; +while (1) { + $nread = $unpacker->execute( $buffer, $nread ); + if ( $unpacker->is_finished ) { + my $ret = $unpacker->data; + $cb->( $ret ); + $unpacker->reset; + + $buffer = substr( $buffer, $nread ); + $nread = 0; + next if length($buffer) != 0; + } + last; +} +is scalar(@data), 0; + +done_testing; + diff --git a/perl/t/12_stream_unpack4.t b/perl/t/12_stream_unpack4.t new file mode 100644 index 00000000..de76e81d --- /dev/null +++ b/perl/t/12_stream_unpack4.t @@ -0,0 +1,24 @@ +use strict; +use warnings; +use Data::MessagePack; +use Test::More; +use t::Util; + +my @input = ( + +[[]], + [[],[]], + [{"a" => 97},{"a" => 97}], + [{"a" => 97},{"a" => 97},{"a" => 97}], + [ map { +{ "foo $_" => "bar $_" } } 'aa' .. 'zz' ], +); + +plan tests => @input * 2; + +for my $input (@input) { + my $packed = Data::MessagePack->pack($input); + my $up = Data::MessagePack::Unpacker->new(); + $up->execute($packed, 0); + ok $up->is_finished, 'finished'; + is_deeply($up->data, $input); +} + diff --git a/perl/t/13_booleans.t b/perl/t/13_booleans.t new file mode 100755 index 00000000..1ecbe646 --- /dev/null +++ b/perl/t/13_booleans.t @@ -0,0 +1,12 @@ +#!perl -w +use strict; +use Test::More tests => 6; +use Data::MessagePack; + +ok defined(Data::MessagePack::true()), 'true (1)'; +ok defined(Data::MessagePack::true()), 'true (2)'; +ok Data::MessagePack::true(), 'true is true'; + +ok defined(Data::MessagePack::false()), 'false (1)'; +ok defined(Data::MessagePack::false()), 'false (2)'; +ok !Data::MessagePack::false(), 'false is false'; diff --git a/perl/t/14_invalid_data.t b/perl/t/14_invalid_data.t new file mode 100755 index 00000000..f5344857 --- /dev/null +++ b/perl/t/14_invalid_data.t @@ -0,0 +1,18 @@ +use strict; +use warnings; +use Data::MessagePack; +use Test::More; +use t::Util; + +my $nil = Data::MessagePack->pack(undef); + +my @data = do 't/data.pl'; +while(my($dump, $data) = splice @data, 0, 2) { + my $s = Data::MessagePack->pack($data); + eval { + Data::MessagePack->unpack($s . $nil); + }; + like $@, qr/extra bytes/, "dump $dump"; +} + +done_testing; diff --git a/perl/t/50_leaktrace.t b/perl/t/50_leaktrace.t new file mode 100644 index 00000000..440ac901 --- /dev/null +++ b/perl/t/50_leaktrace.t @@ -0,0 +1,62 @@ +#!perl -w +use strict; +use Test::Requires { 'Test::LeakTrace' => 0.13 }; +use Test::More; +use Data::MessagePack; +BEGIN { + if($INC{'Data/MessagePack/PP.pm'}) { + plan skip_all => 'disabled in PP'; + } +} + +my $simple_data = "xyz"; +my $complex_data = { + a => 'foo', + b => 42, + c => undef, + d => [qw(bar baz)], + e => 3.14, +}; + +note 'pack'; + +no_leaks_ok { + my $s = Data::MessagePack->pack($complex_data); +}; + +no_leaks_ok { + eval { Data::MessagePack->pack([\*STDIN]) }; + note $@; + $@ or warn "# it must die"; +}; + +note 'unpack'; + +my $s = Data::MessagePack->pack($simple_data); +my $c = Data::MessagePack->pack($complex_data); + +no_leaks_ok { + my $data = Data::MessagePack->unpack($s); +}; + +no_leaks_ok { + my $data = Data::MessagePack->unpack($c); +}; + +no_leaks_ok { + my $broken = $s; + chop $broken; + eval { Data::MessagePack->unpack($broken) }; + note $@; + $@ or warn "# it must die"; +}; + +note 'stream'; + +no_leaks_ok { + my $up = Data::MessagePack::Unpacker->new(); + $up->execute($c); + my $data = $up->data(); +}; + +done_testing; diff --git a/perl/t/Util.pm b/perl/t/Util.pm index c8debefb..ad69c4d5 100644 --- a/perl/t/Util.pm +++ b/perl/t/Util.pm @@ -1,6 +1,7 @@ package t::Util; use strict; use warnings; +use Data::MessagePack; sub import { my $pkg = caller(0); @@ -15,6 +16,7 @@ sub import { *{"$pkg\::false"} = sub () { Data::MessagePack::false() }; + *{"$pkg\::null"} = sub() { undef }; } 1; diff --git a/perl/t/data.pl b/perl/t/data.pl index 2f58d38c..9bf07b7d 100644 --- a/perl/t/data.pl +++ b/perl/t/data.pl @@ -4,15 +4,48 @@ no warnings; # i need this, i need this. '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], + + '82 d0 2a c2 d0 2b c3', { 42 => false, 43 => true }, # fix map + 'de 00 02 d0 2a c2 d0 2b c3', { 42 => false, 43 => true }, # map 16 + 'df 00 00 00 02 d0 2a c2 d0 2b c3', { 42 => false, 43 => true }, # map 32 + '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], + '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], + '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}], + '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}], 'ce 00 ff ff ff' => ''.0xFFFFFF, 'aa 34 32 39 34 39 36 37 32 39 35' => ''.0xFFFFFFFF, 'ab 36 38 37 31 39 34 37 36 37 33 35' => ''.0xFFFFFFFFF, + + 'd2 80 00 00 01' => '-2147483647', # int32_t + 'ce 80 00 00 01' => '2147483649', # uint32_t + + 'd2 ff ff ff ff' => '-1', # int32_t + 'ce ff ff ff ff' => '4294967295', # uint32_t + + 'd3 00 00 00 00 80 00 00 01' => '2147483649', # int64_t + 'cf 00 00 00 00 80 00 00 01' => '2147483649', # uint64_t + + 'd3 ff 00 ff ff ff ff ff ff' => '-71776119061217281', # int64_t + 'cf ff 00 ff ff ff ff ff ff' => '18374967954648334335', # uint64_t + + 'd3 ff ff ff ff ff ff ff ff' => '-1', # int64_t + 'cf ff ff ff ff ff ff ff ff' => '18446744073709551615', # uint64_t + + # int64_t + 'd3 00 00 00 10 00 00 00 00' => '68719476736', + 'd3 00 00 00 10 00 00 00 01' => '68719476737', + 'd3 00 00 10 00 00 00 00 00' => '17592186044416', + 'd3 00 10 00 00 00 00 00 00' => '4503599627370496', + 'd3 10 00 00 00 00 00 00 00' => '1152921504606846976', + 'd3 11 00 00 00 00 00 00 00' => '1224979098644774912', ) diff --git a/perl/unpack.c b/perl/unpack.c deleted file mode 100644 index eb6e0ddb..00000000 --- a/perl/unpack.c +++ /dev/null @@ -1,352 +0,0 @@ -#ifdef __cplusplus -extern "C" { -#endif - -#include "EXTERN.h" -#include "perl.h" -#include "XSUB.h" -#include "util.h" -#define NEED_newRV_noinc -#define NEED_sv_2pv_flags -#include "ppport.h" - -#ifdef __cplusplus -}; -#endif - -typedef struct { - int finished; - SV* source; - int incremented; -} 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 - -/* ---------------------------------------------------------------------- */ -/* utility functions */ - -static INLINE SV * -get_bool (const char *name) { - SV * sv = sv_mortalcopy(get_sv( name, 1 )); - - SvREADONLY_on(sv); - SvREADONLY_on( SvRV(sv) ); - - return sv; -} - -/* ---------------------------------------------------------------------- */ - -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 = sv_2mortal(newSVuv(d)); return 0; } - -static INLINE int template_callback_uint16(unpack_user* u, uint16_t d, SV** o) -{ *o = sv_2mortal(newSVuv(d)); return 0; } - -static INLINE int template_callback_uint32(unpack_user* u, uint32_t d, SV** o) -{ *o = sv_2mortal(newSVuv(d)); return 0; } - -static INLINE int template_callback_uint64(unpack_user* u, uint64_t d, SV** o) -{ -#if IVSIZE==4 - *o = sv_2mortal(newSVnv(d)); -#else - *o = sv_2mortal(newSVuv(d)); -#endif - return 0; -} - -static INLINE int template_callback_int8(unpack_user* u, int8_t d, SV** o) -{ *o = sv_2mortal(newSViv((long)d)); return 0; } - -static INLINE int template_callback_int16(unpack_user* u, int16_t d, SV** o) -{ *o = sv_2mortal(newSViv((long)d)); return 0; } - -static INLINE int template_callback_int32(unpack_user* u, int32_t d, SV** o) -{ *o = sv_2mortal(newSViv((long)d)); return 0; } - -static INLINE int template_callback_int64(unpack_user* u, int64_t d, SV** o) -{ *o = sv_2mortal(newSViv(d)); return 0; } - -static INLINE int template_callback_float(unpack_user* u, float d, SV** o) -{ *o = sv_2mortal(newSVnv(d)); return 0; } - -static INLINE int template_callback_double(unpack_user* u, double d, SV** o) -{ *o = sv_2mortal(newSVnv(d)); return 0; } - -/* &PL_sv_undef is not so good. see http://gist.github.com/387743 */ -static INLINE int template_callback_nil(unpack_user* u, SV** o) -{ *o = sv_newmortal(); return 0; } - -static INLINE int template_callback_true(unpack_user* u, SV** o) -{ *o = get_bool("Data::MessagePack::true") ; return 0; } - -static INLINE int template_callback_false(unpack_user* u, SV** o) -{ *o = get_bool("Data::MessagePack::false") ; return 0; } - -static INLINE int template_callback_array(unpack_user* u, unsigned int n, SV** o) -{ AV* a = (AV*)sv_2mortal((SV*)newAV()); *o = sv_2mortal((SV*)newRV_inc((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 = (HV*)sv_2mortal((SV*)newHV()); *o = sv_2mortal(newRV_inc((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 = sv_2mortal((l==0) ? newSVpv("", 0) : newSVpv(p, l)); return 0; } -/* { *o = newSVpvn_flags(p, l, SVs_TEMP); return 0; } <= this does not works. */ - -#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); - SV* obj; - - 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; - - obj = template_data(&mp); - - 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 obj; - } -} - -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 -- */ -/* http://twitter.com/frsyuki/status/13249304748 */ - -static void _reset(SV* self) { - unpack_user u = {0, &PL_sv_undef, 0}; - - UNPACKER(self, mp); - template_init(mp); - mp->user = u; -} - -XS(xs_unpacker_new) { - dXSARGS; - if (items != 1) { - Perl_croak(aTHX_ "Usage: Data::MessagePack::Unpacker->new()"); - } - - 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 sv_2mortal(newSVuv(from)); - } else { - mp->user.finished = 0; - return sv_2mortal(newSVuv(from)); - } -} - -XS(xs_unpacker_execute) { - dXSARGS; - if (items != 3) { - Perl_croak(aTHX_ "Usage: $unpacker->execute_limit(data, off)"); - } - - UNPACKER(ST(0), mp); - { - SV* self = ST(0); - SV* data = ST(1); - IV off = SvIV(ST(2)); /* offset of $data. normaly, 0. */ - - ST(0) = _execute_impl(self, data, off, sv_len(data)); - - { - SV * d2 = template_data(mp); - if (!mp->user.incremented && d2) { - SvREFCNT_inc(d2); - mp->user.incremented = 1; - } - } - } - - XSRETURN(1); -} - -XS(xs_unpacker_execute_limit) { - dXSARGS; - if (items != 4) { - Perl_croak(aTHX_ "Usage: $unpacker->execute_limit(data, off, limit)"); - } - - 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; - if (items != 1) { - Perl_croak(aTHX_ "Usage: $unpacker->is_finished()"); - } - - UNPACKER(ST(0), mp); - ST(0) = (mp->user.finished) ? &PL_sv_yes : &PL_sv_no; - - XSRETURN(1); -} - -XS(xs_unpacker_data) { - dXSARGS; - if (items != 1) { - Perl_croak(aTHX_ "Usage: $unpacker->data()"); - } - - UNPACKER(ST(0), mp); - ST(0) = sv_2mortal(newSVsv(template_data(mp))); - - XSRETURN(1); -} - -XS(xs_unpacker_reset) { - dXSARGS; - if (items != 1) { - Perl_croak(aTHX_ "Usage: $unpacker->reset()"); - } - - UNPACKER(ST(0), mp); - { - SV * data = template_data(mp); - if (data) { - SvREFCNT_dec(data); - } - } - _reset(ST(0)); - - XSRETURN(0); -} - -XS(xs_unpacker_destroy) { - dXSARGS; - if (items != 1) { - Perl_croak(aTHX_ "Usage: $unpacker->DESTROY()"); - } - - UNPACKER(ST(0), mp); - SV * data = template_data(mp); - if (SvOK(data)) { - SvREFCNT_dec(data); - } - Safefree(mp); - - XSRETURN(0); -} diff --git a/perl/util.h b/perl/util.h deleted file mode 100644 index 2b4ed072..00000000 --- a/perl/util.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __PERL_MSGPACK_UTIL_H__ -#define __PERL_MSGPACK_UTIL_H__ - -#if __GNUC__ >= 3 -# define INLINE inline -#else -# define INLINE -#endif - -#endif // __PERL_MSGPACK_UTIL_H__ - diff --git a/perl/MessagePack.c b/perl/xs-src/MessagePack.c similarity index 74% rename from perl/MessagePack.c rename to perl/xs-src/MessagePack.c index fd1b344d..69337f41 100644 --- a/perl/MessagePack.c +++ b/perl/xs-src/MessagePack.c @@ -1,13 +1,7 @@ -#ifdef __cplusplus -extern "C" { -#endif -#include "EXTERN.h" -#include "perl.h" -#include "XSUB.h" -#define NEED_newCONSTSUB -#include "ppport.h" -#ifdef __cplusplus -}; +#include "xshelper.h" + +#ifndef __cplusplus +#include #endif XS(xs_pack); @@ -20,17 +14,18 @@ XS(xs_unpacker_data); XS(xs_unpacker_reset); XS(xs_unpacker_destroy); -void boot_Data__MessagePack_pack(void); +void init_Data__MessagePack_pack(pTHX_ bool const cloning); +void init_Data__MessagePack_unpack(pTHX_ bool const cloning); XS(boot_Data__MessagePack) { dXSARGS; - HV * stash; + PERL_UNUSED_VAR(items); - boot_Data__MessagePack_pack(); + init_Data__MessagePack_pack(aTHX_ false); + init_Data__MessagePack_unpack(aTHX_ false); newXS("Data::MessagePack::pack", xs_pack, __FILE__); newXS("Data::MessagePack::unpack", xs_unpack, __FILE__); - stash = gv_stashpvn("Data::MessagePack", strlen("Data::MessagePack"), TRUE); newXS("Data::MessagePack::Unpacker::new", xs_unpacker_new, __FILE__); newXS("Data::MessagePack::Unpacker::execute", xs_unpacker_execute, __FILE__); diff --git a/perl/pack.c b/perl/xs-src/pack.c similarity index 67% rename from perl/pack.c rename to perl/xs-src/pack.c index 589cee8b..862808eb 100644 --- a/perl/pack.c +++ b/perl/xs-src/pack.c @@ -2,16 +2,7 @@ * code is written by tokuhirom. * buffer alocation technique is taken from JSON::XS. thanks to mlehmann. */ -#ifdef __cplusplus -extern "C" { -#endif -#include "EXTERN.h" -#include "perl.h" -#include "XSUB.h" -#include "ppport.h" -#ifdef __cplusplus -}; -#endif +#include "xshelper.h" #include "msgpack/pack_define.h" @@ -22,17 +13,18 @@ extern "C" { static inline void msgpack_pack ## name typedef struct { - char *cur; /* SvPVX (sv) + current output position */ - char *end; /* SvEND (sv) */ - SV *sv; /* result scalar */ + char *cur; /* SvPVX (sv) + current output position */ + const char *end; /* SvEND (sv) */ + SV *sv; /* result scalar */ } enc_t; -static void need(enc_t *enc, STRLEN len); + +STATIC_INLINE void need(enc_t* const enc, STRLEN const len); #define msgpack_pack_user enc_t* #define msgpack_pack_append_buffer(enc, buf, len) \ - need(enc, len); \ - memcpy(enc->cur, buf, len); \ + need(enc, len); \ + memcpy(enc->cur, buf, len); \ enc->cur += len; #include "msgpack/pack_template.h" @@ -41,29 +33,35 @@ static void need(enc_t *enc, STRLEN len); #if IVSIZE == 8 # define PACK_IV msgpack_pack_int64 +# define PACK_UV msgpack_pack_uint64 #elif IVSIZE == 4 # define PACK_IV msgpack_pack_int32 +# define PACK_UV msgpack_pack_uint32 #elif IVSIZE == 2 # define PACK_IV msgpack_pack_int16 +# define PACK_UV msgpack_pack_uint16 #else # error "msgpack only supports IVSIZE = 8,4,2 environment." #endif +#define ERR_NESTING_EXCEEDED "perl structure exceeds maximum nesting level (max_depth set too low?)" -static void need(enc_t *enc, STRLEN len) + +STATIC_INLINE void need(enc_t* const enc, STRLEN const len) { if (enc->cur + len >= enc->end) { - STRLEN cur = enc->cur - (char *)SvPVX (enc->sv); - SvGROW (enc->sv, cur + (len < (cur >> 2) ? cur >> 2 : len) + 1); - enc->cur = SvPVX (enc->sv) + cur; - enc->end = SvPVX (enc->sv) + SvLEN (enc->sv) - 1; + dTHX; + STRLEN const cur = enc->cur - SvPVX_const(enc->sv); + sv_grow (enc->sv, cur + (len < (cur >> 2) ? cur >> 2 : len) + 1); + enc->cur = SvPVX_mutable(enc->sv) + cur; + enc->end = SvPVX_const(enc->sv) + SvLEN (enc->sv) - 1; } } static int s_pref_int = 0; -static int pref_int_set(pTHX_ SV* sv, MAGIC* mg) { +STATIC_INLINE int pref_int_set(pTHX_ SV* sv, MAGIC* mg PERL_UNUSED_DECL) { if (SvTRUE(sv)) { s_pref_int = 1; } else { @@ -85,14 +83,14 @@ MGVTBL pref_int_vtbl = { #endif }; -void boot_Data__MessagePack_pack(void) { +void init_Data__MessagePack_pack(pTHX_ bool const cloning) { SV* var = get_sv("Data::MessagePack::PreferInteger", 0); sv_magicext(var, NULL, PERL_MAGIC_ext, &pref_int_vtbl, NULL, 0); SvSETMAGIC(var); } -static int try_int(enc_t* enc, const char *p, size_t len) { +STATIC_INLINE int try_int(enc_t* enc, const char *p, size_t len) { int negative = 0; const char* pe = p + len; uint64_t num = 0; @@ -146,32 +144,37 @@ static int try_int(enc_t* enc, const char *p, size_t len) { } -static void _msgpack_pack_rv(enc_t *enc, SV* sv); +STATIC_INLINE void _msgpack_pack_rv(enc_t *enc, SV* sv, int depth); -static void _msgpack_pack_sv(enc_t *enc, SV* sv) { +STATIC_INLINE void _msgpack_pack_sv(enc_t* const enc, SV* const sv, int const depth) { + dTHX; + assert(sv); + if (UNLIKELY(depth <= 0)) Perl_croak(aTHX_ ERR_NESTING_EXCEEDED); SvGETMAGIC(sv); - if (sv==NULL) { - msgpack_pack_nil(enc); - } else if (SvPOKp(sv)) { - STRLEN len; - char * csv = SvPV(sv, len); + if (SvPOKp(sv)) { + STRLEN const len = SvCUR(sv); + const char* const pv = SvPVX_const(sv); - if (s_pref_int && try_int(enc, csv, len)) { + if (s_pref_int && try_int(enc, pv, len)) { return; } else { msgpack_pack_raw(enc, len); - msgpack_pack_raw_body(enc, csv, len); + msgpack_pack_raw_body(enc, pv, len); + } + } else if (SvNIOKp(sv)) { + if(SvUOK(sv)) { + PACK_UV(enc, SvUVX(sv)); + } + else if(SvIOKp(sv)) { + PACK_IV(enc, SvIVX(sv)); + } + else { + /* XXX long double is not supported yet. */ + msgpack_pack_double(enc, (double)SvNVX(sv)); } - } else if (SvNOKp(sv)) { - /* XXX long double is not supported yet. */ - msgpack_pack_double(enc, (double)SvNVX(sv)); - } else if (SvIOK_UV(sv)) { - msgpack_pack_uint32(enc, SvUV(sv)); - } else if (SvIOKp(sv)) { - PACK_IV(enc, SvIV(sv)); } else if (SvROK(sv)) { - _msgpack_pack_rv(enc, SvRV(sv)); + _msgpack_pack_rv(enc, SvRV(sv), depth-1); } else if (!SvOK(sv)) { msgpack_pack_nil(enc); } else if (isGV(sv)) { @@ -182,8 +185,10 @@ static void _msgpack_pack_sv(enc_t *enc, SV* sv) { } } -static void _msgpack_pack_rv(enc_t *enc, SV* sv) { +STATIC_INLINE void _msgpack_pack_rv(enc_t *enc, SV* sv, int depth) { svtype svt; + dTHX; + assert(sv); SvGETMAGIC(sv); svt = SvTYPE(sv); @@ -206,9 +211,9 @@ static void _msgpack_pack_rv(enc_t *enc, SV* sv) { msgpack_pack_map(enc, count); - while (he = hv_iternext(hval)) { - _msgpack_pack_sv(enc, hv_iterkeysv(he)); - _msgpack_pack_sv(enc, HeVAL(he)); + while ((he = hv_iternext(hval))) { + _msgpack_pack_sv(enc, hv_iterkeysv(he), depth); + _msgpack_pack_sv(enc, HeVAL(he), depth); } } else if (svt == SVt_PVAV) { AV* ary = (AV*)sv; @@ -218,7 +223,7 @@ static void _msgpack_pack_rv(enc_t *enc, SV* sv) { for (i=0; ipack($dat)"); + if (items < 2) { + Perl_croak(aTHX_ "Usage: Data::MessagePack->pack($dat [,$max_depth])"); } - SV* val = ST(1); + SV* val = ST(1); + int depth = 512; + if (items >= 3) depth = SvIV(ST(2)); enc_t enc; - enc.sv = sv_2mortal(NEWSV(0, INIT_SIZE)); + enc.sv = sv_2mortal(newSV(INIT_SIZE)); enc.cur = SvPVX(enc.sv); enc.end = SvEND(enc.sv); SvPOK_only(enc.sv); - _msgpack_pack_sv(&enc, val); + _msgpack_pack_sv(&enc, val, depth); SvCUR_set(enc.sv, enc.cur - SvPVX (enc.sv)); *SvEND (enc.sv) = 0; /* many xs functions expect a trailing 0 for text strings */ diff --git a/perl/xs-src/unpack.c b/perl/xs-src/unpack.c new file mode 100644 index 00000000..95b5910f --- /dev/null +++ b/perl/xs-src/unpack.c @@ -0,0 +1,439 @@ +#define NEED_newRV_noinc +#define NEED_sv_2pv_flags +#define NEED_my_snprintf +#include "xshelper.h" + +#define MY_CXT_KEY "Data::MessagePack::_unpack_guts" XS_VERSION +typedef struct { + SV* msgpack_true; + SV* msgpack_false; +} my_cxt_t; +START_MY_CXT + +typedef struct { + bool finished; + bool incremented; +} unpack_user; + +#include "msgpack/unpack_define.h" + +#define msgpack_unpack_struct(name) \ + struct template ## name + +#define msgpack_unpack_func(ret, name) \ + STATIC_INLINE ret template ## name + +#define msgpack_unpack_callback(name) \ + template_callback ## name + +#define msgpack_unpack_object SV* + +#define msgpack_unpack_user unpack_user + +void init_Data__MessagePack_unpack(pTHX_ bool const cloning) { + // booleans are load on demand (lazy load). + if(!cloning) { + MY_CXT_INIT; + MY_CXT.msgpack_true = NULL; + MY_CXT.msgpack_false = NULL; + } + else { + MY_CXT_CLONE; + MY_CXT.msgpack_true = NULL; + MY_CXT.msgpack_false = NULL; + } +} + + + +/* ---------------------------------------------------------------------- */ +/* utility functions */ + +static SV* +load_bool(pTHX_ const char* const name) { + CV* const cv = get_cv(name, GV_ADD); + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + call_sv((SV*)cv, G_SCALAR); + SPAGAIN; + SV* const sv = newSVsv(POPs); + PUTBACK; + FREETMPS; + LEAVE; + assert(sv); + assert(sv_isobject(sv)); + return sv; +} + +static SV* +get_bool(bool const value) { + dTHX; + dMY_CXT; + if(value) { + if(!MY_CXT.msgpack_true) { + MY_CXT.msgpack_true = load_bool(aTHX_ "Data::MessagePack::true"); + } + return newSVsv(MY_CXT.msgpack_true); + } + else { + if(!MY_CXT.msgpack_false) { + MY_CXT.msgpack_false = load_bool(aTHX_ "Data::MessagePack::false"); + } + return newSVsv(MY_CXT.msgpack_false); + } +} + +/* ---------------------------------------------------------------------- */ +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 PERL_UNUSED_DECL, + const char* data, size_t len, size_t* off); + +STATIC_INLINE SV* template_callback_root(unpack_user* u PERL_UNUSED_DECL) +{ + return NULL; +} + +#if IVSIZE == 4 + +STATIC_INLINE int template_callback_UV(unpack_user* u PERL_UNUSED_DECL, UV const d, SV** o) +{ + dTHX; + *o = newSVuv(d); + return 0; +} + +STATIC_INLINE int template_callback_IV(unpack_user* u PERL_UNUSED_DECL, IV const d, SV** o) +{ + dTHX; + *o = newSViv(d); + return 0; +} + +static int template_callback_uint64(unpack_user* u PERL_UNUSED_DECL, uint64_t const d, SV** o) +{ + dTHX; + char tbuf[64]; + STRLEN const len = my_snprintf(tbuf, sizeof(tbuf), "%llu", d); + *o = newSVpvn(tbuf, len); + return 0; +} + +static int template_callback_int64(unpack_user* u PERL_UNUSED_DECL, int64_t const d, SV** o) +{ + dTHX; + char tbuf[64]; + STRLEN const len = my_snprintf(tbuf, sizeof(tbuf), "%lld", d); + *o = newSVpvn(tbuf, len); + return 0; +} + +#else /* IVSIZE == 8 */ + + +STATIC_INLINE int template_callback_UV(unpack_user* u PERL_UNUSED_DECL, UV const d, SV** o) +{ + dTHX; + *o = newSVuv(d); + return 0; +} + +#define template_callback_uint64 template_callback_UV + +STATIC_INLINE int template_callback_IV(unpack_user* u PERL_UNUSED_DECL, IV const d, SV** o) +{ + dTHX; + *o = newSViv(d); + return 0; +} + +#define template_callback_int64 template_callback_IV + +#endif /* IVSIZE */ + +#define template_callback_uint8 template_callback_UV +#define template_callback_uint16 template_callback_UV +#define template_callback_uint32 template_callback_UV + +#define template_callback_int8 template_callback_IV +#define template_callback_int16 template_callback_IV +#define template_callback_int32 template_callback_IV + +#define template_callback_float template_callback_double + +STATIC_INLINE int template_callback_double(unpack_user* u PERL_UNUSED_DECL, double d, SV** o) +{ + dTHX; + *o = newSVnv(d); + return 0; +} + +/* &PL_sv_undef is not so good. see http://gist.github.com/387743 */ +STATIC_INLINE int template_callback_nil(unpack_user* u PERL_UNUSED_DECL, SV** o) +{ + dTHX; + *o = newSV(0); + return 0; +} + +STATIC_INLINE int template_callback_true(unpack_user* u PERL_UNUSED_DECL, SV** o) +{ + *o = get_bool(true); + return 0; +} + +STATIC_INLINE int template_callback_false(unpack_user* u PERL_UNUSED_DECL, SV** o) +{ + *o = get_bool(false); + return 0; +} + +STATIC_INLINE int template_callback_array(unpack_user* u PERL_UNUSED_DECL, unsigned int n, SV** o) +{ + dTHX; + AV* const a = newAV(); + *o = newRV_noinc((SV*)a); + av_extend(a, n + 1); + return 0; +} + +STATIC_INLINE int template_callback_array_item(unpack_user* u PERL_UNUSED_DECL, SV** c, SV* o) +{ + dTHX; + AV* const a = (AV*)SvRV(*c); + assert(SvTYPE(a) == SVt_PVAV); + (void)av_store(a, AvFILLp(a) + 1, o); // the same as av_push(a, o) + return 0; +} + +STATIC_INLINE int template_callback_map(unpack_user* u PERL_UNUSED_DECL, unsigned int n, SV** o) +{ + dTHX; + HV* const h = newHV(); + hv_ksplit(h, n); + *o = newRV_noinc((SV*)h); + return 0; +} + +STATIC_INLINE int template_callback_map_item(unpack_user* u PERL_UNUSED_DECL, SV** c, SV* k, SV* v) +{ + dTHX; + HV* const h = (HV*)SvRV(*c); + assert(SvTYPE(h) == SVt_PVHV); + (void)hv_store_ent(h, k, v, 0); + SvREFCNT_dec(k); + return 0; +} + +STATIC_INLINE int template_callback_raw(unpack_user* u PERL_UNUSED_DECL, const char* b PERL_UNUSED_DECL, const char* p, unsigned int l, SV** o) +{ + dTHX; + /* newSVpvn(p, l) returns an undef if p == NULL */ + *o = ((l==0) ? newSVpvs("") : newSVpvn(p, l)); + return 0; +} + +#include "msgpack/unpack_template.h" + +#define UNPACKER(from, name) \ + msgpack_unpack_t *name; \ + if(!(SvROK(from) && SvIOK(SvRV(from)))) { \ + Perl_croak(aTHX_ "Invalid unpacker instance for " #name); \ + } \ + name = INT2PTR(msgpack_unpack_t*, SvIVX(SvRV((from)))); \ + if(name == NULL) { \ + Perl_croak(aTHX_ "NULL found for " # name " when shouldn't be."); \ + } + +XS(xs_unpack) { + dXSARGS; + SV* const data = ST(1); + size_t limit; + + if (items == 2) { + limit = sv_len(data); + } + else if(items == 3) { + limit = SvUVx(ST(2)); + } + else { + Perl_croak(aTHX_ "Usage: Data::MessagePack->unpack('data' [, $limit])"); + } + + STRLEN dlen; + const char* const dptr = SvPV_const(data, dlen); + + msgpack_unpack_t mp; + template_init(&mp); + + unpack_user const u = {false, false}; + mp.user = u; + + size_t from = 0; + int const ret = template_execute(&mp, dptr, (size_t)dlen, &from); + SV* const obj = template_data(&mp); + sv_2mortal(obj); + + if(ret < 0) { + Perl_croak(aTHX_ "Data::MessagePack->unpack: parse error"); + } else if(ret == 0) { + Perl_croak(aTHX_ "Data::MessagePack->unpack: insufficient bytes"); + } else { + if(from < dlen) { + Perl_croak(aTHX_ "Data::MessagePack->unpack: extra bytes"); + } + } + + ST(0) = obj; + XSRETURN(1); +} + +/* ------------------------------ stream -- */ +/* http://twitter.com/frsyuki/status/13249304748 */ + +STATIC_INLINE void _reset(SV* const self) { + dTHX; + unpack_user const u = {false, false}; + + UNPACKER(self, mp); + template_init(mp); + mp->user = u; +} + +XS(xs_unpacker_new) { + dXSARGS; + if (items != 1) { + Perl_croak(aTHX_ "Usage: Data::MessagePack::Unpacker->new()"); + } + + SV* const self = sv_newmortal(); + msgpack_unpack_t *mp; + + Newxz(mp, 1, msgpack_unpack_t); + + sv_setref_pv(self, "Data::MessagePack::Unpacker", mp); + _reset(self); + + ST(0) = self; + XSRETURN(1); +} + +STATIC_INLINE SV* +_execute_impl(SV* const self, SV* const data, UV const offset, UV const limit) { + dTHX; + + if(offset >= limit) { + Perl_croak(aTHX_ "offset (%"UVuf") is bigger than data buffer size (%"UVuf")", + offset, limit); + } + + UNPACKER(self, mp); + + size_t from = offset; + const char* const dptr = SvPV_nolen_const(data); + + int const ret = template_execute(mp, dptr, limit, &from); + + if(ret < 0) { + Perl_croak(aTHX_ "Data::MessagePack::Unpacker: parse error while executing"); + } else { + mp->user.finished = (ret > 0) ? true : false; + return sv_2mortal(newSVuv(from)); + } +} + +XS(xs_unpacker_execute) { + dXSARGS; + SV* const self = ST(0); + SV* const data = ST(1); + UV offset; + + if (items == 2) { + offset = 0; + } + else if (items == 3) { + offset = SvUVx(ST(2)); + } + else { + Perl_croak(aTHX_ "Usage: $unpacker->execute(data, offset = 0)"); + } + + UNPACKER(self, mp); + + ST(0) = _execute_impl(self, data, offset, sv_len(data)); + XSRETURN(1); +} + +XS(xs_unpacker_execute_limit) { + dXSARGS; + if (items != 4) { + Perl_croak(aTHX_ "Usage: $unpacker->execute_limit(data, offset, limit)"); + } + + SV* const self = ST(0); + SV* const data = ST(1); + UV const offset = SvUVx(ST(2)); + UV const limit = SvUVx(ST(3)); + + ST(0) = _execute_impl(self, data, offset, limit); + XSRETURN(1); +} + +XS(xs_unpacker_is_finished) { + dXSARGS; + if (items != 1) { + Perl_croak(aTHX_ "Usage: $unpacker->is_finished()"); + } + + UNPACKER(ST(0), mp); + ST(0) = boolSV(mp->user.finished); + XSRETURN(1); +} + +XS(xs_unpacker_data) { + dXSARGS; + if (items != 1) { + Perl_croak(aTHX_ "Usage: $unpacker->data()"); + } + + 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()"); + } + + UNPACKER(ST(0), mp); + + SV* const data = template_data(mp); + sv_2mortal(data); + _reset(ST(0)); + + XSRETURN(0); +} + +XS(xs_unpacker_destroy) { + dXSARGS; + if (items != 1) { + Perl_croak(aTHX_ "Usage: $unpacker->DESTROY()"); + } + + UNPACKER(ST(0), mp); + + SV* const data = template_data(mp); + sv_2mortal(data); + Safefree(mp); + + XSRETURN(0); +} diff --git a/python/.gitignore b/python/.gitignore index 430c633e..8531de3f 100644 --- a/python/.gitignore +++ b/python/.gitignore @@ -3,3 +3,4 @@ build/* dist/* *.pyc *.pyo +msgpack/__version__.py diff --git a/python/Makefile b/python/Makefile index e06794da..245c09c2 100644 --- a/python/Makefile +++ b/python/Makefile @@ -1,7 +1,12 @@ +.PHONY: test all python3 + all: python setup.py build_ext -i -f python setup.py build sdist -.PHONY: test +python3: + python3 setup.py build_ext -i -f + python3 setup.py build sdist + test: nosetests test diff --git a/python/msgpack/__init__.py b/python/msgpack/__init__.py index 797b29c2..cdf045fb 100644 --- a/python/msgpack/__init__.py +++ b/python/msgpack/__init__.py @@ -1,3 +1,11 @@ # coding: utf-8 -from _msgpack import * +from msgpack.__version__ import * +from msgpack._msgpack import * + +# alias for compatibility to simplejson/marshal/pickle. +load = unpack +loads = unpackb + +dump = pack +dumps = packb diff --git a/python/msgpack/_msgpack.pyx b/python/msgpack/_msgpack.pyx index 61ae36b2..c887127b 100644 --- a/python/msgpack/_msgpack.pyx +++ b/python/msgpack/_msgpack.pyx @@ -1,26 +1,24 @@ # coding: utf-8 -import cStringIO - cdef extern from "Python.h": ctypedef char* const_char_ptr "const char*" ctypedef struct PyObject - cdef object PyString_FromStringAndSize(const_char_ptr b, Py_ssize_t len) + cdef object PyBytes_FromStringAndSize(const_char_ptr b, Py_ssize_t len) cdef PyObject* Py_True cdef PyObject* Py_False - cdef char* PyString_AsString(object o) cdef long long PyLong_AsLongLong(object o) cdef unsigned long long PyLong_AsUnsignedLongLong(object o) - cdef int PyMapping_Check(object o) - cdef int PySequence_Check(object o) - cdef int PyLong_Check(object o) - cdef int PyInt_Check(object o) - cdef int PyFloat_Check(object o) - cdef int PyString_Check(object o) - cdef int PyUnicode_Check(object o) + cdef bint PyBool_Check(object o) + cdef bint PyDict_Check(object o) + cdef bint PySequence_Check(object o) + cdef bint PyLong_Check(object o) + cdef bint PyInt_Check(object o) + cdef bint PyFloat_Check(object o) + cdef bint PyBytes_Check(object o) + cdef bint PyUnicode_Check(object o) cdef extern from "stdlib.h": void* malloc(size_t) @@ -71,20 +69,23 @@ cdef class Packer(object): def __dealloc__(self): free(self.pk.buf); - cdef int __pack(self, object o) except -1: + cdef int _pack(self, object o) except -1: cdef long long llval cdef unsigned long long ullval cdef long longval cdef double fval cdef char* rawval cdef int ret + cdef dict d if o is None: ret = msgpack_pack_nil(&self.pk) - elif o == Py_True: - ret = msgpack_pack_true(&self.pk) - elif o == Py_False: - ret = msgpack_pack_false(&self.pk) + #elif PyBool_Check(o): + elif isinstance(o, bool): + if o: + ret = msgpack_pack_true(&self.pk) + else: + ret = msgpack_pack_false(&self.pk) elif PyLong_Check(o): if o > 0: ullval = PyLong_AsUnsignedLongLong(o) @@ -98,7 +99,7 @@ cdef class Packer(object): elif PyFloat_Check(o): fval = o ret = msgpack_pack_double(&self.pk, fval) - elif PyString_Check(o): + elif PyBytes_Check(o): rawval = o ret = msgpack_pack_raw(&self.pk, len(o)) if ret == 0: @@ -109,19 +110,20 @@ cdef class Packer(object): ret = msgpack_pack_raw(&self.pk, len(o)) if ret == 0: ret = msgpack_pack_raw_body(&self.pk, rawval, len(o)) - elif PyMapping_Check(o): - ret = msgpack_pack_map(&self.pk, len(o)) + elif PyDict_Check(o): + d = o + ret = msgpack_pack_map(&self.pk, len(d)) if ret == 0: - for k,v in o.iteritems(): - ret = self.__pack(k) + for k,v in d.items(): + ret = self._pack(k) if ret != 0: break - ret = self.__pack(v) + ret = self._pack(v) if ret != 0: break elif PySequence_Check(o): ret = msgpack_pack_array(&self.pk, len(o)) if ret == 0: for v in o: - ret = self.__pack(v) + ret = self._pack(v) if ret != 0: break else: # TODO: Serialize with defalt() like simplejson. @@ -130,10 +132,10 @@ cdef class Packer(object): def pack(self, object obj): cdef int ret - ret = self.__pack(obj) + ret = self._pack(obj) if ret: raise TypeError - buf = PyString_FromStringAndSize(self.pk.buf, self.pk.length) + buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length) self.pk.length = 0 return buf @@ -253,19 +255,18 @@ cdef class Unpacker(object): template_init(&self.ctx) self.ctx.user.use_list = use_list - def feed(self, next_bytes): - if not isinstance(next_bytes, str): - raise ValueError, "Argument must be bytes object" + def feed(self, bytes next_bytes): self.waiting_bytes.append(next_bytes) cdef append_buffer(self): cdef char* buf = self.buf cdef Py_ssize_t tail = self.buf_tail cdef Py_ssize_t l + cdef bytes b for b in self.waiting_bytes: l = len(b) - memcpy(buf + tail, PyString_AsString(b), l) + memcpy(buf + tail, (b), l) tail += l self.buf_tail = tail del self.waiting_bytes[:] diff --git a/python/msgpack/unpack.h b/python/msgpack/unpack.h index 61a3786c..9eb8ce77 100644 --- a/python/msgpack/unpack.h +++ b/python/msgpack/unpack.h @@ -175,7 +175,7 @@ static inline int template_callback_map_item(unpack_user* u, msgpack_unpack_obje static inline int template_callback_raw(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_unpack_object* o) { PyObject *py; - py = PyString_FromStringAndSize(p, l); + py = PyBytes_FromStringAndSize(p, l); if (!py) return -1; *o = py; diff --git a/python/setup.py b/python/setup.py index 8d8a6f45..ac7ece5c 100755 --- a/python/setup.py +++ b/python/setup.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # coding: utf-8 +version = (0, 1, 6, 'final') import os from glob import glob @@ -14,7 +15,13 @@ except ImportError: from distutils.command.build_ext import build_ext have_cython = False -version = '0.1.3' +# make msgpack/__verison__.py +f = open('msgpack/__version__.py', 'w') +f.write("version = %r\n" % (version,)) +f.close() +version_str = '.'.join(str(x) for x in version[:3]) +if len(version) > 3 and version[3] != 'final': + version_str += version[3] # take care of extension modules. if have_cython: @@ -53,7 +60,7 @@ What's MessagePack? (from http://msgpack.sourceforge.net/) setup(name='msgpack-python', author='INADA Naoki', author_email='songofacandy@gmail.com', - version=version, + version=version_str, cmdclass={'build_ext': build_ext, 'sdist': Sdist}, ext_modules=[msgpack_mod], packages=['msgpack'], @@ -62,6 +69,7 @@ setup(name='msgpack-python', url='http://msgpack.sourceforge.net/', download_url='http://pypi.python.org/pypi/msgpack/', classifiers=[ + 'Programming Language :: Python :: 3', 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', diff --git a/python/test3/test_case.py b/python/test3/test_case.py new file mode 100644 index 00000000..53dfcaf0 --- /dev/null +++ b/python/test3/test_case.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# coding: utf-8 + +from nose import main +from nose.tools import * +from msgpack import packs, unpacks + + +def check(length, obj): + v = packs(obj) + assert_equal(len(v), length, "%r length should be %r but get %r" % (obj, length, len(v))) + assert_equal(unpacks(v), obj) + +def test_1(): + for o in [None, True, False, 0, 1, (1 << 6), (1 << 7) - 1, -1, + -((1<<5)-1), -(1<<5)]: + check(1, o) + +def test_2(): + for o in [1 << 7, (1 << 8) - 1, + -((1<<5)+1), -(1<<7) + ]: + check(2, o) + +def test_3(): + for o in [1 << 8, (1 << 16) - 1, + -((1<<7)+1), -(1<<15)]: + check(3, o) + +def test_5(): + for o in [1 << 16, (1 << 32) - 1, + -((1<<15)+1), -(1<<31)]: + check(5, o) + +def test_9(): + for o in [1 << 32, (1 << 64) - 1, + -((1<<31)+1), -(1<<63), + 1.0, 0.1, -0.1, -1.0]: + check(9, o) + + +def check_raw(overhead, num): + check(num + overhead, b" " * num) + +def test_fixraw(): + check_raw(1, 0) + check_raw(1, (1<<5) - 1) + +def test_raw16(): + check_raw(3, 1<<5) + check_raw(3, (1<<16) - 1) + +def test_raw32(): + check_raw(5, 1<<16) + + +def check_array(overhead, num): + check(num + overhead, (None,) * num) + +def test_fixarray(): + check_array(1, 0) + check_array(1, (1 << 4) - 1) + +def test_array16(): + check_array(3, 1 << 4) + check_array(3, (1<<16)-1) + +def test_array32(): + check_array(5, (1<<16)) + + +def match(obj, buf): + assert_equal(packs(obj), buf) + assert_equal(unpacks(buf), obj) + +def test_match(): + cases = [ + (None, b'\xc0'), + (False, b'\xc2'), + (True, b'\xc3'), + (0, b'\x00'), + (127, b'\x7f'), + (128, b'\xcc\x80'), + (256, b'\xcd\x01\x00'), + (-1, b'\xff'), + (-33, b'\xd0\xdf'), + (-129, b'\xd1\xff\x7f'), + ({1:1}, b'\x81\x01\x01'), + (1.0, b"\xcb\x3f\xf0\x00\x00\x00\x00\x00\x00"), + ((), b'\x90'), + (tuple(range(15)),b"\x9f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"), + (tuple(range(16)),b"\xdc\x00\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"), + ({}, b'\x80'), + (dict([(x,x) for x in range(15)]), b'\x8f\x00\x00\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07\x08\x08\t\t\n\n\x0b\x0b\x0c\x0c\r\r\x0e\x0e'), + (dict([(x,x) for x in range(16)]), b'\xde\x00\x10\x00\x00\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07\x08\x08\t\t\n\n\x0b\x0b\x0c\x0c\r\r\x0e\x0e\x0f\x0f'), + ] + + for v, p in cases: + match(v, p) + +if __name__ == '__main__': + main() diff --git a/python/test3/test_except.py b/python/test3/test_except.py new file mode 100644 index 00000000..574728fb --- /dev/null +++ b/python/test3/test_except.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# coding: utf-8 + +from nose.tools import * +from msgpack import packs, unpacks + +import datetime + +def test_raise_on_find_unsupported_value(): + assert_raises(TypeError, packs, datetime.datetime.now()) + +if __name__ == '__main__': + from nose import main + main() diff --git a/python/test3/test_format.py b/python/test3/test_format.py new file mode 100644 index 00000000..022e6801 --- /dev/null +++ b/python/test3/test_format.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# coding: utf-8 + +from nose import main +from nose.tools import * +from msgpack import unpacks + +def check(src, should): + assert_equal(unpacks(src), should) + +def testSimpleValue(): + check(b"\x93\xc0\xc2\xc3", + (None, False, True,)) + +def testFixnum(): + check(b"\x92\x93\x00\x40\x7f\x93\xe0\xf0\xff", + ((0,64,127,), (-32,-16,-1,),) + ) + +def testFixArray(): + check(b"\x92\x90\x91\x91\xc0", + ((),((None,),),), + ) + +def testFixRaw(): + check(b"\x94\xa0\xa1a\xa2bc\xa3def", + (b"", b"a", b"bc", b"def",), + ) + +def testFixMap(): + check( + b"\x82\xc2\x81\xc0\xc0\xc3\x81\xc0\x80", + {False: {None: None}, True:{None:{}}}, + ) + +def testUnsignedInt(): + check( + b"\x99\xcc\x00\xcc\x80\xcc\xff\xcd\x00\x00\xcd\x80\x00" + b"\xcd\xff\xff\xce\x00\x00\x00\x00\xce\x80\x00\x00\x00" + b"\xce\xff\xff\xff\xff", + (0, 128, 255, 0, 32768, 65535, 0, 2147483648, 4294967295,), + ) + +def testSignedInt(): + check(b"\x99\xd0\x00\xd0\x80\xd0\xff\xd1\x00\x00\xd1\x80\x00" + b"\xd1\xff\xff\xd2\x00\x00\x00\x00\xd2\x80\x00\x00\x00" + b"\xd2\xff\xff\xff\xff", + (0, -128, -1, 0, -32768, -1, 0, -2147483648, -1,)) + +def testRaw(): + check(b"\x96\xda\x00\x00\xda\x00\x01a\xda\x00\x02ab\xdb\x00\x00" + b"\x00\x00\xdb\x00\x00\x00\x01a\xdb\x00\x00\x00\x02ab", + (b"", b"a", b"ab", b"", b"a", b"ab")) + +def testArray(): + check(b"\x96\xdc\x00\x00\xdc\x00\x01\xc0\xdc\x00\x02\xc2\xc3\xdd\x00" + b"\x00\x00\x00\xdd\x00\x00\x00\x01\xc0\xdd\x00\x00\x00\x02" + b"\xc2\xc3", + ((), (None,), (False,True), (), (None,), (False,True)) + ) + +def testMap(): + check( + b"\x96" + b"\xde\x00\x00" + b"\xde\x00\x01\xc0\xc2" + b"\xde\x00\x02\xc0\xc2\xc3\xc2" + b"\xdf\x00\x00\x00\x00" + b"\xdf\x00\x00\x00\x01\xc0\xc2" + b"\xdf\x00\x00\x00\x02\xc0\xc2\xc3\xc2", + ({}, {None: False}, {True: False, None: False}, {}, + {None: False}, {True: False, None: False})) + +if __name__ == '__main__': + main() diff --git a/python/test3/test_pack.py b/python/test3/test_pack.py new file mode 100644 index 00000000..c861704b --- /dev/null +++ b/python/test3/test_pack.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# coding: utf-8 + +from nose import main +from nose.tools import * + +from msgpack import packs, unpacks + +def check(data): + re = unpacks(packs(data)) + assert_equal(re, data) + +def testPack(): + test_data = [ + 0, 1, 127, 128, 255, 256, 65535, 65536, + -1, -32, -33, -128, -129, -32768, -32769, + 1.0, + b"", b"a", b"a"*31, b"a"*32, + None, True, False, + (), ((),), ((), None,), + {None: 0}, + (1<<23), + ] + for td in test_data: + check(td) + +if __name__ == '__main__': + main() diff --git a/python/test3/test_sequnpack.py b/python/test3/test_sequnpack.py new file mode 100644 index 00000000..5fd377c4 --- /dev/null +++ b/python/test3/test_sequnpack.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# coding: utf-8 + + + +from msgpack import Unpacker + +def test_foobar(): + unpacker = Unpacker(read_size=3) + unpacker.feed(b'foobar') + assert unpacker.unpack() == ord(b'f') + assert unpacker.unpack() == ord(b'o') + assert unpacker.unpack() == ord(b'o') + assert unpacker.unpack() == ord(b'b') + assert unpacker.unpack() == ord(b'a') + assert unpacker.unpack() == ord(b'r') + try: + o = unpacker.unpack() + print(("Oops!", o)) + assert 0 + except StopIteration: + assert 1 + else: + assert 0 + unpacker.feed(b'foo') + unpacker.feed(b'bar') + + k = 0 + for o, e in zip(unpacker, b'foobarbaz'): + assert o == e + k += 1 + assert k == len(b'foobar') + +if __name__ == '__main__': + test_foobar() + diff --git a/ruby/ChangeLog b/ruby/ChangeLog index e69de29b..d3a72829 100644 --- a/ruby/ChangeLog +++ b/ruby/ChangeLog @@ -0,0 +1,6 @@ + +2010-06-29 version 0.4.3: + + * Adds MessagePack::VERSION constant + * Fixes SEGV problem caused by GC bug at MessagePack_Unpacker_mark + diff --git a/ruby/compat.h b/ruby/compat.h new file mode 100644 index 00000000..d7a2ca78 --- /dev/null +++ b/ruby/compat.h @@ -0,0 +1,77 @@ +/* + * MessagePack for Ruby + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef COMPAT_H__ +#define COMPAT_H__ + + +#ifdef HAVE_RUBY_ENCODING_H +#include "ruby/encoding.h" +#define COMPAT_HAVE_ENCODING +extern int s_enc_utf8; +extern int s_enc_ascii8bit; +extern int s_enc_usascii; +extern VALUE s_enc_utf8_value; +#endif + +#ifdef RUBY_VM +#define COMPAT_RERAISE rb_exc_raise(rb_errinfo()) +#else +#define COMPAT_RERAISE rb_exc_raise(ruby_errinfo) +#endif + + +/* ruby 1.8 and Rubinius */ +#ifndef RBIGNUM_POSITIVE_P +# ifdef RUBINIUS +# define RBIGNUM_POSITIVE_P(b) (rb_funcall(b, rb_intern(">="), 1, INT2FIX(0)) == Qtrue) +# else +# define RBIGNUM_POSITIVE_P(b) (RBIGNUM(b)->sign) +# endif +#endif + + +/* Rubinius */ +#ifdef RUBINIUS +static inline void rb_gc_enable() { return; } +static inline void rb_gc_disable() { return; } +#endif + + +/* ruby 1.8.5 */ +#ifndef RSTRING_PTR +#define RSTRING_PTR(s) (RSTRING(s)->ptr) +#endif + +/* ruby 1.8.5 */ +#ifndef RSTRING_LEN +#define RSTRING_LEN(s) (RSTRING(s)->len) +#endif + +/* ruby 1.8.5 */ +#ifndef RARRAY_PTR +#define RARRAY_PTR(s) (RARRAY(s)->ptr) +#endif + +/* ruby 1.8.5 */ +#ifndef RARRAY_LEN +#define RARRAY_LEN(s) (RARRAY(s)->len) +#endif + + +#endif /* compat.h */ + diff --git a/ruby/extconf.rb b/ruby/extconf.rb index e6d4bd6d..f1d44ec1 100644 --- a/ruby/extconf.rb +++ b/ruby/extconf.rb @@ -1,4 +1,5 @@ require 'mkmf' -$CFLAGS << " -I.. -Wall -O4" +require './version.rb' +$CFLAGS << %[ -I.. -Wall -O4 -DMESSAGEPACK_VERSION=\\"#{MessagePack::VERSION}\\" -g] create_makefile('msgpack') diff --git a/ruby/makegem.sh b/ruby/makegem.sh index 827f4521..bf30cd47 100755 --- a/ruby/makegem.sh +++ b/ruby/makegem.sh @@ -8,6 +8,7 @@ cp pack.h ext/ cp rbinit.c ext/ cp unpack.c ext/ cp unpack.h ext/ +cp version.rb ext/ cp ../msgpack/pack_define.h msgpack/ cp ../msgpack/pack_template.h msgpack/ cp ../msgpack/unpack_define.h msgpack/ @@ -19,6 +20,8 @@ cp ../test/cases.json test/ gem build msgpack.gemspec +rdoc rbinit.c pack.c unpack.c + if [ $? -eq 0 ]; then rm -rf ext msgpack test/msgpack_test.rb fi diff --git a/ruby/msgpack.gemspec b/ruby/msgpack.gemspec index fb6338a5..95a2bd0a 100644 --- a/ruby/msgpack.gemspec +++ b/ruby/msgpack.gemspec @@ -1,7 +1,8 @@ +require './version.rb' Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY s.name = "msgpack" - s.version = "0.4.2" + s.version = MessagePack::VERSION s.summary = "MessagePack, a binary-based efficient data interchange format." s.author = "FURUHASHI Sadayuki" s.email = "frsyuki@users.sourceforge.jp" diff --git a/ruby/pack.c b/ruby/pack.c index bbeac4a5..8ce46aaf 100644 --- a/ruby/pack.c +++ b/ruby/pack.c @@ -16,6 +16,8 @@ * limitations under the License. */ #include "ruby.h" +#include "compat.h" + #include "msgpack/pack_define.h" static ID s_to_msgpack; @@ -116,10 +118,6 @@ static VALUE MessagePack_Fixnum_to_msgpack(int argc, VALUE *argv, VALUE self) } -#ifndef RBIGNUM_SIGN // Ruby 1.8 -#define RBIGNUM_SIGN(b) (RBIGNUM(b)->sign) -#endif - /* * Document-method: Bignum#to_msgpack * @@ -131,10 +129,9 @@ static VALUE MessagePack_Fixnum_to_msgpack(int argc, VALUE *argv, VALUE self) static VALUE MessagePack_Bignum_to_msgpack(int argc, VALUE *argv, VALUE self) { ARG_BUFFER(out, argc, argv); - // FIXME bignum - if(RBIGNUM_SIGN(self)) { // positive + if(RBIGNUM_POSITIVE_P(self)) { msgpack_pack_uint64(out, rb_big2ull(self)); - } else { // negative + } else { msgpack_pack_int64(out, rb_big2ll(self)); } return out; @@ -168,6 +165,14 @@ static VALUE MessagePack_Float_to_msgpack(int argc, VALUE *argv, VALUE self) static VALUE MessagePack_String_to_msgpack(int argc, VALUE *argv, VALUE self) { ARG_BUFFER(out, argc, argv); +#ifdef COMPAT_HAVE_ENCODING + int enc = ENCODING_GET(self); + if(enc != s_enc_utf8 && enc != s_enc_ascii8bit && enc != s_enc_usascii) { + if(!ENC_CODERANGE_ASCIIONLY(self)) { + self = rb_str_encode(self, s_enc_utf8_value, 0, Qnil); + } + } +#endif msgpack_pack_raw(out, RSTRING_LEN(self)); msgpack_pack_raw_body(out, RSTRING_PTR(self), RSTRING_LEN(self)); return out; @@ -184,12 +189,16 @@ static VALUE MessagePack_String_to_msgpack(int argc, VALUE *argv, VALUE self) */ static VALUE MessagePack_Symbol_to_msgpack(int argc, VALUE *argv, VALUE self) { +#ifdef COMPAT_HAVE_ENCODING + return MessagePack_String_to_msgpack(argc, argv, rb_id2str(SYM2ID(self))); +#else ARG_BUFFER(out, argc, argv); const char* name = rb_id2name(SYM2ID(self)); size_t len = strlen(name); msgpack_pack_raw(out, len); msgpack_pack_raw_body(out, name, len); return out; +#endif } @@ -205,7 +214,8 @@ static VALUE MessagePack_Symbol_to_msgpack(int argc, VALUE *argv, VALUE self) static VALUE MessagePack_Array_to_msgpack(int argc, VALUE *argv, VALUE self) { ARG_BUFFER(out, argc, argv); - msgpack_pack_array(out, RARRAY_LEN(self)); + // FIXME check sizeof(long) > sizeof(unsigned int) && RARRAY_LEN(self) > UINT_MAX + msgpack_pack_array(out, (unsigned int)RARRAY_LEN(self)); VALUE* p = RARRAY_PTR(self); VALUE* const pend = p + RARRAY_LEN(self); for(;p != pend; ++p) { @@ -239,7 +249,8 @@ static int MessagePack_Hash_to_msgpack_foreach(VALUE key, VALUE value, VALUE out static VALUE MessagePack_Hash_to_msgpack(int argc, VALUE *argv, VALUE self) { ARG_BUFFER(out, argc, argv); - msgpack_pack_map(out, RHASH_SIZE(self)); + // FIXME check sizeof(st_index_t) > sizeof(unsigned int) && RARRAY_LEN(self) > UINT_MAX + msgpack_pack_map(out, (unsigned int)RHASH_SIZE(self)); rb_hash_foreach(self, MessagePack_Hash_to_msgpack_foreach, out); return out; } diff --git a/ruby/rbinit.c b/ruby/rbinit.c index ad51f6b4..1d1cbc69 100644 --- a/ruby/rbinit.c +++ b/ruby/rbinit.c @@ -17,9 +17,17 @@ */ #include "pack.h" #include "unpack.h" +#include "compat.h" static VALUE mMessagePack; +#ifdef COMPAT_HAVE_ENCODING +int s_enc_utf8; +int s_enc_ascii8bit; +int s_enc_usascii; +VALUE s_enc_utf8_value; +#endif + /** * Document-module: MessagePack * @@ -43,7 +51,16 @@ static VALUE mMessagePack; void Init_msgpack(void) { mMessagePack = rb_define_module("MessagePack"); + + rb_define_const(mMessagePack, "VERSION", rb_str_new2(MESSAGEPACK_VERSION)); + +#ifdef COMPAT_HAVE_ENCODING + s_enc_ascii8bit = rb_ascii8bit_encindex(); + s_enc_utf8 = rb_utf8_encindex(); + s_enc_usascii = rb_usascii_encindex(); + s_enc_utf8_value = rb_enc_from_encoding(rb_utf8_encoding()); +#endif + Init_msgpack_unpack(mMessagePack); Init_msgpack_pack(mMessagePack); } - diff --git a/ruby/test/test_encoding.rb b/ruby/test/test_encoding.rb new file mode 100644 index 00000000..2cf0767c --- /dev/null +++ b/ruby/test/test_encoding.rb @@ -0,0 +1,68 @@ +#!/usr/bin/env ruby +require File.dirname(__FILE__)+'/test_helper' + +if RUBY_VERSION < "1.9" + exit +end + +class MessagePackTestEncoding < Test::Unit::TestCase + def self.it(name, &block) + define_method("test_#{name}", &block) + end + + it "US-ASCII" do + check_unpack "abc".force_encoding("US-ASCII") + end + + it "UTF-8 ascii" do + check_unpack "abc".force_encoding("UTF-8") + end + + it "UTF-8 mbstr" do + check_unpack "\xE3\x81\x82".force_encoding("UTF-8") + end + + it "UTF-8 invalid" do + check_unpack "\xD0".force_encoding("UTF-8") + end + + it "ASCII-8BIT" do + check_unpack "\xD0".force_encoding("ASCII-8BIT") + end + + it "EUC-JP" do + x = "\xA4\xA2".force_encoding("EUC-JP") + check_unpack(x) + end + + it "EUC-JP invalid" do + begin + "\xD0".force_encoding("EUC-JP").to_msgpack + assert(false) + rescue Encoding::InvalidByteSequenceError + assert(true) + end + end + + private + def check_unpack(str) + if str.encoding.to_s == "ASCII-8BIT" + should_str = str.dup.force_encoding("UTF-8") + else + should_str = str.encode("UTF-8") + end + + raw = str.to_msgpack + r = MessagePack.unpack(str.to_msgpack) + assert_equal(r.encoding.to_s, "UTF-8") + assert_equal(r, should_str.force_encoding("UTF-8")) + + if str.valid_encoding? + sym = str.to_sym + r = MessagePack.unpack(sym.to_msgpack) + assert_equal(r.encoding.to_s, "UTF-8") + assert_equal(r, should_str.force_encoding("UTF-8")) + end + end +end + diff --git a/ruby/test/test_helper.rb b/ruby/test/test_helper.rb index 19226ef0..4def861a 100644 --- a/ruby/test/test_helper.rb +++ b/ruby/test/test_helper.rb @@ -5,3 +5,6 @@ rescue LoadError require File.dirname(__FILE__) + '/../lib/msgpack' end +if ENV["GC_STRESS"] + GC.stress = true +end diff --git a/ruby/test/test_pack_unpack.rb b/ruby/test/test_pack_unpack.rb index 9dff44f1..f378c3c7 100644 --- a/ruby/test/test_pack_unpack.rb +++ b/ruby/test/test_pack_unpack.rb @@ -153,7 +153,8 @@ class MessagePackTestPackUnpack < Test::Unit::TestCase end it "{1=>1}" do - match ({1=>1}), "\x81\x01\x01" + obj = {1=>1} + match obj, "\x81\x01\x01" end it "1.0" do @@ -165,15 +166,18 @@ class MessagePackTestPackUnpack < Test::Unit::TestCase end it "[0, 1, ..., 14]" do - match (0..14).to_a, "\x9f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e" + obj = (0..14).to_a + match obj, "\x9f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e" end it "[0, 1, ..., 15]" do - match (0..15).to_a, "\xdc\x00\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + obj = (0..15).to_a + match obj, "\xdc\x00\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" end it "{}" do - match ({}), "\x80" + obj = {} + match obj, "\x80" end ## FIXME @@ -235,7 +239,7 @@ class MessagePackTestPackUnpack < Test::Unit::TestCase end it "gc mark" do - obj = [{["a","b"]=>["c","d"]}, ["e","f"], "d"] + obj = [1024, {["a","b"]=>["c","d"]}, ["e","f"], "d", 70000, 4.12, 1.5, 1.5, 1.5] num = 4 raw = obj.to_msgpack * num pac = MessagePack::Unpacker.new @@ -253,7 +257,7 @@ class MessagePackTestPackUnpack < Test::Unit::TestCase end it "streaming backward compatibility" do - obj = [{["a","b"]=>["c","d"]}, ["e","f"], "d"] + obj = [1024, {["a","b"]=>["c","d"]}, ["e","f"], "d", 70000, 4.12, 1.5, 1.5, 1.5] num = 4 raw = obj.to_msgpack * num pac = MessagePack::Unpacker.new @@ -276,6 +280,10 @@ class MessagePackTestPackUnpack < Test::Unit::TestCase assert_equal(parsed, num) end + it "MessagePack::VERSION constant" do + p MessagePack::VERSION + end + private def check(len, obj) v = obj.to_msgpack diff --git a/ruby/unpack.c b/ruby/unpack.c index 65ae476e..2d10e75d 100644 --- a/ruby/unpack.c +++ b/ruby/unpack.c @@ -16,17 +16,13 @@ * limitations under the License. */ #include "ruby.h" +#include "compat.h" #include "msgpack/unpack_define.h" static ID s_sysread; static ID s_readpartial; -#ifdef HAVE_RUBY_ENCODING_H -#include "ruby/encoding.h" -int s_ascii_8bit; -#endif - struct unpack_buffer { size_t size; size_t free; @@ -136,6 +132,9 @@ static inline int template_callback_raw(unpack_user* u, const char* b, const cha } else { *o = rb_str_substr(u->source, p - b, l); } +#ifdef COMPAT_HAVE_ENCODING + ENCODING_SET(*o, s_enc_utf8); +#endif return 0; } @@ -156,27 +155,11 @@ static inline int template_callback_raw(unpack_user* u, const char* b, const cha rb_raise(rb_eTypeError, "instance of String needed"); \ } -#ifdef RUBY_VM -#define RERAISE rb_exc_raise(rb_errinfo()) -#else -#define RERAISE rb_exc_raise(ruby_errinfo) -#endif - - -#ifdef HAVE_RUBY_ENCODING_H -static VALUE template_execute_rescue_enc(VALUE data) -{ - rb_gc_enable(); - VALUE* resc = (VALUE*)data; - rb_enc_set_index(resc[0], (int)resc[1]); - RERAISE; -} -#endif static VALUE template_execute_rescue(VALUE nouse) { rb_gc_enable(); - RERAISE; + COMPAT_RERAISE; } static VALUE template_execute_do(VALUE argv) @@ -203,31 +186,16 @@ static int template_execute_wrap(msgpack_unpack_t* mp, (VALUE)from, }; -#ifdef HAVE_RUBY_ENCODING_H - int enc_orig = rb_enc_get_index(str); - rb_enc_set_index(str, s_ascii_8bit); -#endif - // FIXME execute実行中はmp->topが更新されないのでGC markが機能しない rb_gc_disable(); mp->user.source = str; -#ifdef HAVE_RUBY_ENCODING_H - VALUE resc[2] = {str, enc_orig}; - int ret = (int)rb_rescue(template_execute_do, (VALUE)args, - template_execute_rescue_enc, (VALUE)resc); -#else int ret = (int)rb_rescue(template_execute_do, (VALUE)args, template_execute_rescue, Qnil); -#endif rb_gc_enable(); -#ifdef HAVE_RUBY_ENCODING_H - rb_enc_set_index(str, enc_orig); -#endif - return ret; } @@ -287,6 +255,7 @@ static void MessagePack_Unpacker_mark(msgpack_unpack_t *mp) unsigned int i; rb_gc_mark(mp->user.stream); rb_gc_mark(mp->user.streambuf); + rb_gc_mark_maybe(template_data(mp)); for(i=0; i < mp->top; ++i) { rb_gc_mark(mp->stack[i].obj); rb_gc_mark_maybe(mp->stack[i].map_key); @@ -297,6 +266,17 @@ static VALUE MessagePack_Unpacker_alloc(VALUE klass) { VALUE obj; msgpack_unpack_t* mp = ALLOC_N(msgpack_unpack_t, 1); + + // rb_gc_mark (not _maybe) is used for following member objects. + mp->user.stream = Qnil; + mp->user.streambuf = Qnil; + + mp->user.finished = 0; + mp->user.offset = 0; + mp->user.buffer.size = 0; + mp->user.buffer.free = 0; + mp->user.buffer.ptr = NULL; + obj = Data_Wrap_Struct(klass, MessagePack_Unpacker_mark, MessagePack_Unpacker_free, mp); return obj; @@ -343,14 +323,10 @@ static VALUE MessagePack_Unpacker_initialize(int argc, VALUE *argv, VALUE self) UNPACKER(self, mp); template_init(mp); - mp->user.finished = 0; - mp->user.offset = 0; - mp->user.buffer.size = 0; - mp->user.buffer.free = 0; - mp->user.buffer.ptr = NULL; mp->user.stream = stream; mp->user.streambuf = rb_str_buf_new(MSGPACK_UNPACKER_BUFFER_RESERVE_SIZE); mp->user.stream_append_method = append_method_of(stream); + return self; } @@ -638,7 +614,7 @@ static VALUE MessagePack_Unpacker_execute_impl(VALUE self, VALUE data, * Document-method: MessagePack::Unpacker#execute_limit * * call-seq: - * unpacker.unpack_limit(data, offset, limit) -> next offset + * unpacker.execute_limit(data, offset, limit) -> next offset * * Deserializes one object over the specified buffer from _offset_ bytes upto _limit_ bytes. * @@ -660,7 +636,7 @@ static VALUE MessagePack_Unpacker_execute_limit(VALUE self, VALUE data, * Document-method: MessagePack::Unpacker#execute * * call-seq: - * unpacker.unpack(data, offset) -> next offset + * unpacker.execute(data, offset) -> next offset * * Deserializes one object over the specified buffer from _offset_ bytes. * @@ -738,10 +714,6 @@ void Init_msgpack_unpack(VALUE mMessagePack) s_sysread = rb_intern("sysread"); s_readpartial = rb_intern("readpartial"); -#ifdef HAVE_RUBY_ENCODING_H - s_ascii_8bit = rb_enc_find_index("ASCII-8BIT"); -#endif - eUnpackError = rb_define_class_under(mMessagePack, "UnpackError", rb_eStandardError); cUnpacker = rb_define_class_under(mMessagePack, "Unpacker", rb_cObject); rb_define_alloc_func(cUnpacker, MessagePack_Unpacker_alloc); diff --git a/ruby/version.rb b/ruby/version.rb new file mode 100644 index 00000000..b1566203 --- /dev/null +++ b/ruby/version.rb @@ -0,0 +1,3 @@ +module MessagePack + VERSION = "0.4.3" +end