diff --git a/.gitignore b/.gitignore index ffc26910..3328fdce 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ Makefile.in /libtool /msgpack.pc /src/msgpack/version.h +/src/msgpack/version.hpp /stamp-h1 Makefile .deps diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ee162dd..79c86ba2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,18 @@ SET (exec_prefix "\${prefix}") SET (libdir "\${exec_prefix}/lib") SET (includedir "\${prefix}/include") +OPTION (MSGPACK_CXX11 "Using c++11 compiler" OFF) + +IF (MSGPACK_CXX11) + IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + SET (CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") + ELSEIF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + SET (CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") + ELSEIF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + MESSAGE ( FATAL_ERROR "MSVC doesn't support C++11 yet.") + ENDIF () +ENDIF () + FIND_PACKAGE (GTest) FIND_PACKAGE (ZLIB) FIND_PACKAGE (Threads) @@ -17,6 +29,26 @@ IF (GTEST_FOUND AND ZLIB_FOUND AND THREADS_FOUND) OPTION (MSGPACK_BUILD_TESTS "Build msgpack tests." ON) ENDIF () + +# Files to copy + +MACRO (COPY_FILE SOURCE DESTINATION) + # COPY_FILE (readme.txt dir1/dir2/) would add new target + # that will copy readme.txt to dir1/dir2 where dir1 will be located + # in current binary directory. + ADD_CUSTOM_COMMAND ( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${DESTINATION}/${SOURCE} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${DESTINATION} + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE} ${CMAKE_CURRENT_BINARY_DIR}/${DESTINATION}/${SOURCE} + ) +ENDMACRO () + +IF (MSGPACK_CXX11) + COPY_FILE (cpp11/zone.hpp src/msgpack/zone.hpp) + COPY_FILE (cpp11/define.hpp src/msgpack/type/define.hpp) + COPY_FILE (cpp11/tuple.hpp src/msgpack/type/tuple.hpp) +ENDIF () + OPTION (MSGPACK_ENABLE_CXX "Enable C++ interface." ON) INCLUDE (CheckCXXSourceCompiles) @@ -109,6 +141,10 @@ CONFIGURE_FILE ( src/msgpack/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/msgpack/version.h ) +CONFIGURE_FILE ( + src/msgpack/version.hpp.in + ${CMAKE_CURRENT_BINARY_DIR}/src/msgpack/version.hpp +) CONFIGURE_FILE ( msgpack.pc.in diff --git a/README.md b/README.md index 8799052a..a9621f1b 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,16 @@ $ make $ sudo make install ``` +If you want to setup C++11 version of msgpack, execute the following command: + +``` +$ git clone https://github.com/msgpack/msgpack-c.git +$ cd msgpack-c +$ ./bootstrap +$ ./configure CXXFLAGS="-std=c++11" +``` +You need the compiler that fully supports C++11. + ##### Using cmake You will need gcc (4.1.0 or higher), cmake. @@ -48,6 +58,16 @@ $ cmake . $ make ``` +If you want to setup C++11 version of msgpack, execute the following command: + +``` +$ git clone https://github.com/msgpack/msgpack-c.git +$ cd msgpack-c +$ cmake -DMSGPACK_CXX11=ON . +``` + +You need the compiler that fully supports C++11. + #### Install from package ##### UNIX-like platform with ./configure diff --git a/configure.in b/configure.in index b22714a8..bb61f60d 100644 --- a/configure.in +++ b/configure.in @@ -3,6 +3,27 @@ AC_CONFIG_AUX_DIR(ac) AM_INIT_AUTOMAKE(msgpack, 0.5.9) AC_CONFIG_HEADER(config.h) + +AC_LANG_PUSH([C++]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ +#if __cplusplus < 201103 +#error +#endif +])], +[ +./preprocess_cpp11 +if test $? != 0; then + exit 1 +fi +], +[ +./preprocess +if test $? != 0; then + exit 1 +fi +]) +AC_LANG_POP() + AC_SUBST(CFLAGS) CFLAGS="-O3 -Wall $CFLAGS" @@ -96,5 +117,6 @@ AC_OUTPUT([Makefile msgpack.pc src/Makefile src/msgpack/version.h + src/msgpack/version.hpp test/Makefile]) diff --git a/cpp11/define.hpp b/cpp11/define.hpp new file mode 100644 index 00000000..9754af27 --- /dev/null +++ b/cpp11/define.hpp @@ -0,0 +1,170 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2013 FURUHASHI Sadayuki and KONDO Takatoshi +// +// 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_DEFINE_HPP +#define MSGPACK_TYPE_DEFINE_HPP + +#define MSGPACK_DEFINE(...) \ + template \ + void msgpack_pack(Packer& pk) const \ + { \ + msgpack::type::make_define(__VA_ARGS__).msgpack_pack(pk); \ + } \ + void msgpack_unpack(msgpack::object o) \ + { \ + msgpack::type::make_define(__VA_ARGS__).msgpack_unpack(o); \ + }\ + template \ + void msgpack_object(MSGPACK_OBJECT* o, msgpack::zone* z) const \ + { \ + msgpack::type::make_define(__VA_ARGS__).msgpack_object(o, z); \ + } + +// MSGPACK_ADD_ENUM must be used in the global namespace. +#define MSGPACK_ADD_ENUM(enum) \ + namespace msgpack { \ + template <> \ + inline enum& operator>> (object o, enum& v) \ + { \ + int tmp; \ + o >> tmp; \ + v = static_cast(tmp); \ + return v; \ + } \ + template <> \ + void operator<< (object::with_zone& o, const enum& v) \ + { \ + int tmp = static_cast(v); \ + o << tmp; \ + } \ + namespace detail { \ + template \ + struct packer_serializer { \ + static packer& pack(packer& o, const enum& v) { \ + return o << static_cast(v); \ + } \ + }; \ + } \ + } + +namespace msgpack { +namespace type { + +template +struct define_imp { + template + static void pack(Packer& pk, Tuple const& t) { + define_imp::pack(pk, t); + pk.pack(std::get(t)); + } + static void unpack(msgpack::object o, Tuple& t) { + define_imp::unpack(o, t); + const size_t size = o.via.array.size; + if(size <= N-1) { return; } + o.via.array.ptr[N-1].convert(&std::get(t)); + } + static void object(msgpack::object* o, msgpack::zone* z, Tuple const& t) { + define_imp::object(o, z, t); + o->via.array.ptr[N-1] = msgpack::object(std::get(t), z); + } +}; + +template +struct define_imp { + template + static void pack(Packer& pk, Tuple const& t) { + pk.pack(std::get<0>(t)); + } + static void unpack(msgpack::object o, Tuple& t) { + const size_t size = o.via.array.size; + if(size <= 0) { return; } + o.via.array.ptr[0].convert(&std::get<0>(t)); + } + static void object(msgpack::object* o, msgpack::zone* z, Tuple const& t) { + o->via.array.ptr[0] = msgpack::object(std::get<0>(t), z); + } +}; + +template +struct define { + typedef define value_type; + typedef tuple tuple_type; + define(Args&... args) : + a(args...) {} + template + void msgpack_pack(Packer& pk) const + { + pk.pack_array(sizeof...(Args)); + + define_imp, sizeof...(Args)>::pack(pk, a); + } + void msgpack_unpack(msgpack::object o) + { + if(o.type != type::ARRAY) { throw type_error(); } + + define_imp, sizeof...(Args)>::unpack(o, a); + } + void msgpack_object(msgpack::object* o, msgpack::zone* z) const + { + o->type = type::ARRAY; + o->via.array.ptr = (object*)z->malloc(sizeof(object)*sizeof...(Args)); + o->via.array.size = sizeof...(Args); + + define_imp, sizeof...(Args)>::object(o, z, a); + } + + tuple a; +}; + +template <> +struct define<> { + typedef define<> value_type; + typedef tuple<> tuple_type; + template + void msgpack_pack(Packer& pk) const + { + pk.pack_array(0); + } + void msgpack_unpack(msgpack::object o) + { + if(o.type != type::ARRAY) { throw type_error(); } + } + void msgpack_object(msgpack::object* o, msgpack::zone* z) const + { + o->type = type::ARRAY; + o->via.array.ptr = NULL; + o->via.array.size = 0; + } +}; + +inline define<> make_define() +{ + return define<>(); +} + +template +define make_define(Args&... args) +{ + return define(args...); +} + +} // namespace type +} // namespace msgpack + + +#endif /* msgpack/type/define.hpp */ + diff --git a/cpp11/tuple.hpp b/cpp11/tuple.hpp new file mode 100644 index 00000000..0fb015a4 --- /dev/null +++ b/cpp11/tuple.hpp @@ -0,0 +1,125 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2013 FURUHASHI Sadayuki and KONDO Takatoshi +// +// 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_TUPLE_HPP +#define MSGPACK_TYPE_TUPLE_HPP + +#include "msgpack/object.hpp" +#include + +namespace msgpack { +namespace type { + +template +using tuple = std::tuple; + +} // type + +// --- Pack ( from tuple to packer stream --- +template +struct Packer { + static void pack( + packer& o, + const Tuple& v) { + Packer::pack(o, v); + o.pack(std::get(v)); + } +}; + +template +struct Packer { + static void pack ( + packer& o, + const Tuple& v) { + o.pack(std::get<0>(v)); + } +}; + +template +const packer& operator<< ( + packer& o, + const type::tuple& v) { + o.pack_array(sizeof...(Args)); + Packer::pack(o, v); + return o; +} + +// --- Convert from tuple to object --- + +template +struct Converter { + static void convert( + object o, + Tuple& v) { + Converter::convert(o, v); + o.via.array.ptr[N-1].convert(v))>::type>(&std::get(v)); + } +}; + +template +struct Converter { + static void convert ( + object o, + Tuple& v) { + o.via.array.ptr[0].convert(v))>::type>(&std::get<0>(v)); + } +}; + +template +type::tuple& operator>> ( + object o, + type::tuple& v) { + if(o.type != type::ARRAY) { throw type_error(); } + if(o.via.array.size < sizeof...(Args)) { throw type_error(); } + Converter::convert(o, v); + return v; +} + +// --- Convert from tuple to object with zone --- +template +struct TupleToObjectWithZone { + static void convert( + object::with_zone& o, + const Tuple& v) { + TupleToObjectWithZone::convert(o, v); + o.via.array.ptr[N-1] = object(std::get(v), o.zone); + } +}; + +template +struct TupleToObjectWithZone { + static void convert ( + object::with_zone& o, + const Tuple& v) { + o.via.array.ptr[0] = object(std::get<0>(v), o.zone); + } +}; + +template +inline void operator<< ( + object::with_zone& o, + type::tuple& v) { + o.type = type::ARRAY; + o.via.array.ptr = (object*)o.zone->malloc(sizeof(object)*sizeof...(Args)); + o.via.array.size = sizeof...(Args); + TupleToObjectWithZone::convert(o, v); +} + +} // msgpack + +#endif /* msgpack/type/tuple.hpp */ + diff --git a/cpp11/zone.hpp b/cpp11/zone.hpp new file mode 100644 index 00000000..f15733ab --- /dev/null +++ b/cpp11/zone.hpp @@ -0,0 +1,304 @@ +// +// MessagePack for C++ memory pool +// +// Copyright (C) 2008-2013 FURUHASHI Sadayuki and KONDO Takatoshi +// +// 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_ZONE_HPP +#define MSGPACK_ZONE_HPP + +#include +#include +#include + +#ifndef MSGPACK_ZONE_CHUNK_SIZE +#define MSGPACK_ZONE_CHUNK_SIZE 8192 +#endif + +#ifndef MSGPACK_ZONE_ALIGN +#define MSGPACK_ZONE_ALIGN sizeof(int) +#endif + +namespace msgpack { + +class zone { +private: + struct finalizer { + finalizer(void (*func)(void*), void* data):func_(func), data_(data) {} + void operator()() { func_(data_); } + void (*func_)(void*); + void* data_; + }; + struct finalizer_array { + finalizer_array():tail_(nullptr), end_(nullptr), array_(nullptr) {} + void call() { + finalizer* fin = tail_; + for(; fin != array_; --fin) (*(fin-1))(); + } + ~finalizer_array() { + call(); + ::free(array_); + } + void clear() { + call(); + tail_ = array_; + } + void push(void (*func)(void* data), void* data) + { + finalizer* fin = tail_; + + if(fin == end_) { + push_expand(func, data); + return; + } + + fin->func_ = func; + fin->data_ = data; + + ++tail_; + } + void push_expand(void (*func)(void*), void* data) { + const size_t nused = end_ - array_; + size_t nnext; + if(nused == 0) { + nnext = (sizeof(finalizer) < 72/2) ? + 72 / sizeof(finalizer) : 8; + } else { + nnext = nused * 2; + } + finalizer* tmp = + (finalizer*)::realloc(array_, sizeof(finalizer) * nnext); + if(!tmp) { + throw std::bad_alloc(); + } + array_ = tmp; + end_ = tmp + nnext; + tail_ = tmp + nused; + new (tail_) finalizer(func, data); + + ++tail_; + } + finalizer* tail_; + finalizer* end_; + finalizer* array_; + }; + struct chunk { + chunk* next_; + }; + struct chunk_list { + chunk_list(size_t chunk_size) + { + chunk* c = (chunk*)::malloc(sizeof(chunk) + chunk_size); + if(!c) { + throw std::bad_alloc(); + } + + head_ = c; + free_ = chunk_size; + ptr_ = ((char*)c) + sizeof(chunk); + c->next_ = nullptr; + } + ~chunk_list() + { + chunk* c = head_; + while(true) { + chunk* n = c->next_; + ::free(c); + if(n) { + c = n; + } else { + break; + } + } + } + void clear(size_t chunk_size) + { + chunk* c = head_; + while(true) { + chunk* n = c->next_; + if(n) { + ::free(c); + c = n; + } else { + break; + } + } + head_->next_ = nullptr; + free_ = chunk_size; + ptr_ = ((char*)head_) + sizeof(chunk); + } + size_t free_; + char* ptr_; + chunk* head_; + }; + size_t chunk_size_; + chunk_list chunk_list_; + finalizer_array finalizer_array_; + +public: + zone(size_t chunk_size = MSGPACK_ZONE_CHUNK_SIZE); + +public: + static zone* create(size_t chunk_size); + static void destroy(zone* zone); + void* malloc(size_t size); + void* malloc_no_align(size_t size); + + void push_finalizer(void (*func)(void*), void* data); + + template + void push_finalizer(std::unique_ptr obj); + + void clear(); + + void swap(zone& o); + + + template + T* allocate(Args... args); + +private: + void undo_malloc(size_t size); + + template + static void object_destructor(void* obj); + + void* malloc_expand(size_t size); +}; + +inline zone* zone::create(size_t chunk_size) +{ + zone* z = (zone*)::malloc(sizeof(zone) + chunk_size); + if (!z) { + return nullptr; + } + try { + new (z) zone(chunk_size); + } + catch (...) { + ::free(z); + return nullptr; + } + return z; +} + +inline void zone::destroy(zone* z) +{ + z->~zone(); + ::free(z); +} + +inline zone::zone(size_t chunk_size):chunk_size_(chunk_size), chunk_list_(chunk_size_) +{ +} + +inline void* zone::malloc(size_t size) +{ + return malloc_no_align( + ((size)+((MSGPACK_ZONE_ALIGN)-1)) & ~((MSGPACK_ZONE_ALIGN)-1)); +} + +inline void* zone::malloc_no_align(size_t size) +{ + if(chunk_list_.free_ < size) { + return malloc_expand(size); + } + + char* ptr = chunk_list_.ptr_; + chunk_list_.free_ -= size; + chunk_list_.ptr_ += size; + + return ptr; +} + +inline void* zone::malloc_expand(size_t size) +{ + chunk_list* const cl = &chunk_list_; + + size_t sz = chunk_size_; + + while(sz < size) { + sz *= 2; + } + + chunk* c = (chunk*)::malloc(sizeof(chunk) + sz); + + char* ptr = ((char*)c) + sizeof(chunk); + + c->next_ = cl->head_; + cl->head_ = c; + cl->free_ = sz - size; + cl->ptr_ = ptr + size; + + return ptr; +} + +inline void zone::push_finalizer(void (*func)(void*), void* data) +{ + finalizer_array_.push(func, data); +} + +template +inline void zone::push_finalizer(std::unique_ptr obj) +{ + finalizer_array_.push(&zone::object_destructor, obj.get()); + obj.release(); +} + +inline void zone::clear() +{ + finalizer_array_.clear(); + chunk_list_.clear(chunk_size_); +} + +inline void zone::swap(zone& o) +{ + std::swap(*this, o); +} + +template +void zone::object_destructor(void* obj) +{ + reinterpret_cast(obj)->~T(); +} + +inline void zone::undo_malloc(size_t size) +{ + chunk_list_.ptr_ -= size; + chunk_list_.free_ += size; +} + + +template +T* zone::allocate(Args... args) +{ + void* x = malloc(sizeof(T)); + try { + finalizer_array_.push(&zone::object_destructor, x); + } catch (...) { + undo_malloc(sizeof(T)); + throw; + } + try { + return new (x) T(args...); + } catch (...) { + --finalizer_array_.tail_; + undo_malloc(sizeof(T)); + throw; + } +} + +} // namespace msgpack + +#endif /* msgpack/zone.hpp */ diff --git a/preprocess_cpp11 b/preprocess_cpp11 new file mode 100755 index 00000000..d7b01d33 --- /dev/null +++ b/preprocess_cpp11 @@ -0,0 +1,17 @@ +#!/bin/sh + +if [ "$1" = "clean" ];then + rm -f src/msgpack/type/tuple.hpp + rm -f src/msgpack/type/define.hpp + rm -f src/msgpack/zone.hpp +else + cp -f cpp11/zone.hpp src/msgpack/ + cp -f cpp11/tuple.hpp src/msgpack/type/ + cp -f cpp11/define.hpp src/msgpack/type/ +fi +cp -f cases.mpac test/ +cp -f 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/src/Makefile.am b/src/Makefile.am index 37993f26..93207b8c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -62,6 +62,7 @@ nobase_include_HEADERS += \ msgpack/fbuffer.hpp \ msgpack/pack.hpp \ msgpack/unpack.hpp \ + msgpack/version.hpp \ msgpack/object.hpp \ msgpack/zone.hpp \ msgpack/type.hpp \ @@ -86,6 +87,7 @@ endif EXTRA_DIST = \ msgpack/version.h.in \ + msgpack/version.hpp.in \ msgpack/zone.hpp.erb \ msgpack/type/define.hpp.erb \ msgpack/type/tuple.hpp.erb diff --git a/src/msgpack.hpp b/src/msgpack.hpp index e14680dd..62d4b3d3 100644 --- a/src/msgpack.hpp +++ b/src/msgpack.hpp @@ -21,4 +21,4 @@ #include "msgpack/unpack.hpp" #include "msgpack/sbuffer.hpp" #include "msgpack/vrefbuffer.hpp" -#include "msgpack.h" +#include "msgpack/version.hpp" diff --git a/src/msgpack/sbuffer.hpp b/src/msgpack/sbuffer.hpp index fce56ad5..aa197038 100644 --- a/src/msgpack/sbuffer.hpp +++ b/src/msgpack/sbuffer.hpp @@ -1,7 +1,7 @@ // // MessagePack for C++ simple buffer implementation // -// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// Copyright (C) 2008-2013 FURUHASHI Sadayuki and KONDO Takatoshi // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,94 +15,99 @@ // See the License for the specific language governing permissions and // limitations under the License. // -#ifndef MSGPACK_SBUFFER_HPP__ -#define MSGPACK_SBUFFER_HPP__ +#ifndef MSGPACK_SBUFFER_HPP +#define MSGPACK_SBUFFER_HPP -#include "sbuffer.h" #include +#ifndef MSGPACK_SBUFFER_INIT_SIZE +#define MSGPACK_SBUFFER_INIT_SIZE 8192 +#endif + namespace msgpack { -class sbuffer : public msgpack_sbuffer { +class sbuffer { public: - sbuffer(size_t initsz = MSGPACK_SBUFFER_INIT_SIZE) + sbuffer(size_t initsz = MSGPACK_SBUFFER_INIT_SIZE):size_(0), alloc_(initsz) { if(initsz == 0) { - base::data = NULL; + data_ = nullptr; } else { - base::data = (char*)::malloc(initsz); - if(!base::data) { + data_ = (char*)::malloc(initsz); + if(!data_) { throw std::bad_alloc(); } } - - base::size = 0; - base::alloc = initsz; } ~sbuffer() { - ::free(base::data); + ::free(data_); } public: void write(const char* buf, size_t len) { - if(base::alloc - base::size < len) { + if(alloc_ - size_ < len) { expand_buffer(len); } - memcpy(base::data + base::size, buf, len); - base::size += len; + ::memcpy(data_ + size_, buf, len); + size_ += len; } char* data() { - return base::data; + return data_; } const char* data() const { - return base::data; + return data_; } size_t size() const { - return base::size; + return size_; } char* release() { - return msgpack_sbuffer_release(this); + char* tmp = data_; + size_ = 0; + data_ = nullptr; + alloc_ = 0; + return tmp; } void clear() { - msgpack_sbuffer_clear(this); + size_ = 0; } private: void expand_buffer(size_t len) { - size_t nsize = (base::alloc > 0) ? - base::alloc * 2 : MSGPACK_SBUFFER_INIT_SIZE; + size_t nsize = (alloc_ > 0) ? + alloc_ * 2 : MSGPACK_SBUFFER_INIT_SIZE; - while(nsize < base::size + len) { nsize *= 2; } + while(nsize < size_ + len) { nsize *= 2; } - void* tmp = realloc(base::data, nsize); + void* tmp = ::realloc(data_, nsize); if(!tmp) { throw std::bad_alloc(); } - base::data = (char*)tmp; - base::alloc = nsize; + data_ = (char*)tmp; + alloc_ = nsize; } -private: - typedef msgpack_sbuffer base; - private: sbuffer(const sbuffer&); +private: + size_t size_; + char* data_; + size_t alloc_; }; diff --git a/src/msgpack/unpack.hpp b/src/msgpack/unpack.hpp index baca1176..2b7c0b17 100644 --- a/src/msgpack/unpack.hpp +++ b/src/msgpack/unpack.hpp @@ -1,7 +1,7 @@ // // MessagePack for C++ deserializing routine // -// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// Copyright (C) 2008-2013 FURUHASHI Sadayuki and KONDO Takatoshi // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,22 +15,490 @@ // See the License for the specific language governing permissions and // limitations under the License. // -#ifndef MSGPACK_UNPACK_HPP__ -#define MSGPACK_UNPACK_HPP__ +#ifndef MSGPACK_UNPACK_HPP +#define MSGPACK_UNPACK_HPP -#include "unpack.h" #include "object.hpp" #include "zone.hpp" +#include "unpack_define.h" + #include #include + +#define COUNTER_SIZE (sizeof(_msgpack_atomic_counter_t)) + +#ifndef MSGPACK_UNPACKER_INIT_BUFFER_SIZE +#define MSGPACK_UNPACKER_INIT_BUFFER_SIZE (64*1024) +#endif + +#ifndef MSGPACK_UNPACKER_RESERVE_SIZE +#define MSGPACK_UNPACKER_RESERVE_SIZE (32*1024) +#endif + + // backward compatibility #ifndef MSGPACK_UNPACKER_DEFAULT_INITIAL_BUFFER_SIZE #define MSGPACK_UNPACKER_DEFAULT_INITIAL_BUFFER_SIZE MSGPACK_UNPACKER_INIT_BUFFER_SIZE #endif + namespace msgpack { +namespace detail { + +struct unpack_user { + zone* z; + bool referenced; +}; + + +static inline ::msgpack::object template_callback_root(unpack_user* u) +{ ::msgpack::object o = {}; return o; } + +static inline int template_callback_uint8(unpack_user* u, uint8_t d, ::msgpack::object* o) +{ o->type = ::msgpack::type::POSITIVE_INTEGER; o->via.u64 = d; return 0; } + +static inline int template_callback_uint16(unpack_user* u, uint16_t d, ::msgpack::object* o) +{ o->type = ::msgpack::type::POSITIVE_INTEGER; o->via.u64 = d; return 0; } + +static inline int template_callback_uint32(unpack_user* u, uint32_t d, ::msgpack::object* o) +{ o->type = ::msgpack::type::POSITIVE_INTEGER; o->via.u64 = d; return 0; } + +static inline int template_callback_uint64(unpack_user* u, uint64_t d, ::msgpack::object* o) +{ o->type = ::msgpack::type::POSITIVE_INTEGER; o->via.u64 = d; return 0; } + +static inline int template_callback_int8(unpack_user* u, int8_t d, ::msgpack::object* o) +{ if(d >= 0) { o->type = ::msgpack::type::POSITIVE_INTEGER; o->via.u64 = d; return 0; } + else { o->type = ::msgpack::type::NEGATIVE_INTEGER; o->via.i64 = d; return 0; } } + +static inline int template_callback_int16(unpack_user* u, int16_t d, ::msgpack::object* o) +{ if(d >= 0) { o->type = ::msgpack::type::POSITIVE_INTEGER; o->via.u64 = d; return 0; } + else { o->type = ::msgpack::type::NEGATIVE_INTEGER; o->via.i64 = d; return 0; } } + +static inline int template_callback_int32(unpack_user* u, int32_t d, ::msgpack::object* o) +{ if(d >= 0) { o->type = ::msgpack::type::POSITIVE_INTEGER; o->via.u64 = d; return 0; } + else { o->type = ::msgpack::type::NEGATIVE_INTEGER; o->via.i64 = d; return 0; } } + +static inline int template_callback_int64(unpack_user* u, int64_t d, ::msgpack::object* o) +{ if(d >= 0) { o->type = ::msgpack::type::POSITIVE_INTEGER; o->via.u64 = d; return 0; } + else { o->type = ::msgpack::type::NEGATIVE_INTEGER; o->via.i64 = d; return 0; } } + +static inline int template_callback_float(unpack_user* u, float d, ::msgpack::object* o) +{ o->type = ::msgpack::type::DOUBLE; o->via.dec = d; return 0; } + +static inline int template_callback_double(unpack_user* u, double d, ::msgpack::object* o) +{ o->type = ::msgpack::type::DOUBLE; o->via.dec = d; return 0; } + +static inline int template_callback_nil(unpack_user* u, ::msgpack::object* o) +{ o->type = ::msgpack::type::NIL; return 0; } + +static inline int template_callback_true(unpack_user* u, ::msgpack::object* o) +{ o->type = ::msgpack::type::BOOLEAN; o->via.boolean = true; return 0; } + +static inline int template_callback_false(unpack_user* u, ::msgpack::object* o) +{ o->type = ::msgpack::type::BOOLEAN; o->via.boolean = false; return 0; } + +static inline int template_callback_array(unpack_user* u, unsigned int n, ::msgpack::object* o) +{ + o->type = ::msgpack::type::ARRAY; + o->via.array.size = 0; + o->via.array.ptr = (::msgpack::object*)u->z->malloc(n*sizeof(::msgpack::object)); + if(o->via.array.ptr == NULL) { return -1; } + return 0; +} + +static inline int template_callback_array_item(unpack_user* u, ::msgpack::object* c, ::msgpack::object o) +{ c->via.array.ptr[c->via.array.size++] = o; return 0; } + +static inline int template_callback_map(unpack_user* u, unsigned int n, ::msgpack::object* o) +{ + o->type = ::msgpack::type::MAP; + o->via.map.size = 0; + o->via.map.ptr = (::msgpack::object_kv*)u->z->malloc(n*sizeof(::msgpack::object_kv)); + if(o->via.map.ptr == NULL) { return -1; } + return 0; +} + +static inline int template_callback_map_item(unpack_user* u, ::msgpack::object* c, ::msgpack::object k, ::msgpack::object v) +{ + c->via.map.ptr[c->via.map.size].key = k; + c->via.map.ptr[c->via.map.size].val = v; + ++c->via.map.size; + return 0; +} + +static inline int template_callback_raw(unpack_user* u, const char* b, const char* p, unsigned int l, ::msgpack::object* o) +{ + o->type = ::msgpack::type::RAW; + o->via.raw.ptr = p; + o->via.raw.size = l; + u->referenced = true; + return 0; +} + + +struct template_unpack_stack { + ::msgpack::object obj; + size_t count; + unsigned int ct; + ::msgpack::object map_key; +}; + +struct template_context { + unpack_user user; + unsigned int cs; + unsigned int trail; + unsigned int top; + template_unpack_stack stack[MSGPACK_EMBED_STACK_SIZE]; +}; + + +inline void init_count(void* buffer) +{ + *(volatile _msgpack_atomic_counter_t*)buffer = 1; +} + +inline void decl_count(void* buffer) +{ + // atomic if(--*(_msgpack_atomic_counter_t*)buffer == 0) { free(buffer); } + if(_msgpack_sync_decr_and_fetch((volatile _msgpack_atomic_counter_t*)buffer) == 0) { + free(buffer); + } +} + +inline void incr_count(void* buffer) +{ + // atomic ++*(_msgpack_atomic_counter_t*)buffer; + _msgpack_sync_incr_and_fetch((volatile _msgpack_atomic_counter_t*)buffer); +} + +inline _msgpack_atomic_counter_t get_count(void* buffer) +{ + return *(volatile _msgpack_atomic_counter_t*)buffer; +} + +inline void template_init(template_context& ctx) +{ + ctx.cs = CS_HEADER; + ctx.trail = 0; + ctx.top = 0; + ctx.stack[0].obj = template_callback_root(&ctx.user); +} + +::msgpack::object template_data(template_context const& ctx) +{ + return ctx.stack[0].obj; +} + +template +inline unsigned int next_cs(T p) +{ + return (unsigned int)*p & 0x1f; +} + +int template_execute(template_context& ctx, const char* data, size_t len, size_t* off) +{ + assert(len >= *off); + + const unsigned char* p = (unsigned char*)data + *off; + const unsigned char* const pe = (unsigned char*)data + len; + const void* n = nullptr; + + unsigned int trail = ctx.trail; + unsigned int cs = ctx.cs; + unsigned int top = ctx.top; + detail::template_unpack_stack* stack = ctx.stack; + /* + unsigned int stack_size = ctx.stack_size; + */ + unpack_user* user = &ctx.user; + + ::msgpack::object obj; + detail::template_unpack_stack* c = nullptr; + + int ret; + + + if(p == pe) { goto _out; } + do { + switch(cs) { + case CS_HEADER: + if (0) { + } else if(0x00 <= *p && *p <= 0x7f) { // Positive Fixnum + if(template_callback_uint8(user, *(uint8_t*)p, &obj) < 0) { goto _failed; } + goto _push; + } else if(0xe0 <= *p && *p <= 0xff) { // Negative Fixnum + if(template_callback_int8(user, *(int8_t*)p, &obj) < 0) { goto _failed; } + goto _push; + } else if(0xc0 <= *p && *p <= 0xdf) { // Variable + switch(*p) { + case 0xc0: // nil + if(template_callback_nil(user, &obj) < 0) { goto _failed; } + goto _push; + //case 0xc1: // string + // again_terminal_trail(next_cs(p), p+1); + case 0xc2: // false + if(template_callback_false(user, &obj) < 0) { goto _failed; } + goto _push; + case 0xc3: // true + if(template_callback_true(user, &obj) < 0) { goto _failed; } + goto _push; + case 0xc4: // bin 8 + case 0xc5: // bin 16 + case 0xc6: // bin 32 + trail = 1 << (((unsigned int)*p) & 0x03); + cs = next_cs(p); + goto _fixed_trail_again; + + //case 0xc7: + //case 0xc8: + //case 0xc9: + case 0xca: // float + case 0xcb: // double + case 0xcc: // unsigned int 8 + case 0xcd: // unsigned int 16 + case 0xce: // unsigned int 32 + case 0xcf: // unsigned int 64 + case 0xd0: // signed int 8 + case 0xd1: // signed int 16 + case 0xd2: // signed int 32 + case 0xd3: // signed int 64 + trail = 1 << (((unsigned int)*p) & 0x03); + cs = next_cs(p); + goto _fixed_trail_again; + //case 0xd4: + //case 0xd5: + //case 0xd6: // big integer 16 + //case 0xd7: // big integer 32 + //case 0xd8: // big float 16 + case 0xd9: // raw 8 (str 8) + case 0xda: // raw 16 (str 16) + case 0xdb: // raw 32 (str 32) + trail = 1 << ((((unsigned int)*p) & 0x03) - 1); + cs = next_cs(p); + goto _fixed_trail_again; + case 0xdc: // array 16 + case 0xdd: // array 32 + case 0xde: // map 16 + case 0xdf: // map 32 + trail = 2 << (((unsigned int)*p) & 0x01); + cs = next_cs(p); + goto _fixed_trail_again; + default: + goto _failed; + } + } else if(0xa0 <= *p && *p <= 0xbf) { // FixRaw + trail = (unsigned int)*p & 0x1f; + if(trail == 0) { goto _raw_zero; } + cs = ACS_RAW_VALUE; + goto _fixed_trail_again; + + } else if(0x90 <= *p && *p <= 0x9f) { // FixArray + if(top >= MSGPACK_EMBED_STACK_SIZE) { goto _failed; } /* FIXME */ + if(template_callback_array(user, ((unsigned int)*p) & 0x0f, &stack[top].obj) < 0) { goto _failed; } + if((((unsigned int)*p) & 0x0f) == 0) { obj = stack[top].obj; goto _push; } + stack[top].ct = CT_ARRAY_ITEM; + stack[top].count = ((unsigned int)*p) & 0x0f; + ++top; + goto _header_again; + + } else if(0x80 <= *p && *p <= 0x8f) { // FixMap + if(top >= MSGPACK_EMBED_STACK_SIZE) { goto _failed; } /* FIXME */ + if(template_callback_map(user, ((unsigned int)*p) & 0x0f, &stack[top].obj) < 0) { goto _failed; } + if((((unsigned int)*p) & 0x0f) == 0) { obj = stack[top].obj; goto _push; } + stack[top].ct = CT_MAP_KEY; + stack[top].count = ((unsigned int)*p) & 0x0f; + ++top; + goto _header_again; + + } else { + goto _failed; + } + // end CS_HEADER + + + _fixed_trail_again: + ++p; + + default: + if((size_t)(pe - p) < trail) { goto _out; } + n = p; p += trail - 1; + switch(cs) { + //case CS_ + //case CS_ + case CS_FLOAT: { + union { uint32_t i; float f; } mem; + mem.i = _msgpack_load32(uint32_t,n); + if(template_callback_float(user, mem.f, &obj) < 0) { goto _failed; } + goto _push; } + case CS_DOUBLE: { + union { uint64_t i; double f; } mem; + mem.i = _msgpack_load64(uint64_t,n); +#if defined(__arm__) && !(__ARM_EABI__) // arm-oabi + // https://github.com/msgpack/msgpack-perl/pull/1 + mem.i = (mem.i & 0xFFFFFFFFUL) << 32UL | (mem.i >> 32UL); +#endif + if(template_callback_double(user, mem.f, &obj) < 0) { goto _failed; } + goto _push; } + case CS_UINT_8: + if(template_callback_uint8(user, *(uint8_t*)n, &obj) < 0) { goto _failed; } + goto _push; + case CS_UINT_16: + if(template_callback_uint16(user, _msgpack_load16(uint16_t,n), &obj) < 0) { goto _failed; } + goto _push; + case CS_UINT_32: + if(template_callback_uint32(user, _msgpack_load32(uint32_t,n), &obj) < 0) { goto _failed; } + goto _push; + case CS_UINT_64: + if(template_callback_uint64(user, _msgpack_load64(uint64_t,n), &obj) < 0) { goto _failed; } + goto _push; + + case CS_INT_8: + if(template_callback_int8(user, *(int8_t*)n, &obj) < 0) { goto _failed; } + goto _push; + case CS_INT_16: + if(template_callback_int16(user, _msgpack_load16(int16_t,n), &obj) < 0) { goto _failed; } + goto _push; + case CS_INT_32: + if(template_callback_int32(user, _msgpack_load32(int32_t,n), &obj) < 0) { goto _failed; } + goto _push; + case CS_INT_64: + if(template_callback_int64(user, _msgpack_load64(int64_t,n), &obj) < 0) { goto _failed; } + goto _push; + + case CS_BIN_8: + case CS_RAW_8: + trail = *(uint8_t*)n; + if(trail == 0) { goto _raw_zero; } + cs = ACS_RAW_VALUE; + goto _fixed_trail_again; + case CS_BIN_16: + case CS_RAW_16: + trail = _msgpack_load16(uint16_t, n); + if(trail == 0) { goto _raw_zero; } + cs = ACS_RAW_VALUE; + goto _fixed_trail_again; + case CS_BIN_32: + case CS_RAW_32: + trail = _msgpack_load32(uint32_t, n); + if(trail == 0) { goto _raw_zero; } + cs = ACS_RAW_VALUE; + goto _fixed_trail_again; + case ACS_RAW_VALUE: + _raw_zero: + if(template_callback_raw(user, (const char*)data, (const char*)n, trail, &obj) < 0) { goto _failed; } + goto _push; + case CS_ARRAY_16: + if(top >= MSGPACK_EMBED_STACK_SIZE) { goto _failed; } /* FIXME */ + if(template_callback_array(user, _msgpack_load16(uint16_t, n), &stack[top].obj) < 0) { goto _failed; } + if(_msgpack_load16(uint16_t, n) == 0) { obj = stack[top].obj; goto _push; } + stack[top].ct = CT_ARRAY_ITEM; + stack[top].count = _msgpack_load16(uint16_t, n); + ++top; + goto _header_again; + case CS_ARRAY_32: + /* FIXME security guard */ + if(top >= MSGPACK_EMBED_STACK_SIZE) { goto _failed; } /* FIXME */ + if(template_callback_array(user, _msgpack_load32(uint32_t, n), &stack[top].obj) < 0) { goto _failed; } + if(_msgpack_load32(uint32_t, n) == 0) { obj = stack[top].obj; goto _push; } + stack[top].ct = CT_ARRAY_ITEM; + stack[top].count = _msgpack_load32(uint32_t, n); + ++top; + goto _header_again; + + case CS_MAP_16: + if(top >= MSGPACK_EMBED_STACK_SIZE) { goto _failed; } /* FIXME */ + if(template_callback_map(user, _msgpack_load16(uint16_t, n), &stack[top].obj) < 0) { goto _failed; } + if(_msgpack_load16(uint16_t, n) == 0) { obj = stack[top].obj; goto _push; } + stack[top].ct = CT_MAP_KEY; + stack[top].count = _msgpack_load16(uint16_t, n); + ++top; + goto _header_again; + case CS_MAP_32: + /* FIXME security guard */ + if(top >= MSGPACK_EMBED_STACK_SIZE) { goto _failed; } /* FIXME */ + if(template_callback_map(user, _msgpack_load32(uint32_t, n), &stack[top].obj) < 0) { goto _failed; } + if(_msgpack_load32(uint32_t, n) == 0) { obj = stack[top].obj; goto _push; } + stack[top].ct = CT_MAP_KEY; + stack[top].count = _msgpack_load32(uint32_t, n); + ++top; + goto _header_again; + + default: + goto _failed; + } + } + +_push: + if(top == 0) { goto _finish; } + c = &stack[top-1]; + switch(c->ct) { + case CT_ARRAY_ITEM: + if(template_callback_array_item(user, &c->obj, obj) < 0) { goto _failed; } + if(--c->count == 0) { + obj = c->obj; + --top; + /*printf("stack pop %d\n", top);*/ + goto _push; + } + goto _header_again; + case CT_MAP_KEY: + c->map_key = obj; + c->ct = CT_MAP_VALUE; + goto _header_again; + case CT_MAP_VALUE: + if(template_callback_map_item(user, &c->obj, c->map_key, obj) < 0) { goto _failed; } + if(--c->count == 0) { + obj = c->obj; + --top; + /*printf("stack pop %d\n", top);*/ + goto _push; + } + c->ct = CT_MAP_KEY; + goto _header_again; + + default: + goto _failed; + } + +_header_again: + cs = CS_HEADER; + ++p; + } while(p != pe); + goto _out; + + +_finish: + stack[0].obj = obj; + ++p; + ret = 1; + /*printf("-- finish --\n"); */ + goto _end; + +_failed: + /*printf("** FAILED **\n"); */ + ret = -1; + goto _end; + +_out: + ret = 0; + goto _end; + +_end: + ctx.cs = cs; + ctx.trail = trail; + ctx.top = top; + *off = p - (const unsigned char*)data; + + return ret; +} + + + + +} // detail + struct unpack_error : public std::runtime_error { unpack_error(const std::string& msg) : @@ -42,8 +510,8 @@ class unpacked { public: unpacked() { } - unpacked(object obj, std::auto_ptr z) : - m_obj(obj), m_zone(z) { } + unpacked(object obj, std::unique_ptr z) : + m_obj(obj), m_zone(std::move(z)) { } object& get() { return m_obj; } @@ -51,19 +519,19 @@ public: const object& get() const { return m_obj; } - std::auto_ptr& zone() + std::unique_ptr& zone() { return m_zone; } - const std::auto_ptr& zone() const + const std::unique_ptr& zone() const { return m_zone; } private: object m_obj; - std::auto_ptr m_zone; + std::unique_ptr m_zone; }; -class unpacker : public msgpack_unpacker { +class unpacker { public: unpacker(size_t init_buffer_size = MSGPACK_UNPACKER_INIT_BUFFER_SIZE); ~unpacker(); @@ -154,7 +622,19 @@ public: void remove_nonparsed_buffer(); private: - typedef msgpack_unpacker base; + void expand_buffer(size_t size); + int execute_imp(); + bool flush_zone(); + +private: + char* buffer_; + size_t used_; + size_t free_; + size_t off_; + size_t parsed_; + zone* z_; + size_t initial_buffer_size_; + detail::template_context ctx_; private: unpacker(const unpacker&); @@ -184,42 +664,130 @@ static object unpack(const char* data, size_t len, zone& z, size_t* off = NULL); inline unpacker::unpacker(size_t initial_buffer_size) { - if(!msgpack_unpacker_init(this, initial_buffer_size)) { + if(initial_buffer_size < COUNTER_SIZE) { + initial_buffer_size = COUNTER_SIZE; + } + + char* buffer = (char*)::malloc(initial_buffer_size); + if(!buffer) { throw std::bad_alloc(); } + + zone* z = zone::create(MSGPACK_ZONE_CHUNK_SIZE); + if(!z) { + ::free(buffer); + throw std::bad_alloc(); + } + + buffer_ = buffer; + used_ = COUNTER_SIZE; + free_ = initial_buffer_size - used_; + off_ = COUNTER_SIZE; + parsed_ = 0; + initial_buffer_size_ = initial_buffer_size; + z_ = z; + + detail::init_count(buffer_); + + detail::template_init(ctx_); + ctx_.user.z = z_; + ctx_.user.referenced = false; } inline unpacker::~unpacker() { - msgpack_unpacker_destroy(this); + zone::destroy(z_); + detail::decl_count(buffer_); } inline void unpacker::reserve_buffer(size_t size) { - if(!msgpack_unpacker_reserve_buffer(this, size)) { - throw std::bad_alloc(); + if(free_ >= size) return; + expand_buffer(size); +} + +inline void unpacker::expand_buffer(size_t size) +{ + if(used_ == off_ && detail::get_count(buffer_) == 1 + && !ctx_.user.referenced) { + // rewind buffer + free_ += used_ - COUNTER_SIZE; + used_ = COUNTER_SIZE; + off_ = COUNTER_SIZE; + + if(free_ >= size) return; + } + + if(off_ == COUNTER_SIZE) { + size_t next_size = (used_ + free_) * 2; // include COUNTER_SIZE + while(next_size < size + used_) { + next_size *= 2; + } + + char* tmp = (char*)::realloc(buffer_, next_size); + if(!tmp) { + throw std::bad_alloc(); + } + + buffer_ = tmp; + free_ = next_size - used_; + + } else { + size_t next_size = initial_buffer_size_; // include COUNTER_SIZE + size_t not_parsed = used_ - off_; + while(next_size < size + not_parsed + COUNTER_SIZE) { + next_size *= 2; + } + + char* tmp = (char*)::malloc(next_size); + if(!tmp) { + throw std::bad_alloc(); + } + + detail::init_count(tmp); + + ::memcpy(tmp+COUNTER_SIZE, buffer_ + off_, not_parsed); + + if(ctx_.user.referenced) { + try { + z_->push_finalizer(&detail::decl_count, buffer_); + } + catch (...) { + ::free(tmp); + throw; + } + ctx_.user.referenced = false; + } else { + detail::decl_count(buffer_); + } + + buffer_ = tmp; + used_ = not_parsed + COUNTER_SIZE; + free_ = next_size - used_; + off_ = COUNTER_SIZE; } } inline char* unpacker::buffer() { - return msgpack_unpacker_buffer(this); + return buffer_ + used_; } inline size_t unpacker::buffer_capacity() const { - return msgpack_unpacker_buffer_capacity(this); + return free_; } inline void unpacker::buffer_consumed(size_t size) { - return msgpack_unpacker_buffer_consumed(this, size); + used_ += size; + free_ -= size; } inline bool unpacker::next(unpacked* result) { - int ret = msgpack_unpacker_execute(this); + int ret = execute_imp(); if(ret < 0) { throw unpack_error("parse error"); @@ -241,7 +809,7 @@ inline bool unpacker::next(unpacked* result) inline bool unpacker::execute() { - int ret = msgpack_unpacker_execute(this); + int ret = execute_imp(); if(ret < 0) { throw unpack_error("parse error"); } else if(ret == 0) { @@ -251,77 +819,159 @@ inline bool unpacker::execute() } } +inline int unpacker::execute_imp() +{ + size_t off = off_; + int ret = detail::template_execute(ctx_, + buffer_, used_, &off_); + if(off_ > off) { + parsed_ += off_ - off; + } + return ret; +} + inline object unpacker::data() { - return msgpack_unpacker_data(this); + return template_data(ctx_); } inline zone* unpacker::release_zone() { - return static_cast(msgpack_unpacker_release_zone(static_cast(this))); + if(!flush_zone()) { + return nullptr; + } + + zone* r = zone::create(MSGPACK_ZONE_CHUNK_SIZE); + if(!r) { + return nullptr; + } + + zone* old = z_; + z_ = r; + ctx_.user.z = z_; + + return old; } inline void unpacker::reset_zone() { - msgpack_unpacker_reset_zone(this); + z_->clear(); +} + +inline bool unpacker::flush_zone() +{ + if(ctx_.user.referenced) { + try { + z_->push_finalizer(&detail::decl_count, buffer_); + } catch (...) { + return false; + } + ctx_.user.referenced = false; + + detail::incr_count(buffer_); + } + + return true; } inline void unpacker::reset() { - msgpack_unpacker_reset(this); + detail::template_init(ctx_); + // don't reset referenced flag + parsed_ = 0; } - inline size_t unpacker::message_size() const { - return msgpack_unpacker_message_size(this); + return parsed_ - off_ + used_; } inline size_t unpacker::parsed_size() const { - return msgpack_unpacker_parsed_size(this); + return parsed_; } inline char* unpacker::nonparsed_buffer() { - return base::buffer + base::off; + return buffer_ + off_; } inline size_t unpacker::nonparsed_size() const { - return base::used - base::off; + return used_ - off_; } inline void unpacker::skip_nonparsed_buffer(size_t size) { - base::off += size; + off_ += size; } inline void unpacker::remove_nonparsed_buffer() { - base::used = base::off; + used_ = off_; } +namespace detail { + +inline unpack_return +unpack_imp(const char* data, size_t len, size_t* off, + zone* result_zone, ::msgpack::object* result) +{ + size_t noff = 0; + if(off != NULL) { noff = *off; } + + if(len <= noff) { + // FIXME + return UNPACK_CONTINUE; + } + + detail::template_context ctx; + detail::template_init(ctx); + + ctx.user.z = result_zone; + ctx.user.referenced = false; + + int e = detail::template_execute(ctx, data, len, &noff); + if(e < 0) { + return UNPACK_PARSE_ERROR; + } + + if(off != NULL) { *off = noff; } + + if(e == 0) { + return UNPACK_CONTINUE; + } + + *result = detail::template_data(ctx); + + if(noff < len) { + return UNPACK_EXTRA_BYTES; + } + + return UNPACK_SUCCESS; +} + +} // detail inline void unpack(unpacked* result, const char* data, size_t len, size_t* offset) { msgpack::object obj; - std::auto_ptr z(new zone()); + std::unique_ptr z(new zone()); + + unpack_return ret = detail::unpack_imp( + data, len, offset, z.get(), &obj); - unpack_return ret = (unpack_return)msgpack_unpack( - data, len, offset, z.get(), - reinterpret_cast(&obj)); switch(ret) { case UNPACK_SUCCESS: result->get() = obj; - result->zone() = z; + result->zone() = std::move(z); return; case UNPACK_EXTRA_BYTES: result->get() = obj; - result->zone() = z; + result->zone() = std::move(z); return; case UNPACK_CONTINUE: @@ -338,8 +988,8 @@ inline void unpack(unpacked* result, inline unpack_return unpack(const char* data, size_t len, size_t* off, zone* z, object* result) { - return (unpack_return)msgpack_unpack(data, len, off, - z, reinterpret_cast(result)); + return detail::unpack_imp(data, len, off, + z, result); } // obsolete @@ -367,7 +1017,6 @@ inline object unpack(const char* data, size_t len, zone& z, size_t* off) } } - } // namespace msgpack #endif /* msgpack/unpack.hpp */ diff --git a/src/msgpack/version.hpp.in b/src/msgpack/version.hpp.in new file mode 100644 index 00000000..f13ab0b7 --- /dev/null +++ b/src/msgpack/version.hpp.in @@ -0,0 +1,37 @@ +/* + * MessagePack for C++ version information + * + * Copyright (C) 2008-2013 FURUHASHI Sadayuki and Takatoshi Kondo + * + * 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_HPP +#define MSGPACK_VERSION_HPP + +#define MSGPACK_VERSION "@VERSION@" +#define MSGPACK_VERSION_MAJOR @VERSION_MAJOR@ +#define MSGPACK_VERSION_MINOR @VERSION_MINOR@ + +inline const char* msgpack_version(void) { + return MSGPACK_VERSION; +} + +inline int msgpack_version_major(void) { + return MSGPACK_VERSION_MAJOR; +} + +inline int msgpack_version_minor(void) { + return MSGPACK_VERSION_MINOR; +} + +#endif /* msgpack/version.hpp */ diff --git a/src/msgpack/vrefbuffer.hpp b/src/msgpack/vrefbuffer.hpp index 2cf7f89c..8d22cfea 100644 --- a/src/msgpack/vrefbuffer.hpp +++ b/src/msgpack/vrefbuffer.hpp @@ -1,7 +1,7 @@ // // MessagePack for C++ zero-copy buffer implementation // -// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// Copyright (C) 2008-2013 FURUHASHI Sadayuki and KONDO Takatoshi // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,34 +15,96 @@ // See the License for the specific language governing permissions and // limitations under the License. // -#ifndef MSGPACK_VREFBUFFER_HPP__ -#define MSGPACK_VREFBUFFER_HPP__ +#ifndef MSGPACK_VREFBUFFER_HPP +#define MSGPACK_VREFBUFFER_HPP -#include "vrefbuffer.h" #include +#ifndef MSGPACK_VREFBUFFER_REF_SIZE +#define MSGPACK_VREFBUFFER_REF_SIZE 32 +#endif + +#ifndef MSGPACK_VREFBUFFER_CHUNK_SIZE +#define MSGPACK_VREFBUFFER_CHUNK_SIZE 8192 +#endif + +#ifndef _WIN32 +#include +#else +struct iovec { + void *iov_base; + size_t iov_len; +}; +#endif + namespace msgpack { +namespace detail { -class vrefbuffer : public msgpack_vrefbuffer { + +} // detail + +class vrefbuffer { +private: + struct chunk { + chunk* next; + }; + struct inner_buffer { + size_t free; + char* ptr; + chunk* head; + }; public: vrefbuffer(size_t ref_size = MSGPACK_VREFBUFFER_REF_SIZE, size_t chunk_size = MSGPACK_VREFBUFFER_CHUNK_SIZE) + :ref_size_(ref_size), chunk_size_(chunk_size) { - if (!msgpack_vrefbuffer_init(this, ref_size, chunk_size)) { + size_t nfirst = (sizeof(iovec) < 72/2) ? + 72 / sizeof(iovec) : 8; + + iovec* array = (iovec*)::malloc( + sizeof(iovec) * nfirst); + if(!array) { throw std::bad_alloc(); } + + tail_ = array; + end_ = array + nfirst; + array_ = array; + + chunk* c = (chunk*)::malloc(sizeof(chunk) + chunk_size); + if(!c) { + ::free(array); + throw std::bad_alloc(); + } + inner_buffer* const ib = &inner_buffer_; + + ib->free = chunk_size; + ib->ptr = ((char*)c) + sizeof(chunk); + ib->head = c; + c->next = nullptr; + } ~vrefbuffer() { - msgpack_vrefbuffer_destroy(this); + chunk* c = inner_buffer_.head; + while(true) { + chunk* n = c->next; + ::free(c); + if(n != NULL) { + c = n; + } else { + break; + } + } + ::free(array_); } public: void write(const char* buf, size_t len) { - if(len < base::ref_size) { + if(len < ref_size_) { append_copy(buf, len); } else { append_ref(buf, len); @@ -51,45 +113,162 @@ public: void append_ref(const char* buf, size_t len) { - if(msgpack_vrefbuffer_append_ref(this, buf, len) < 0) { - throw std::bad_alloc(); + if(tail_ == end_) { + const size_t nused = tail_ - array_; + const size_t nnext = nused * 2; + + iovec* nvec = (iovec*)::realloc( + array_, sizeof(iovec)*nnext); + if(!nvec) { + throw std::bad_alloc(); + } + + array_ = nvec; + end_ = nvec + nnext; + tail_ = nvec + nused; } + + tail_->iov_base = (char*)buf; + tail_->iov_len = len; + ++tail_; } void append_copy(const char* buf, size_t len) { - if(msgpack_vrefbuffer_append_copy(this, buf, len) < 0) { - throw std::bad_alloc(); + inner_buffer* const ib = &inner_buffer_; + + if(ib->free < len) { + size_t sz = chunk_size_; + if(sz < len) { + sz = len; + } + + chunk* c = (chunk*)::malloc(sizeof(chunk) + sz); + if(!c) { + throw std::bad_alloc(); + } + + c->next = ib->head; + ib->head = c; + ib->free = sz; + ib->ptr = ((char*)c) + sizeof(chunk); + } + + char* m = ib->ptr; + ::memcpy(m, buf, len); + ib->free -= len; + ib->ptr += len; + + if(tail_ != array_ && m == + (const char*)((tail_ - 1)->iov_base) + (tail_ - 1)->iov_len) { + (tail_ - 1)->iov_len += len; + return; + } else { + append_ref( m, len); } } const struct iovec* vector() const { - return msgpack_vrefbuffer_vec(this); + return array_; } size_t vector_size() const { - return msgpack_vrefbuffer_veclen(this); + return tail_ - array_; } void migrate(vrefbuffer* to) { - if(msgpack_vrefbuffer_migrate(this, to) < 0) { + size_t sz = chunk_size_; + + chunk* empty = (chunk*)::malloc(sizeof(chunk) + sz); + if(!empty) { throw std::bad_alloc(); } - } + empty->next = nullptr; + + const size_t nused = tail_ - array_; + if(to->tail_ + nused < end_) { + const size_t tosize = to->tail_ - to->array_; + const size_t reqsize = nused + tosize; + size_t nnext = (to->end_ - to->array_) * 2; + while(nnext < reqsize) { + nnext *= 2; + } + + iovec* nvec = (iovec*)::realloc( + to->array_, sizeof(iovec)*nnext); + if(!nvec) { + ::free(empty); + throw std::bad_alloc(); + } + + to->array_ = nvec; + to->end_ = nvec + nnext; + to->tail_ = nvec + tosize; + } + + ::memcpy(to->tail_, array_, sizeof(iovec)*nused); + + to->tail_ += nused; + tail_ = array_; + + + inner_buffer* const ib = &inner_buffer_; + inner_buffer* const toib = &to->inner_buffer_; + + chunk* last = ib->head; + while(last->next) { + last = last->next; + } + last->next = toib->head; + toib->head = ib->head; + + if(toib->free < ib->free) { + toib->free = ib->free; + toib->ptr = ib->ptr; + } + + ib->head = empty; + ib->free = sz; + ib->ptr = ((char*)empty) + sizeof(chunk); + + } + void clear() { - msgpack_vrefbuffer_clear(this); - } + chunk* c = inner_buffer_.head->next; + chunk* n; + while(c) { + n = c->next; + ::free(c); + c = n; + } -private: - typedef msgpack_vrefbuffer base; + inner_buffer* const ib = &inner_buffer_; + c = ib->head; + c->next = nullptr; + ib->free = chunk_size_; + ib->ptr = ((char*)c) + sizeof(chunk); + + tail_ = array_; + } private: vrefbuffer(const vrefbuffer&); + +private: + iovec* tail_; + iovec* end_; + iovec* array_; + + size_t ref_size_; + size_t chunk_size_; + + inner_buffer inner_buffer_; + }; diff --git a/src/msgpack/zbuffer.hpp b/src/msgpack/zbuffer.hpp index 69e2eb79..1afe8025 100644 --- a/src/msgpack/zbuffer.hpp +++ b/src/msgpack/zbuffer.hpp @@ -1,7 +1,7 @@ // // MessagePack for C++ deflate buffer implementation // -// Copyright (C) 2010 FURUHASHI Sadayuki +// Copyright (C) 2010-2013 FURUHASHI Sadayuki and KONDO Takatoshi // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,84 +15,144 @@ // See the License for the specific language governing permissions and // limitations under the License. // -#ifndef MSGPACK_ZBUFFER_HPP__ -#define MSGPACK_ZBUFFER_HPP__ +#ifndef MSGPACK_ZBUFFER_HPP +#define MSGPACK_ZBUFFER_HPP -#include "zbuffer.h" #include +#include + +#ifndef MSGPACK_ZBUFFER_RESERVE_SIZE +#define MSGPACK_ZBUFFER_RESERVE_SIZE 512 +#endif + +#ifndef MSGPACK_ZBUFFER_INIT_SIZE +#define MSGPACK_ZBUFFER_INIT_SIZE 8192 +#endif namespace msgpack { -class zbuffer : public msgpack_zbuffer { +class zbuffer { public: zbuffer(int level = Z_DEFAULT_COMPRESSION, size_t init_size = MSGPACK_ZBUFFER_INIT_SIZE) + : data_(nullptr), init_size_(init_size) { - if (!msgpack_zbuffer_init(this, level, init_size)) { + stream_.zalloc = Z_NULL; + stream_.zfree = Z_NULL; + stream_.opaque = Z_NULL; + stream_.next_out = Z_NULL; + stream_.avail_out = 0; + if(deflateInit(&stream_, level) != Z_OK) { throw std::bad_alloc(); } } ~zbuffer() { - msgpack_zbuffer_destroy(this); + deflateEnd(&stream_); + ::free(data_); } public: void write(const char* buf, size_t len) { - if(msgpack_zbuffer_write(this, buf, len) < 0) { - throw std::bad_alloc(); - } + stream_.next_in = (Bytef*)buf; + stream_.avail_in = len; + + do { + if(stream_.avail_out < MSGPACK_ZBUFFER_RESERVE_SIZE) { + if(!expand()) { + throw std::bad_alloc(); + } + } + + if(deflate(&stream_, Z_NO_FLUSH) != Z_OK) { + throw std::bad_alloc(); + } + } while(stream_.avail_in > 0); } char* flush() { - char* buf = msgpack_zbuffer_flush(this); - if(!buf) { - throw std::bad_alloc(); + while(true) { + switch(deflate(&stream_, Z_FINISH)) { + case Z_STREAM_END: + return data_; + case Z_OK: + if(!expand()) { + throw std::bad_alloc(); + } + break; + default: + throw std::bad_alloc(); + } } - return buf; } char* data() { - return base::data; + return data_; } const char* data() const { - return base::data; + return data_; } size_t size() const { - return msgpack_zbuffer_size(this); + return (char*)stream_.next_out - data_; } void reset() { - if(!msgpack_zbuffer_reset(this)) { + if(deflateReset(&stream_) != Z_OK) { throw std::bad_alloc(); } + reset_buffer(); } void reset_buffer() { - msgpack_zbuffer_reset_buffer(this); + stream_.avail_out += (char*)stream_.next_out - data_; + stream_.next_out = (Bytef*)data_; } char* release_buffer() { - return msgpack_zbuffer_release_buffer(this); + char* tmp = data_; + data_ = NULL; + stream_.next_out = NULL; + stream_.avail_out = 0; + return tmp; } private: - typedef msgpack_zbuffer base; + bool expand() + { + size_t used = (char*)stream_.next_out - data_; + size_t csize = used + stream_.avail_out; + size_t nsize = (csize == 0) ? init_size_ : csize * 2; + char* tmp = (char*)::realloc(data_, nsize); + if(tmp == NULL) { + return false; + } + + data_ = tmp; + stream_.next_out = (Bytef*)(tmp + used); + stream_.avail_out = nsize - used; + + return true; + } private: zbuffer(const zbuffer&); + +private: + z_stream stream_; + char* data_; + size_t init_size_; }; diff --git a/test/fixint_c.cc b/test/fixint_c.cc index caa4d262..7cd05261 100644 --- a/test/fixint_c.cc +++ b/test/fixint_c.cc @@ -1,4 +1,4 @@ -#include +#include #include TEST(fixint, size) diff --git a/test/msgpack_test.cpp b/test/msgpack_test.cpp index fb35c683..937806de 100644 --- a/test/msgpack_test.cpp +++ b/test/msgpack_test.cpp @@ -170,10 +170,10 @@ TEST(MSGPACK, simple_buffer_float) float val2; obj.convert(&val2); - if (isnan(val1)) - EXPECT_TRUE(isnan(val2)); - else if (isinf(val1)) - EXPECT_TRUE(isinf(val2)); + if (std::isnan(val1)) + EXPECT_TRUE(std::isnan(val2)); + else if (std::isinf(val1)) + EXPECT_TRUE(std::isinf(val2)); else EXPECT_TRUE(fabs(val2 - val1) <= kEPS); } @@ -260,10 +260,10 @@ TEST(MSGPACK, simple_buffer_double) double val2; obj.convert(&val2); - if (isnan(val1)) - EXPECT_TRUE(isnan(val2)); - else if (isinf(val1)) - EXPECT_TRUE(isinf(val2)); + if (std::isnan(val1)) + EXPECT_TRUE(std::isnan(val2)); + else if (std::isinf(val1)) + EXPECT_TRUE(std::isinf(val2)); else EXPECT_TRUE(fabs(val2 - val1) <= kEPS); } @@ -901,11 +901,11 @@ public: msgpack::type::tuple tuple; o.convert(&tuple); - is_double = tuple.get<0>(); + is_double = get<0>(tuple); if (is_double) - tuple.get<1>().convert(&value.f); + get<1>(tuple).convert(&value.f); else - tuple.get<1>().convert(&value.i); + get<1>(tuple).convert(&value.i); } }; @@ -1097,7 +1097,7 @@ TEST(MSGPACK, vrefbuffer_int64) obj.convert(&val); \ EXPECT_EQ(*it, val); \ ++it; \ - msgpack_zone_free(life); \ + msgpack::zone::destroy(life); \ } \ p += sz; \ } \ diff --git a/test/streaming.cc b/test/streaming.cc index e80c671b..465dea23 100644 --- a/test/streaming.cc +++ b/test/streaming.cc @@ -69,7 +69,7 @@ public: msgpack::unpacked result; while(pac.next(&result)) { - on_message(result.get(), result.zone()); + on_message(result.get(), std::move(result.zone())); } if(pac.message_size() > 10*1024*1024) { @@ -78,7 +78,7 @@ public: } } - void on_message(msgpack::object obj, std::auto_ptr z) + void on_message(msgpack::object obj, std::unique_ptr z) { EXPECT_EQ(expect, obj.as()); } @@ -133,7 +133,7 @@ TEST(streaming, basic_compat) pac.buffer_consumed(len); while(pac.execute()) { - std::auto_ptr z(pac.release_zone()); + std::unique_ptr z(pac.release_zone()); msgpack::object obj = pac.data(); pac.reset(); @@ -174,10 +174,10 @@ public: pac.buffer_consumed(len); while(pac.execute()) { - std::auto_ptr z(pac.release_zone()); + std::unique_ptr z(pac.release_zone()); msgpack::object obj = pac.data(); pac.reset(); - on_message(obj, z); + on_message(obj, std::move(z)); } if(pac.message_size() > 10*1024*1024) { @@ -186,7 +186,7 @@ public: } } - void on_message(msgpack::object obj, std::auto_ptr z) + void on_message(msgpack::object obj, std::unique_ptr z) { EXPECT_EQ(expect, obj.as()); } diff --git a/test/zone.cc b/test/zone.cc index 5274e9f0..be7b89fc 100644 --- a/test/zone.cc +++ b/test/zone.cc @@ -60,11 +60,11 @@ TEST(zone, push_finalizer) } -TEST(zone, push_finalizer_auto_ptr) +TEST(zone, push_finalizer_unique_ptr) { msgpack::zone z; - std::auto_ptr am(new myclass()); - z.push_finalizer(am); + std::unique_ptr am(new myclass()); + z.push_finalizer(std::move(am)); }