Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions include/msgpack/adaptor/check_container_size.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ template <>
inline void check_container_size<4>(std::size_t /*size*/) {
}

template <std::size_t N>
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 <typename T>
Expand Down
245 changes: 245 additions & 0 deletions include/msgpack/adaptor/ext.hpp
Original file line number Diff line number Diff line change
@@ -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 <cstring>
#include <string>
#include <cassert>

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<sizeof(std::size_t)>(s);
m_data.reserve(static_cast<std::size_t>(s) + 1);
m_data.push_back(static_cast<char>(t));
m_data.insert(m_data.end(), p, p + s);
}
ext(int8_t t, uint32_t s) {
detail::check_container_size_for_ext<sizeof(std::size_t)>(s);
m_data.resize(static_cast<std::size_t>(s) + 1);
m_data[0] = static_cast<char>(t);
}
ext(ext_ref const&);
int8_t type() const {
return static_cast<int8_t>(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<char> m_data;
friend class ext_ref;
};

} // namespace type

namespace adaptor {

template <>
struct convert<msgpack::type::ext> {
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<msgpack::type::ext> {
template <typename Stream>
msgpack::packer<Stream>& operator()(msgpack::packer<Stream>& 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<msgpack::type::ext> {
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<char*>(o.zone.allocate_align(size + 1));
o.via.ext.ptr = ptr;
o.via.ext.size = size;
ptr[0] = static_cast<char>(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<char*>(static_cast<void*>(&m_size))), m_size(0) {}
ext_ref(const char* p, uint32_t s) :
m_ptr(s == 0 ? static_cast<char*>(static_cast<void*>(&m_size)) : p),
m_size(s == 0 ? 0 : s - 1) {
detail::check_container_size_for_ext<sizeof(std::size_t)>(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<int8_t>(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<msgpack::type::ext_ref>;
};

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::type::ext_ref> {
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<msgpack::type::ext_ref> {
template <typename Stream>
msgpack::packer<Stream>& operator()(msgpack::packer<Stream>& 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<msgpack::type::ext_ref> {
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<msgpack::type::ext_ref> {
void operator()(msgpack::object::with_zone& o, const msgpack::type::ext_ref& v) const {
static_cast<msgpack::object&>(o) << v;
}
};

} // namespace adaptor

/// @cond
} // MSGPACK_API_VERSION_NAMESPACE(v1)
/// @endcond

} // namespace msgpack

#endif // MSGPACK_TYPE_EXT_HPP
1 change: 1 addition & 0 deletions include/msgpack/type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
81 changes: 80 additions & 1 deletion test/msgpack_basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ TEST(MSGPACK, simple_buffer_fixext_4byte_65536)
msgpack::sbuffer sbuf;
msgpack::packer<msgpack::sbuffer> packer(sbuf);
char buf[size];
for (int i = 0; i != size; ++i) buf[i] = static_cast<char>(i);
for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast<char>(i);
packer.pack_ext(sizeof(buf), 77);
packer.pack_ext_body(buf, sizeof(buf));

Expand All @@ -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<msgpack::sbuffer> packer(sbuf);
char buf[size];
for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast<char>(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<char>(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<msgpack::sbuffer> packer(sbuf);
char buf[size];
for (std::size_t i = 0; i != size; ++i) buf[i] = static_cast<char>(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<char>(77);
for (std::size_t i = 0; i != data_size; ++i) buf[i + 1] = static_cast<char>(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++) {
Expand Down
Loading