From ff14be8fdb06e995beedf5550da3cba047bdad4c Mon Sep 17 00:00:00 2001 From: Takatoshi Kondo Date: Fri, 22 May 2015 17:46:58 +0900 Subject: [PATCH] Added EXT type supporting classes. --- CMakeLists.txt | 1 + .../msgpack/adaptor/check_container_size.hpp | 10 + include/msgpack/adaptor/ext.hpp | 245 ++++++++++++++++++ include/msgpack/type.hpp | 1 + src/Makefile.am | 1 + test/msgpack_basic.cpp | 81 +++++- test/object_with_zone.cpp | 48 ++++ 7 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 include/msgpack/adaptor/ext.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cdbf22a..d6f2fafa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,6 +121,7 @@ IF (MSGPACK_ENABLE_CXX) include/msgpack/adaptor/detail/cpp03_msgpack_tuple.hpp include/msgpack/adaptor/detail/cpp11_define.hpp include/msgpack/adaptor/detail/cpp11_msgpack_tuple.hpp + include/msgpack/adaptor/ext.hpp include/msgpack/adaptor/fixint.hpp include/msgpack/adaptor/float.hpp include/msgpack/adaptor/int.hpp diff --git a/include/msgpack/adaptor/check_container_size.hpp b/include/msgpack/adaptor/check_container_size.hpp index 294c3ece..b9caf547 100644 --- a/include/msgpack/adaptor/check_container_size.hpp +++ b/include/msgpack/adaptor/check_container_size.hpp @@ -47,6 +47,16 @@ template <> inline void check_container_size<4>(std::size_t /*size*/) { } +template +inline void check_container_size_for_ext(std::size_t size) { + if (size > 0xffffffff) throw container_size_overflow("container size overflow"); +} + +template <> +inline void check_container_size_for_ext<4>(std::size_t size) { + if (size > 0xfffffffe) throw container_size_overflow("container size overflow"); +} + } // namespace detail template diff --git a/include/msgpack/adaptor/ext.hpp b/include/msgpack/adaptor/ext.hpp new file mode 100644 index 00000000..84d67a6b --- /dev/null +++ b/include/msgpack/adaptor/ext.hpp @@ -0,0 +1,245 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2015 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_EXT_HPP +#define MSGPACK_TYPE_EXT_HPP + +#include "msgpack/versioning.hpp" +#include "msgpack/adaptor/adaptor_base.hpp" +#include +#include +#include + +namespace msgpack { + +/// @cond +MSGPACK_API_VERSION_NAMESPACE(v1) { +/// @endcond + +namespace type { +class ext_ref; + +class ext { +public: + ext() : m_data(1, 0) {} + ext(int8_t t, const char* p, uint32_t s) { + detail::check_container_size_for_ext(s); + m_data.reserve(static_cast(s) + 1); + m_data.push_back(static_cast(t)); + m_data.insert(m_data.end(), p, p + s); + } + ext(int8_t t, uint32_t s) { + detail::check_container_size_for_ext(s); + m_data.resize(static_cast(s) + 1); + m_data[0] = static_cast(t); + } + ext(ext_ref const&); + int8_t type() const { + return static_cast(m_data[0]); + } + const char* data() const { + return &m_data[1]; + } + char* data() { + return &m_data[1]; + } + uint32_t size() const { + return m_data.size() - 1; + } + bool operator== (const ext& x) const { + return m_data == x.m_data; + } + + bool operator!= (const ext& x) const { + return !(*this == x); + } + + bool operator< (const ext& x) const { + return m_data < x.m_data; + } + + bool operator> (const ext& x) const { + return m_data > x.m_data; + } +private: + std::vector m_data; + friend class ext_ref; +}; + +} // namespace type + +namespace adaptor { + +template <> +struct convert { + msgpack::object const& operator()(msgpack::object const& o, msgpack::type::ext& v) const { + if(o.type != msgpack::type::EXT) { + throw msgpack::type_error(); + } + v = msgpack::type::ext(o.via.ext.type(), o.via.ext.data(), o.via.ext.size); + return o; + } +}; + +template <> +struct pack { + template + msgpack::packer& operator()(msgpack::packer& o, const msgpack::type::ext& v) const { + // size limit has aleady been checked at ext's constructor + uint32_t size = v.size(); + o.pack_ext(size, v.type()); + o.pack_ext_body(v.data(), size); + return o; + } +}; + +template <> +struct object_with_zone { + void operator()(msgpack::object::with_zone& o, const msgpack::type::ext& v) const { + // size limit has aleady been checked at ext's constructor + uint32_t size = v.size(); + o.type = msgpack::type::EXT; + char* ptr = static_cast(o.zone.allocate_align(size + 1)); + o.via.ext.ptr = ptr; + o.via.ext.size = size; + ptr[0] = static_cast(v.type()); + std::memcpy(ptr + 1, v.data(), size); + } +}; + +} // namespace adaptor + +namespace type { + +class ext_ref { +public: + // ext_ref should be default constructible to support 'convert'. + // A default constructed ext_ref object::m_ptr doesn't have the buffer to point to. + // In order to avoid nullptr checking branches, m_ptr points to m_size. + // So type() returns unspecified but valid value. It might be a zero because m_size + // is initialized as zero, but shoudn't assume that. + ext_ref() : m_ptr(static_cast(static_cast(&m_size))), m_size(0) {} + ext_ref(const char* p, uint32_t s) : + m_ptr(s == 0 ? static_cast(static_cast(&m_size)) : p), + m_size(s == 0 ? 0 : s - 1) { + detail::check_container_size_for_ext(s); + } + + // size limit has aleady been checked at ext's constructor + ext_ref(ext const& x) : m_ptr(&x.m_data[0]), m_size(x.size()) {} + + const char* data() const { + return m_ptr + 1; + } + + uint32_t size() const { + return m_size; + } + + int8_t type() const { + return static_cast(m_ptr[0]); + } + + std::string str() const { + return std::string(m_ptr + 1, m_size); + } + + bool operator== (const ext_ref& x) const { + return m_size == x.m_size && std::memcmp(m_ptr, x.m_ptr, m_size) == 0; + } + + bool operator!= (const ext_ref& x) const { + return !(*this == x); + } + + bool operator< (const ext_ref& x) const { + if (m_size < x.m_size) return true; + if (m_size > x.m_size) return false; + return std::memcmp(m_ptr, x.m_ptr, m_size) < 0; + } + + bool operator> (const ext_ref& x) const { + if (m_size > x.m_size) return true; + if (m_size < x.m_size) return false; + return std::memcmp(m_ptr, x.m_ptr, m_size) > 0; + } +private: + const char* m_ptr; + uint32_t m_size; + friend struct msgpack::adaptor::object; +}; + +ext::ext(ext_ref const& x) { + // size limit has aleady been checked at ext_ref's constructor + m_data.reserve(x.size() + 1); + + m_data.push_back(x.type()); + m_data.insert(m_data.end(), x.data(), x.data() + x.size()); +} + +} // namespace type + +namespace adaptor { + +template <> +struct convert { + msgpack::object const& operator()(msgpack::object const& o, msgpack::type::ext_ref& v) const { + if(o.type != msgpack::type::EXT) { throw msgpack::type_error(); } + v = msgpack::type::ext_ref(o.via.ext.ptr, o.via.ext.size + 1); + return o; + } +}; + +template <> +struct pack { + template + msgpack::packer& operator()(msgpack::packer& o, const msgpack::type::ext_ref& v) const { + // size limit has aleady been checked at ext_ref's constructor + uint32_t size = v.size(); + o.pack_ext(size, v.type()); + o.pack_ext_body(v.data(), size); + return o; + } +}; + +template <> +struct object { + void operator()(msgpack::object& o, const msgpack::type::ext_ref& v) const { + // size limit has aleady been checked at ext_ref's constructor + uint32_t size = v.size(); + o.type = msgpack::type::EXT; + o.via.ext.ptr = v.m_ptr; + o.via.ext.size = size; + } +}; + +template <> +struct object_with_zone { + void operator()(msgpack::object::with_zone& o, const msgpack::type::ext_ref& v) const { + static_cast(o) << v; + } +}; + +} // namespace adaptor + +/// @cond +} // MSGPACK_API_VERSION_NAMESPACE(v1) +/// @endcond + +} // namespace msgpack + +#endif // MSGPACK_TYPE_EXT_HPP diff --git a/include/msgpack/type.hpp b/include/msgpack/type.hpp index 2300a0f4..2246a5bf 100644 --- a/include/msgpack/type.hpp +++ b/include/msgpack/type.hpp @@ -2,6 +2,7 @@ #include "adaptor/bool.hpp" #include "adaptor/char_ptr.hpp" #include "adaptor/deque.hpp" +#include "adaptor/ext.hpp" #include "adaptor/fixint.hpp" #include "adaptor/float.hpp" #include "adaptor/int.hpp" diff --git a/src/Makefile.am b/src/Makefile.am index b5dafe60..fbca7f97 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -72,6 +72,7 @@ nobase_include_HEADERS += \ ../include/msgpack/adaptor/detail/cpp03_msgpack_tuple.hpp \ ../include/msgpack/adaptor/detail/cpp11_define.hpp \ ../include/msgpack/adaptor/detail/cpp11_msgpack_tuple.hpp \ + ../include/msgpack/adaptor/ext.hpp \ ../include/msgpack/adaptor/fixint.hpp \ ../include/msgpack/adaptor/float.hpp \ ../include/msgpack/adaptor/int.hpp \ diff --git a/test/msgpack_basic.cpp b/test/msgpack_basic.cpp index 3f2fd4d1..e55e711f 100644 --- a/test/msgpack_basic.cpp +++ b/test/msgpack_basic.cpp @@ -452,7 +452,7 @@ TEST(MSGPACK, simple_buffer_fixext_4byte_65536) msgpack::sbuffer sbuf; msgpack::packer packer(sbuf); char buf[size]; - for (int i = 0; i != size; ++i) buf[i] = static_cast(i); + for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast(i); packer.pack_ext(sizeof(buf), 77); packer.pack_ext_body(buf, sizeof(buf)); @@ -464,6 +464,85 @@ TEST(MSGPACK, simple_buffer_fixext_4byte_65536) std::equal(buf, buf + sizeof(buf), ret.get().via.ext.data())); } +TEST(MSGPACK, simple_buffer_ext_convert) +{ + std::size_t const size = 65536; + msgpack::sbuffer sbuf; + msgpack::packer packer(sbuf); + char buf[size]; + for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast(i); + packer.pack_ext(sizeof(buf), 77); + packer.pack_ext_body(buf, sizeof(buf)); + + msgpack::unpacked ret; + msgpack::unpack(ret, sbuf.data(), sbuf.size()); + msgpack::type::ext e; + ret.get().convert(e); + EXPECT_EQ(size, e.size()); + EXPECT_EQ(77, e.type()); + EXPECT_TRUE( + std::equal(buf, buf + sizeof(buf), e.data())); +} + +TEST(MSGPACK, simple_buffer_ext_pack_convert) +{ + std::size_t const size = 65536; + msgpack::sbuffer sbuf; + msgpack::type::ext val1(77, size); + char* buf = val1.data(); + for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast(i); + msgpack::pack(sbuf, val1); + + msgpack::unpacked ret; + msgpack::unpack(ret, sbuf.data(), sbuf.size()); + msgpack::type::ext val2; + ret.get().convert(val2); + EXPECT_EQ(size, val2.size()); + EXPECT_EQ(77, val2.type()); + EXPECT_TRUE( + std::equal(buf, buf + sizeof(buf), val2.data())); +} + +TEST(MSGPACK, simple_buffer_ext_ref_convert) +{ + std::size_t const size = 65536; + msgpack::sbuffer sbuf; + msgpack::packer packer(sbuf); + char buf[size]; + for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast(i); + packer.pack_ext(sizeof(buf), 77); + packer.pack_ext_body(buf, sizeof(buf)); + + msgpack::unpacked ret; + msgpack::unpack(ret, sbuf.data(), sbuf.size()); + msgpack::type::ext_ref er; + ret.get().convert(er); + EXPECT_EQ(size, er.size()); + EXPECT_EQ(77, er.type()); + EXPECT_TRUE( + std::equal(buf, buf + sizeof(buf), er.data())); +} + +TEST(MSGPACK, simple_buffer_ext_ref_pack_convert) +{ + std::size_t const buf_size = 65536; + std::size_t const data_size = buf_size - 1; + msgpack::sbuffer sbuf; + char buf[buf_size]; + buf[0] = static_cast(77); + for (std::size_t i = 0; i != data_size; ++i) buf[i + 1] = static_cast(i); + msgpack::pack(sbuf, msgpack::type::ext_ref(buf, buf_size)); + + msgpack::unpacked ret; + msgpack::unpack(ret, sbuf.data(), sbuf.size()); + msgpack::type::ext_ref val2; + ret.get().convert(val2); + EXPECT_EQ(data_size, val2.size()); + EXPECT_EQ(77, val2.type()); + EXPECT_TRUE( + std::equal(&buf[1], &buf[buf_size], val2.data())); +} + TEST(MSGPACK_STL, simple_buffer_string) { for (unsigned int k = 0; k < kLoop; k++) { diff --git a/test/object_with_zone.cpp b/test/object_with_zone.cpp index 5b5a7212..e7a254af 100644 --- a/test/object_with_zone.cpp +++ b/test/object_with_zone.cpp @@ -864,3 +864,51 @@ TEST(object_with_zone, tuple_empty) } #endif // !defined(MSGPACK_USE_CPP03) + +TEST(object_with_zone, ext_empty) +{ + msgpack::type::ext v; + msgpack::zone z; + msgpack::object obj(v, z); + EXPECT_EQ(obj.as(), v); + EXPECT_EQ(obj.as(), v); +} + +TEST(object_with_zone, ext) +{ + msgpack::type::ext v(42, 10); + for (int i = 0; i < 10; ++i) v.data()[i] = i; + msgpack::zone z; + msgpack::object obj(v, z); + EXPECT_EQ(obj.as(), v); + EXPECT_EQ(obj.as(), v); +} + +TEST(object_with_zone, ext_from_buf) +{ + char const buf[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + msgpack::type::ext v(42, buf, sizeof(buf)); + msgpack::zone z; + msgpack::object obj(v, z); + EXPECT_EQ(obj.as(), v); + EXPECT_EQ(obj.as(), v); +} + +TEST(object_with_zone, ext_ref_empty) +{ + msgpack::type::ext_ref v; + msgpack::zone z; + msgpack::object obj(v, z); + EXPECT_EQ(obj.as(), v); + EXPECT_EQ(obj.as(), v); +} + +TEST(object_with_zone, ext_ref_from_buf) +{ + char const buf[] = { 77, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + msgpack::type::ext_ref v(buf, sizeof(buf)); + msgpack::zone z; + msgpack::object obj(v, z); + EXPECT_EQ(obj.as(), v); + EXPECT_EQ(obj.as(), v); +}