2009-02-15 09:09:57 +00:00
|
|
|
//
|
|
|
|
// MessagePack for C++ deserializing routine
|
|
|
|
//
|
2009-02-15 09:10:02 +00:00
|
|
|
// Copyright (C) 2008-2009 FURUHASHI Sadayuki
|
2009-02-15 09:09:57 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2009-02-15 09:09:56 +00:00
|
|
|
#include "msgpack/unpack.hpp"
|
2009-02-15 09:09:57 +00:00
|
|
|
#include "msgpack/unpack_define.h"
|
2009-02-15 09:09:56 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
namespace msgpack {
|
|
|
|
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:00 +00:00
|
|
|
//namespace {
|
2009-02-15 09:10:02 +00:00
|
|
|
struct unpack_user {
|
2009-02-15 09:10:00 +00:00
|
|
|
zone* z;
|
|
|
|
bool referenced;
|
|
|
|
};
|
|
|
|
//} // noname namespace
|
|
|
|
|
|
|
|
|
2009-02-15 09:09:57 +00:00
|
|
|
#define msgpack_unpack_struct(name) \
|
2009-02-15 09:10:02 +00:00
|
|
|
struct msgpack_unpacker ## name
|
2009-02-15 09:09:57 +00:00
|
|
|
|
|
|
|
#define msgpack_unpack_func(ret, name) \
|
2009-02-15 09:10:02 +00:00
|
|
|
ret msgpack_unpacker ## name
|
2009-02-15 09:09:57 +00:00
|
|
|
|
|
|
|
#define msgpack_unpack_callback(name) \
|
2009-02-15 09:10:02 +00:00
|
|
|
msgpack_unpack ## name
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:09:58 +00:00
|
|
|
#define msgpack_unpack_object object
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
#define msgpack_unpack_user unpack_user
|
2009-02-15 09:09:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
struct msgpack_unpacker_context;
|
|
|
|
|
|
|
|
static void msgpack_unpacker_init(struct msgpack_unpacker_context* ctx);
|
|
|
|
|
2009-02-15 09:09:58 +00:00
|
|
|
static object msgpack_unpacker_data(struct msgpack_unpacker_context* ctx);
|
2009-02-15 09:09:57 +00:00
|
|
|
|
|
|
|
static int msgpack_unpacker_execute(struct msgpack_unpacker_context* ctx,
|
|
|
|
const char* data, size_t len, size_t* off);
|
|
|
|
|
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_init(unpack_user* u)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ return object(); }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_uint8(unpack_user* u, uint8_t d)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ object o; o.type = type::POSITIVE_INTEGER; o.via.u64 = d; return o; }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_uint16(unpack_user* u, uint16_t d)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ object o; o.type = type::POSITIVE_INTEGER; o.via.u64 = d; return o; }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_uint32(unpack_user* u, uint32_t d)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ object o; o.type = type::POSITIVE_INTEGER; o.via.u64 = d; return o; }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_uint64(unpack_user* u, uint64_t d)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ object o; o.type = type::POSITIVE_INTEGER; o.via.u64 = d; return o; }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_int8(unpack_user* u, int8_t d)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ if(d >= 0) { object o; o.type = type::POSITIVE_INTEGER; o.via.u64 = d; return o; }
|
|
|
|
else { object o; o.type = type::NEGATIVE_INTEGER; o.via.i64 = d; return o; } }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_int16(unpack_user* u, int16_t d)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ if(d >= 0) { object o; o.type = type::POSITIVE_INTEGER; o.via.u64 = d; return o; }
|
|
|
|
else { object o; o.type = type::NEGATIVE_INTEGER; o.via.i64 = d; return o; } }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_int32(unpack_user* u, int32_t d)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ if(d >= 0) { object o; o.type = type::POSITIVE_INTEGER; o.via.u64 = d; return o; }
|
|
|
|
else { object o; o.type = type::NEGATIVE_INTEGER; o.via.i64 = d; return o; } }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_int64(unpack_user* u, int64_t d)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ if(d >= 0) { object o; o.type = type::POSITIVE_INTEGER; o.via.u64 = d; return o; }
|
|
|
|
else { object o; o.type = type::NEGATIVE_INTEGER; o.via.i64 = d; return o; } }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_float(unpack_user* u, float d)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ object o; o.type = type::DOUBLE; o.via.dec = d; return o; }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_double(unpack_user* u, double d)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ object o; o.type = type::DOUBLE; o.via.dec = d; return o; }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_nil(unpack_user* u)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ object o; o.type = type::NIL; return o; }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_true(unpack_user* u)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ object o; o.type = type::BOOLEAN; o.via.boolean = true; return o; }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_false(unpack_user* u)
|
2009-02-15 09:09:58 +00:00
|
|
|
{ object o; o.type = type::BOOLEAN; o.via.boolean = false; return o; }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_array(unpack_user* u, unsigned int n)
|
2009-02-15 09:09:58 +00:00
|
|
|
{
|
|
|
|
object o;
|
|
|
|
o.type = type::ARRAY;
|
2009-02-15 18:39:30 +09:00
|
|
|
o.via.array.size = 0;
|
|
|
|
o.via.array.ptr = (object*)u->z->malloc(n*sizeof(object));
|
2009-02-15 09:09:58 +00:00
|
|
|
return o;
|
|
|
|
}
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline void msgpack_unpack_array_item(unpack_user* u, object* c, object o)
|
2009-02-15 18:39:30 +09:00
|
|
|
{ c->via.array.ptr[c->via.array.size++] = o; }
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_map(unpack_user* u, unsigned int n)
|
2009-02-15 09:09:58 +00:00
|
|
|
{
|
|
|
|
object o;
|
|
|
|
o.type = type::MAP;
|
2009-02-15 18:39:30 +09:00
|
|
|
o.via.map.size = 0;
|
|
|
|
o.via.map.ptr = (object_kv*)u->z->malloc(n*sizeof(object_kv));
|
2009-02-15 09:09:58 +00:00
|
|
|
return o;
|
|
|
|
}
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline void msgpack_unpack_map_item(unpack_user* u, object* c, object k, object v)
|
2009-02-15 09:09:58 +00:00
|
|
|
{
|
2009-02-15 18:39:30 +09:00
|
|
|
c->via.map.ptr[c->via.map.size].key = k;
|
|
|
|
c->via.map.ptr[c->via.map.size].val = v;
|
|
|
|
++c->via.map.size;
|
2009-02-15 09:09:58 +00:00
|
|
|
}
|
2009-02-15 09:09:57 +00:00
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline object msgpack_unpack_raw(unpack_user* u, const char* b, const char* p, unsigned int l)
|
2009-02-15 09:10:00 +00:00
|
|
|
{
|
|
|
|
object o;
|
|
|
|
o.type = type::RAW;
|
2009-02-15 18:39:30 +09:00
|
|
|
o.via.raw.ptr = p;
|
|
|
|
o.via.raw.size = l;
|
2009-02-15 09:10:02 +00:00
|
|
|
u->referenced = true;
|
2009-02-15 09:10:00 +00:00
|
|
|
return o;
|
|
|
|
}
|
2009-02-15 09:09:57 +00:00
|
|
|
|
|
|
|
#include "msgpack/unpack_template.h"
|
|
|
|
|
|
|
|
|
2009-02-15 09:10:00 +00:00
|
|
|
namespace {
|
|
|
|
struct context {
|
|
|
|
context()
|
2009-02-15 09:09:56 +00:00
|
|
|
{
|
|
|
|
msgpack_unpacker_init(&m_ctx);
|
2009-02-15 09:10:02 +00:00
|
|
|
unpack_user u = {NULL, false};
|
|
|
|
m_ctx.user = u;
|
2009-02-15 09:09:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~context() { }
|
|
|
|
|
2009-02-15 09:09:57 +00:00
|
|
|
int execute(const char* data, size_t len, size_t* off)
|
2009-02-15 09:09:56 +00:00
|
|
|
{
|
2009-02-15 09:09:57 +00:00
|
|
|
return msgpack_unpacker_execute(&m_ctx, data, len, off);
|
2009-02-15 09:09:56 +00:00
|
|
|
}
|
|
|
|
|
2009-02-15 09:09:58 +00:00
|
|
|
object data()
|
2009-02-15 09:09:56 +00:00
|
|
|
{
|
|
|
|
return msgpack_unpacker_data(&m_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset()
|
|
|
|
{
|
2009-02-15 09:10:00 +00:00
|
|
|
zone* z = m_ctx.user.z;
|
2009-02-15 09:09:56 +00:00
|
|
|
msgpack_unpacker_init(&m_ctx);
|
2009-02-15 09:10:02 +00:00
|
|
|
unpack_user u = {z, false};
|
|
|
|
m_ctx.user = u;
|
2009-02-15 09:09:56 +00:00
|
|
|
}
|
|
|
|
|
2009-02-15 09:10:00 +00:00
|
|
|
void set_zone(zone* z)
|
2009-02-15 09:09:56 +00:00
|
|
|
{
|
2009-02-15 09:10:00 +00:00
|
|
|
m_ctx.user.z = z;
|
2009-02-15 09:09:56 +00:00
|
|
|
}
|
|
|
|
|
2009-02-15 09:10:00 +00:00
|
|
|
bool is_referenced() const
|
2009-02-15 09:09:56 +00:00
|
|
|
{
|
2009-02-15 09:10:00 +00:00
|
|
|
return m_ctx.user.referenced;
|
2009-02-15 09:09:56 +00:00
|
|
|
}
|
|
|
|
|
2009-02-15 09:09:56 +00:00
|
|
|
private:
|
2009-02-15 09:09:57 +00:00
|
|
|
msgpack_unpacker_context m_ctx;
|
2009-02-15 09:10:00 +00:00
|
|
|
zone* m_zone;
|
2009-02-15 09:09:56 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
context(const context&);
|
|
|
|
};
|
|
|
|
|
2009-02-15 09:10:02 +00:00
|
|
|
static inline context* as_ctx(void* m)
|
2009-02-15 09:10:00 +00:00
|
|
|
{
|
|
|
|
return reinterpret_cast<context*>(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const size_t COUNTER_SIZE = sizeof(unsigned int);
|
|
|
|
|
|
|
|
static inline void init_count(void* buffer)
|
|
|
|
{
|
|
|
|
*(volatile unsigned int*)buffer = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void decl_count(void* buffer)
|
|
|
|
{
|
|
|
|
//if(--*(unsigned int*)buffer == 0) {
|
|
|
|
if(__sync_sub_and_fetch((unsigned int*)buffer, 1) == 0) {
|
|
|
|
free(buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void incr_count(void* buffer)
|
|
|
|
{
|
|
|
|
//++*(unsigned int*)buffer;
|
|
|
|
__sync_add_and_fetch((unsigned int*)buffer, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned int get_count(void* buffer)
|
|
|
|
{
|
|
|
|
return *(volatile unsigned int*)buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // noname namespace
|
|
|
|
|
2009-02-15 09:09:56 +00:00
|
|
|
|
2009-02-15 09:09:59 +00:00
|
|
|
unpacker::unpacker(size_t initial_buffer_size) :
|
2009-02-15 09:09:56 +00:00
|
|
|
m_buffer(NULL),
|
|
|
|
m_used(0),
|
|
|
|
m_free(0),
|
2009-02-15 09:09:58 +00:00
|
|
|
m_off(0),
|
|
|
|
m_zone(new zone()),
|
2009-02-15 09:10:00 +00:00
|
|
|
m_ctx(new context()),
|
2009-02-15 09:09:59 +00:00
|
|
|
m_initial_buffer_size(initial_buffer_size)
|
2009-02-15 09:10:00 +00:00
|
|
|
{
|
|
|
|
if(m_initial_buffer_size < COUNTER_SIZE) {
|
|
|
|
m_initial_buffer_size = COUNTER_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
as_ctx(m_ctx)->set_zone(m_zone.get());
|
|
|
|
|
|
|
|
m_buffer = (char*)::malloc(m_initial_buffer_size);
|
|
|
|
if(!m_buffer) { throw std::bad_alloc(); }
|
|
|
|
init_count(m_buffer);
|
|
|
|
|
|
|
|
m_used = COUNTER_SIZE;
|
|
|
|
m_free = m_initial_buffer_size - m_used;
|
|
|
|
m_off = COUNTER_SIZE;
|
|
|
|
}
|
2009-02-15 09:09:56 +00:00
|
|
|
|
|
|
|
|
2009-02-15 09:09:56 +00:00
|
|
|
unpacker::~unpacker()
|
|
|
|
{
|
2009-02-15 09:10:00 +00:00
|
|
|
delete as_ctx(m_ctx);
|
|
|
|
decl_count(m_buffer);
|
2009-02-15 09:09:56 +00:00
|
|
|
}
|
2009-02-15 09:09:56 +00:00
|
|
|
|
2009-02-15 09:09:56 +00:00
|
|
|
void unpacker::expand_buffer(size_t len)
|
2009-02-15 09:09:56 +00:00
|
|
|
{
|
2009-02-15 09:10:00 +00:00
|
|
|
if(m_used == m_off && get_count(m_buffer) == 1 &&
|
|
|
|
!as_ctx(m_ctx)->is_referenced()) {
|
|
|
|
// rewind buffer
|
|
|
|
m_free += m_used - COUNTER_SIZE;
|
|
|
|
m_used = COUNTER_SIZE;
|
|
|
|
m_off = COUNTER_SIZE;
|
|
|
|
if(m_free >= len) { return; }
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_off == COUNTER_SIZE) {
|
|
|
|
size_t next_size = (m_used + m_free) * 2;
|
2009-02-15 09:09:56 +00:00
|
|
|
while(next_size < len + m_used) { next_size *= 2; }
|
|
|
|
|
2009-02-15 09:10:00 +00:00
|
|
|
char* tmp = (char*)::realloc(m_buffer, next_size);
|
|
|
|
if(!tmp) { throw std::bad_alloc(); }
|
|
|
|
|
|
|
|
m_buffer = tmp;
|
2009-02-15 09:09:56 +00:00
|
|
|
m_free = next_size - m_used;
|
|
|
|
|
|
|
|
} else {
|
2009-02-15 09:10:00 +00:00
|
|
|
size_t next_size = m_initial_buffer_size; // include COUNTER_SIZE
|
|
|
|
size_t not_parsed = m_used - m_off;
|
|
|
|
while(next_size < len + not_parsed + COUNTER_SIZE) { next_size *= 2; }
|
|
|
|
|
|
|
|
char* tmp = (char*)::malloc(next_size);
|
|
|
|
if(!tmp) { throw std::bad_alloc(); }
|
|
|
|
init_count(tmp);
|
|
|
|
|
|
|
|
try {
|
|
|
|
m_zone->push_finalizer(decl_count, m_buffer);
|
|
|
|
} catch (...) { free(tmp); throw; }
|
2009-02-15 09:09:56 +00:00
|
|
|
|
2009-02-15 09:10:00 +00:00
|
|
|
memcpy(tmp+COUNTER_SIZE, m_buffer+m_off, not_parsed);
|
2009-02-15 09:09:56 +00:00
|
|
|
|
|
|
|
m_buffer = tmp;
|
2009-02-15 09:10:00 +00:00
|
|
|
m_used = not_parsed + COUNTER_SIZE;
|
2009-02-15 09:09:56 +00:00
|
|
|
m_free = next_size - m_used;
|
2009-02-15 09:10:00 +00:00
|
|
|
m_off = COUNTER_SIZE;
|
2009-02-15 09:09:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool unpacker::execute()
|
|
|
|
{
|
2009-02-15 09:10:00 +00:00
|
|
|
int ret = as_ctx(m_ctx)->execute(m_buffer, m_used, &m_off);
|
2009-02-15 09:09:56 +00:00
|
|
|
if(ret < 0) {
|
|
|
|
throw unpack_error("parse error");
|
2009-02-15 09:09:56 +00:00
|
|
|
} else if(ret == 0) {
|
|
|
|
return false;
|
2009-02-15 09:09:56 +00:00
|
|
|
} else {
|
2009-02-15 09:09:56 +00:00
|
|
|
return true;
|
2009-02-15 09:09:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-15 09:09:58 +00:00
|
|
|
zone* unpacker::release_zone()
|
|
|
|
{
|
2009-02-15 09:10:00 +00:00
|
|
|
m_zone->push_finalizer(decl_count, m_buffer);
|
|
|
|
incr_count(m_buffer);
|
|
|
|
|
|
|
|
//std::auto_ptr<zone> old(new zone());
|
|
|
|
//m_zone.swap(old);
|
2009-02-15 09:09:58 +00:00
|
|
|
zone* n = new zone();
|
|
|
|
std::auto_ptr<zone> old(m_zone.release());
|
|
|
|
m_zone.reset(n);
|
|
|
|
|
2009-02-15 09:10:00 +00:00
|
|
|
as_ctx(m_ctx)->set_zone(m_zone.get());
|
2009-02-15 09:09:58 +00:00
|
|
|
|
|
|
|
return old.release();
|
|
|
|
}
|
|
|
|
|
2009-02-15 09:09:56 +00:00
|
|
|
object unpacker::data()
|
|
|
|
{
|
2009-02-15 09:10:00 +00:00
|
|
|
return as_ctx(m_ctx)->data();
|
2009-02-15 09:09:56 +00:00
|
|
|
}
|
|
|
|
|
2009-02-15 09:09:58 +00:00
|
|
|
void unpacker::reset()
|
2009-02-15 09:09:56 +00:00
|
|
|
{
|
2009-02-15 18:39:30 +09:00
|
|
|
//if(!m_zone->empty()) { delete release_zone(); }
|
2009-02-15 09:10:00 +00:00
|
|
|
as_ctx(m_ctx)->reset();
|
2009-02-15 09:09:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-15 09:09:57 +00:00
|
|
|
object unpacker::unpack(const char* data, size_t len, zone& z, size_t* off)
|
2009-02-15 09:09:56 +00:00
|
|
|
{
|
2009-02-15 09:10:00 +00:00
|
|
|
context ctx;
|
|
|
|
ctx.set_zone(&z);
|
2009-02-15 09:09:57 +00:00
|
|
|
if(off) {
|
2009-02-15 09:10:00 +00:00
|
|
|
size_t noff = *off;
|
|
|
|
int ret = ctx.execute(data, len, &noff);
|
2009-02-15 09:09:57 +00:00
|
|
|
if(ret < 0) {
|
|
|
|
throw unpack_error("parse error");
|
|
|
|
} else if(ret == 0) {
|
|
|
|
throw unpack_error("insufficient bytes");
|
|
|
|
}
|
2009-02-15 09:10:00 +00:00
|
|
|
*off = noff;
|
2009-02-15 09:09:57 +00:00
|
|
|
} else {
|
|
|
|
size_t noff = 0;
|
|
|
|
int ret = ctx.execute(data, len, &noff);
|
|
|
|
if(ret < 0) {
|
|
|
|
throw unpack_error("parse error");
|
|
|
|
} else if(ret == 0) {
|
|
|
|
throw unpack_error("insufficient bytes");
|
|
|
|
} else if(noff < len) {
|
|
|
|
throw unpack_error("extra bytes");
|
|
|
|
}
|
2009-02-15 09:09:56 +00:00
|
|
|
}
|
|
|
|
return ctx.data();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace msgpack
|
|
|
|
|