Может ли протокулярное маршальное сообщение перейти к уже выделенному байтовому массиву без копирования? - PullRequest
0 голосов
/ 15 января 2019

Я осуществляю взаимодействие клиент-сервер с помощью пакетов, определенных пользователем. Я использую Go net.conn. Он может набирать схемы tcp / unix, что очень удобно. Я использую protocol buffer для определения своих сообщений.

Я определил пакет, который содержит length и buffer

type Packet struct {
    length uint32
    buffer []byte
}

Функция API выглядит следующим образом:
func(api *API) Send(m *proto.Message) error
func(api *API) Receive(p *Packet) error

Возьмем функцию send в качестве примера, она принимает сообщение protobuf, преобразует его в Packet. И напиши это в net.conn.

Вот упрощенная версия функции отправки:

func(api *API) Send(m *proto.Message) error {
    bytes, err := proto.Marshal(m)
    if err != nil {
        return err
    }
    buffer := api.packet[:length]
    copy(buffer, bytes)
    _, err := api.conn.Write(buffer)
    if err != nil {
        return err
    }
    return nil
}

Я копировал bytes в buffer. Поскольку API буфера протокола Go предоставляет только
func Marshal(pb Message) ([]byte, error)

В буфере протокола C ++ это обеспечивает bool SerializeToArray(void * data, int size) const, который сериализует сообщение и сохраняет его в заданном байтовом массиве. Но я не могу найти то же самое в API буфера протокола Go.

Есть ли способ избежать копирования, если я хочу напрямую сохранить сериализованный результат в заданном байтовом массиве?

Ответы [ 3 ]

0 голосов
/ 16 января 2019

Кажется, вы можете сделать Packet.buffer, чтобы быть proto.Buffer

type Packet struct {
    length uint32
    buffer proto.Buffer
}
...
var packet Packet
packet.length = YouLength
packet.buffer = proto.NewBuffer(make([]byte, YouLength))
//Then you can Marshall in Packet directly and it  may be reused.
err := packet.Marshal(message)
0 голосов
/ 06 июня 2019

Вы ищете MarshalTo метод из gogo/protobuf, еще одну реализацию protobuf, совместимую с оригиналом.

Вы можете повторно использовать один и тот же буфер через несколько вызовов маршала, когда вы передаете ему буфер для заполнения. Очевидно, буфер должен быть достаточно большим.

func MarshalTo([]byte, m) error 
0 голосов
/ 16 января 2019

Непонятно, о чем вы спрашиваете. Обратите внимание, что функция proto Marshal () делает именно то, что вам нужно: она сериализует сообщение в байтовый фрагмент (что вы, вероятно, подразумеваете под байтовым массивом)

Посмотрите, может ли какая-либо из этих справок:

func(api *API) Send(m *proto.Message) error {
    p := Packet{}
    p.buffer, err := proto.Marshal(m)
    if err != nil {
        return err
    }
    _, err := api.conn.Write(p.buffer)
    if err != nil {
        return err
    }
    return nil
}

Или

func(api *API) Send(m *proto.Message) error {
    buffer := api.packet[:length]
    buffer, err := proto.Marshal(m)
    if err != nil {
        return err
    }
    _, err := api.conn.Write(buffer)
    if err != nil {
        return err
    }
    return nil
}
...