簡體   English   中英

大多數基本類型的高效二進制序列化

[英]Efficient Binary Serialization Of Mostly Basic Types

我試圖找出通過網絡傳輸一些數據的最佳方法。 這是我希望實現的目標:

應用程序運行並計算一些數據:

int w = 5;
float x = 4.736;
std::string y = "Some String.";
std::vector<int> z;
z.push_back(1);
z.push_back(2);
z.push_back(3);

然后我們把它放在一個二進制容器中:

BinaryContainer Data;
Data.Write(w);
Data.Write(x);
Data.Write(y);
Data.Write(z);

然后我們通過網絡傳輸它:

SendData(Data.c_str());

並在另一邊讀出:

BinaryContainer ReceivedData(IncomingData);
int w = ReceivedData.Read();
float x = ReceivedData.Read();
std::string y = ReceivedData.Read();
std::vector<int> z = ReceivedData.Read();

上面的示例從高級角度概述了基本功能應該如何工作。 我查看了許多不同的序列化庫,但似乎沒有一個非常合適。 我傾向於學習如何自己編寫功能。

字節序無關緊要。 讀取和寫入數據的架構永遠不會不同。 我們只需要在容器內存儲二進制數據。 讀取應用程序和寫入應用程序專門負責以與寫入相同的順序讀取數據。 只需要編寫基本類型,不需要完整的任意類或指向事物的指針。 最重要的是,發生這種情況的速度應該是最高優先級,因為一旦數據被公式化,我們需要將其寫入容器,通過網絡傳輸,並在另一端以盡可能快的速度讀取。

目前正在使用低級 WinSock RIO API 完成網絡傳輸,我們正在盡快將數據從應用程序移動到線路。 線路上的傳輸延遲將始終是更高且可變的速率。 我們在傳輸之前序列化數據的點是鏈中的下一步,以確保我們在將數據傳輸到網絡之前浪費盡可能少的時間。

將非常快地接收新數據包,因此預分配資源的能力將是有益的。 例如:

Serializer DataHandler;
...
void NewIncomingPacket(const char* Data)
{
    DataHandler.Reset();
    DataHandler.Load(Data);
    int x = DataHandler.Read();
    float y = DataHandler.Read();
    ...
}

我正在尋找社區專家的意見,告訴他們該往哪個方向發展。

我寫得很認真,一個只有頭文件的快速 C++ 庫,應該可以做你想做的 :-)

它提供了一個序列化器和一個反序列化器。

序列化數據可以跨不同架構和字節序移植。 沒有外部依賴。

    seriously::Packer<1024> packer;     // a 1024 byte serialization buffer

    int32_t value1 = 83656;
    bool value2 = true;
    int16_t value3 = -2345;
    std::string value4("only an example");
    double value5 = -6.736;
    std::vector<int64_t> value6;

    value6.push_back(42);
    value6.push_back(11);
    value6.push_back(93);

    packer << value1 << value2 << value3 << value4 << value5 << value6;

    std::cout << "packed size: " << packer.size() << std::endl;
    // packer.data() contains the serialized data

    int32_t restored1;
    bool restored2;
    int16_t restored3;
    std::string restored4;
    double restored5 = -6.736;
    std::vector<int64_t> restored6;

    packer >> restored1 >> restored2 >> restored3 >> restored4 >> restored5 >> restored6;

    std::cout << "unpacked: " << restored1 << " " << (restored2 ? "t" : "f") << " " << restored3 << " " << restored4 << " " << restored5 << std::endl;

    std::vector<int64_t>::const_iterator it;
    for (it = restored6.begin(); it != restored6.end(); it++) {
        std::cout << *it << std::endl;
    }

如果您不關心字節序並且只想序列化簡單的類型,那么簡單的 memcpy 將是最快且安全的。 序列化/反序列化時,只需 memcpy 進入/退出緩沖區。

#include <iostream>
#include <vector>
#include <cstring>
#include <cstdint>
#include <type_traits>
#include <cstddef>

template <std::size_t CapacityV>
struct BinaryContainer
{
    BinaryContainer() :
        m_write(0),
        m_read(0)
    {
    }

    template <typename T>
    void write(const std::vector<T>& vec)
    {
        static_assert(std::is_trivial_v<T>);

        // TODO: check if access is valid

        const std::size_t bytes = vec.size() * sizeof(T);
        std::memcpy(m_buffer + m_write, vec.data(), bytes);
        m_write += bytes;
    }

    template <typename T>
    void write(T value)
    {
        static_assert(std::is_trivial_v<T>);

        // TODO: check if access is valid

        const std::size_t bytes = sizeof(T);
        std::memcpy(m_buffer + m_write, &value, bytes);
        m_write += bytes;
    }

    template <typename T>
    std::vector<T> read(std::size_t count)
    {
        static_assert(std::is_trivial_v<T>);

        // TODO: check if access is valid

        std::vector<T> result;
        result.resize(count);

        const std::size_t bytes = count * sizeof(T);
        std::memcpy(result.data(), m_buffer + m_read, bytes);
        m_read += bytes;

        return result;
    }

    template <typename T>
    T read()
    {
        static_assert(std::is_trivial_v<T>);

        // TODO: check if access is valid

        T result;

        const std::size_t bytes = sizeof(T);
        std::memcpy(&result, m_buffer + m_read, bytes);
        m_read += bytes;

        return result;
    }

    const char* data() const
    {
        return m_buffer;
    }

    std::size_t size() const
    {
        return m_write;
    }

private:
    std::size_t m_write;
    std::size_t m_read;
    char m_buffer[CapacityV]; // or a dynamically sized equivalent
};

int main()
{

    BinaryContainer<1024> cont;

    {
        std::vector<std::uint32_t> values = {1, 2, 3, 4, 5};
        // probably want to make serializing size part of the vector serializer
        cont.write(values.size());
        cont.write(values);
    }

    {
        auto size = cont.read<std::vector<std::uint32_t>::size_type>();
        auto values = cont.read<std::uint32_t>(size);

        for (auto val : values) std::cout << val << ' ';
    }
}

演示: http : //coliru.stacked-crooked.com/a/4d176a41666dbad1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM