msgpack-c 官方文档整理翻译之unpack

原文地址

msgpack::unpacker

msgpack::unpacker 从包含 msgpack 格式数据的缓冲区中解包 msgpack::object。 msgpack 提供了两个解包功能。当客户端控制缓冲区时使用一个,当客户端不想控制缓冲区时使用另一个。这两种方法都使用 msgpack::object_handle 来访问未打包的数据。

Accessing unpacked data(访问解压数据)

当你调用解包函数时,你需要传递 msgpack::object_handle 作为参数。这是客户端的 msgpack::object_handle 接口:

class object_handle {
public:
    object_handle();
    const object& get() const;
    msgpack::unique_ptr<msgpack::zone>& zone();
};

For example:

void some_function(const char* data, std::size_t len) {
    // Default construct msgpack::object_handle
    msgpack::object_handle result;

     // Pass the msgpack::object_handle
    unpack(result, data, len);
    // Get msgpack::object from msgpack::object_handle (shallow copy)
    msgpack::object obj(result.get());
    // Get msgpack::zone from msgpack::object_handle (move)
    msgpack::unique_ptr<msgpack::zone> z(result.zone());
    ...

msgpack::object 具有引用语义。所以 msgpack::object 的拷贝构造和拷贝赋值是浅拷贝。 msgpack::object_handle 管理 msgpack::object 的生命周期。 msgpack::object 是在 msgpack::zone 的内部缓冲区上构造的。当 msgpack::object_handle 被销毁时,被销毁的 msgpack::object_handle 中的 msgpack::object 将成为一个悬空引用。如果要保持 msgpack::object 的生命周期,则需要保持 msgpack::object_handle 的生命周期如下:

msgpack::object_handle some_function(const char* data, std::size_t len) {
    // Default construct msgpack::object_handle
    msgpack::object_handle result;

    // Pass the msgpack::object_handle
    unpack(result, data, len);
    return result;
}

void foo() {
    ...
    msgpack::object_handle handle = some_function(data, len);
    // Use object
    msgpack::object obj = handle.get();

您还可以使用 zone() 从 msgpack::object_handle 获取 msgpack::zone。那是针对高级用例的。在典型的用例中,使用 msgpack::object_handle 并从中获取 msgpack::object 就足够了。

当您想精确控制一个缓冲存储器时,引用的参数很有用。解包后,如果解包 msgpack::object 引用缓冲区,则 referenced 返回 true,否则 referenced 为 false。当您调用 unpack() 时,缓冲区是您传递的参数。当你调用 unpacker::next() 时,缓冲区是 unpacker 的内部缓冲区。

Client controls a buffer

如果 msgpack 用户、客户端已经准备好包含 msgpack 格式数据的缓冲区,请使用以下函数:

// simple (since C++11)
object_handle unpack(
    const char* data,
    std::size_t len,
    unpack_reference_func f = nullptr,
    void* user_data = nullptr,
    unpack_limit const& limit = unpack_limit());

// get referenced information (since C++11)
object_handle unpack(
    const char* data,
    std::size_t len,
    bool& referenced,
    unpack_reference_func f = nullptr,
    void* user_data = nullptr,
    unpack_limit const& limit = unpack_limit());

// set and get offset information (since C++11)
object_handle unpack(
    const char* data,
    std::size_t len,
    std::size_t& off,
    unpack_reference_func f = nullptr,
    void* user_data = nullptr,
    unpack_limit const& limit = unpack_limit());

// get referenced information
// set and get offset information
//  (since C++11)
object_handle unpack(
    const char* data,
    std::size_t len,
    std::size_t& off,
    bool& referenced,
    unpack_reference_func f = nullptr,
    void* user_data = nullptr,
    unpack_limit const& limit = unpack_limit());
// simple
void unpack(
    object_handle& result,
    const char* data,
    std::size_t len,
    unpack_reference_func f = nullptr,
    void* user_data = nullptr,
    unpack_limit const& limit = unpack_limit());

// get referenced information
void unpack(
    object_handle& result,
    const char* data,
    std::size_t len,
    bool& referenced,
    unpack_reference_func f = nullptr,
    void* user_data = nullptr,
    unpack_limit const& limit = unpack_limit());

// set and get offset information
void unpack(
    object_handle& result,
    const char* data,
    std::size_t len,
    std::size_t& off,
    unpack_reference_func f = nullptr,
    void* user_data = nullptr,
    unpack_limit const& limit = unpack_limit());

// get referenced information
// set and get offset information
void unpack(
    object_handle& result,
    const char* data,
    std::size_t len,
    std::size_t& off,
    bool& referenced,
    unpack_reference_func f = nullptr,
    void* user_data = nullptr,
    unpack_limit const& limit = unpack_limit());
If the buffer cointains one msgpack data, just pass the buffer address and length:

object_handle result;
const char* data = ...
std::size_t len = ...

unpack(result, data, len);
result = unpack(data, len); // since C++11

如果缓冲区包含多个 msgpack 数据,请使用以下函数:

object_handle result;
const char* data = ...
std::size_t len = ...
std::size_t off = ...
unpack(result, data, len, off);
result = unpack(data, len, off); // since C++11

如果知道msgpack数据的个数,可以这样写:

// Caller knows there are three msgpack data in the buffer.
void some_function(const char* buffer, std::size_t len) {
    msgpack::object_handle result1;
    msgpack::object_handle result2;
    msgpack::object_handle result3;
    std::size_t off = 0;

    // Unpack the first msgpack data.
    // off is updated when function is returned.
    unpack(result1, buffer, len, off);
    // Unpack the second msgpack data.
    // off is updated when function is returned.
    unpack(result2, buffer, len, off);
    // Unpack the third msgpack data.
    // off is updated when function is returned.
    unpack(result3, buffer, len, off);
    assert(len == off);
    ...

如果不知道msgpack数据的个数但是数据完整,可以这样写:

void some_function(const char* buffer, std::size_t len) {
    std::size_t off = 0;

    while (off != len) {
        msgpack::object_handle result;
        unpack(result, buffer, len, off);
        msgpack::object obj(result.get());
        // Use obj
    }
}

那些 msgpack::unpack 函数可能会抛出 msgpack::unpack_error。

  • 如果缓冲区的 msgpack 数据格式无效,则会收到带有“parse error”消息的 msgpack::unpack_error。
  • 如果来自 off(offset) 位置的缓冲区包含不完整的 msgpack 数据,则会收到带有“insufficient bytes”消息的 msgpack::unpack_error。

为了使用这些解包功能,客户端需要准备好几个完整的msgpack数据。如果客户端可能得到不完整的 msgpack 数据,则使用 msgpack::unpacker。

Limit size of elements

您可以将 unpack_limit 传递给 unpack()。

这是 unpack_limit 的定义:

class unpack_limit {
public:
    unpack_limit(
        std::size_t array = 0xffffffff,
        std::size_t map = 0xffffffff,
        std::size_t str = 0xffffffff,
        std::size_t bin = 0xffffffff,
        std::size_t ext = 0xffffffff,
        std::size_t depth = 0xffffffff);
    std::size_t array() const;
    std::size_t map() const;
    std::size_t str() const;
    std::size_t bin() const;
    std::size_t ext() const;
    std::size_t depth() const;
};

默认限制为 0xffffffff。它们是 msgpack 格式的最大值。严格来说,深度的限制不是由 msgpack 格式定义的。

如果元素数量超过 unpack_limit,则 unpack 函数会抛出以下异常:

struct size_overflow : public unpack_error {
};

struct array_size_overflow : public size_overflow {
};

struct map_size_overflow : public size_overflow {
};

struct str_size_overflow : public size_overflow {
};

struct bin_size_overflow : public size_overflow {
};

struct ext_size_overflow : public size_overflow {
};

struct depth_size_overflow : public size_overflow {
};

您可以保护您的应用程序免受格式错误的 msgpack 格式数据的影响。

See https://github.com/msgpack/msgpack-c/blob/master/test/limit.cpp

Memory management

默认情况下,客户端缓冲区中的所有 msgpack 数据都将复制到 msgpack::zone。您可以在解包后释放包含 msgpack 格式数据的缓冲区。

cli_def

您可以使用 unpack_reference_func 自定义行为。该函数在 STR、BIN 和 EXT 解包时调用。如果函数返回true,则对应于STR、BIN和EXT的msgpack::object指向客户端缓冲区。所以客户端需要保持缓冲区的生命周期至少直到 msgpack::object 被销毁。如果函数返回 false,则 msgpack::object 指向从客户端缓冲区复制的 msgpack::zone 上的 STR、BIN 和 EXT 有效负载。

unpack_reference_func 定义如下:

typedef bool (*unpack_reference_func)(type::object_type type, std::size_t length, void* user_data);

type::object_type is defined as follows:

namespace type {
    enum object_type {
        NIL                 = MSGPACK_OBJECT_NIL,
        BOOLEAN             = MSGPACK_OBJECT_BOOLEAN,
        POSITIVE_INTEGER    = MSGPACK_OBJECT_POSITIVE_INTEGER,
        NEGATIVE_INTEGER    = MSGPACK_OBJECT_NEGATIVE_INTEGER,
        DOUBLE              = MSGPACK_OBJECT_DOUBLE,
        STR                 = MSGPACK_OBJECT_STR,
        BIN                 = MSGPACK_OBJECT_BIN,
        ARRAY               = MSGPACK_OBJECT_ARRAY,
        MAP                 = MSGPACK_OBJECT_MAP,
        EXT                 = MSGPACK_OBJECT_EXT
    };
}

当 msgpack 从缓冲区解包对象时,unpack_reference_func 被调用,‘type’ 作为解包类型,‘length’ 作为有效负载大小,以及’user_data’ 这三个作为参数传递给 msgpack::unpack()。您可以将 STR、BIN 和 EXT 作为类型参数。其他类型永远不会出现,因为具有其他类型的 msgpack::object 是通过转换创建的。相反,STR、BIN 和 EXT 的有效载荷可以直接从 msgpack::object 使用。

“size”表示有效负载大小。 STR 和 BIN 的有效载荷大小,它是实际数据大小。对于 EXT,它是 EXT 的类型和数据大小。

这是 unpack_reference_func 的示例实现。

bool my_reference_func(type::object_type type, std::size_t length, void* user_data) {
    switch (type) {
    case: type::STR:
        // Small strings are copied.
        if (length < 32) return false;
        break;
    case: type::BIN:
        // BIN is always copied.
        return false;
    case: type::EXT:
        // fixext's are copied.
        switch (length) {
        case  1+1:
        case  2+1:
        case  4+1:
        case  8+1:
        case 16+1:
            return false;
        }
        break;
    default:
        assert(false);
    }
    // otherwise referenced.
    return true;
}

当字符串大小等于或大于 32bytes 时,内存布局如下:

cli_func

您可以使用以下函数获取解压后的 msgpack::object 是否引用缓冲区:

// get referenced information
void unpack(
    object_handle& result,
    const char* data,
    std::size_t len,
    bool& referenced,
    unpack_reference_func f = nullptr,
    void* user_data = nullptr,
    unpack_limit const& limit = unpack_limit());

// get referenced information
// set and get offset information
void unpack(
    object_handle& result,
    const char* data,
    std::size_t len,
    std::size_t& off,
    bool& referenced,
    unpack_reference_func f = nullptr,
    void* user_data = nullptr,
    unpack_limit const& limit = unpack_limit());

如果 referenced 为真,则 msgpack::object 引用作为参数 ‘data’ 传递的缓冲区,否则 msgpack::object 不引用缓冲区。

msgpack controls a buffer

msgpack 提供了一个名为 msgpack::unpacker 的缓冲区管理功能。 msgpack::unpacker 适用于以下动机:

  • msgpack 数据被截断,客户端不知道什么时候完成。这是您开发流式应用程序时的典型情况。
  • 您希望在不仔细管理内存的情况下最大限度地减少复制操作。

这是 msgpack::unpacker 的基本(不是全部)接口:

#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

class unpacker {
public:
    unpacker(unpack_reference_func f = &unpacker::default_reference_func,
             void* user_data = nullptr,
             std::size_t init_buffer_size = MSGPACK_UNPACKER_INIT_BUFFER_SIZE,
             unpack_limit const& limit = unpack_limit());
    void reserve_buffer(std::size_t size = MSGPACK_UNPACKER_RESERVE_SIZE);
    char* buffer();
    void buffer_consumed(std::size_t size);
    bool next(object_handle& result);
};

这是使用 msgpack::unpacker 的基本模式:

// The size may decided by receive performance, transmit layer's protocol and so on.
std::size_t const try_read_size = 100;

msgpack::unpacker unp;

// Message receive loop
while (/* block until input becomes readable */) {
    unp.reserve_buffer(try_read_size);
    // unp has at least try_read_size buffer on this point.

    // input is a kind of I/O library object.
    // read message to msgpack::unpacker's internal buffer directly.
    std::size_t actual_read_size = input.readsome(unp.buffer(), try_read_size);

    // tell msgpack::unpacker actual consumed size.
    unp.buffer_consumed(actual_read_size);

    msgpack::object_handle result;
    // Message pack data loop
    while(unp.next(result)) {
        msgpack::object obj(result.get());
        // Use obj
    }
    // All complete msgpack message is proccessed at this point,
    // then continue to read addtional message.
}

如果处理了一个完整的 msgpack 消息,则 msgpack::unpacker::next() 返回 true。如果 msgpack 消息正确但不充分,则返回 false。但是,解析过程继续进行,上下文信息保存在 msgpack::unpacker 中。它有助于平衡解析的负载。

当 msgpack 消息包含二进制数据、字符串数据或 ext 数据时,它们不会被复制,而是默认从 msgpack::object 引用。请参阅以下实现:

inline bool unpacker::default_reference_func(type::object_type type, uint64_t len, void*)
{
    return true;
}

您还可以自定义 unpack_reference_func。即使你使用引用,你也不需要控制缓冲区的生命周期。缓冲区的生命周期由 msgpack 使用 msgpack::zone 的 finalizer_array 和 msgpack::unpacker 的引用计数机制来控制。

因此,在大多数情况下,默认行为就足够了。如果您想在接收 msgpack 数据模式可预测时控制内存消耗的峰值,自定义 unpack_reference_func 可能会很有用。

您可以使用以下函数从 msgpack::unpacker::next() 获取参考信息:

    bool next(object_handle& result, bool& referenced);

但是,大多数情况下您不需要使用该版本的 next(),因为引用的内存由 unpacker 管理。

unpacker

unpacker behavior

如果您想详细了解内部机制,请参阅内部机制,幻灯片是在未实现 ext 支持和 unpack_reference_func 时编写的。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/v1/object.hpp:664:34: error: ‘void* memcpy(void*, const void*, size_t)’ copying an object of non-trivial type ‘struct msgpack::v2::object’ from an array of ‘const msgpack_object’ {aka ‘const struct msgpack_object’} [-Werror=class-memaccess] std::memcpy(&o, &v, sizeof(v)); ^ In file included from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/object_fwd.hpp:17, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/v1/adaptor/adaptor_base_decl.hpp:14, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/adaptor/adaptor_base_decl.hpp:13, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/adaptor/adaptor_base.hpp:13, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/v1/object_decl.hpp:16, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/object_decl.hpp:14, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/object.hpp:13, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack.hpp:10, from /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/example/cpp03/stream.cpp:10: /home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master/include/msgpack/v2/object_fwd.hpp:23:8: note: ‘struct msgpack::v2::object’ declared here struct object : v1::object { ^~~~~~ cc1plus: all warnings being treated as errors make[2]: *** [example/cpp03/CMakeFiles/stream.dir/build.make:63:example/cpp03/CMakeFiles/stream.dir/stream.cpp.o] 错误 1 make[2]: 离开目录“/home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master” make[1]: *** [CMakeFiles/Makefile2:415:example/cpp03/CMakeFiles/stream.dir/all] 错误 2 make[1]: 离开目录“/home/AQTJClient/AQTJAuditClient/depends/msgpack-c-master”
07-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值