Выравнивание в Голанге - PullRequest
       4

Выравнивание в Голанге

0 голосов
/ 13 декабря 2018

Я реализую сетевой пакет на Голанге.Это уже было реализовано в C ++.Цель состоит в том, чтобы заставить реализованный клиент golang связываться с внедренным сервером C ++.

Они будут общаться пакетами.Структура пакета:

type Packet struct {
    length   uint32
    nameLen  uint8
    data     []byte
} // in golang

struct Packet {
    uint32_t length;
    uint8_t  nameLen;
    byte     data[];
} // in C++

Подчеркнутая структура - это байтовый массив.При получении сообщения в формате байтового массива.Нам нужно перевести его в пакет.

auto p = reinterpret_cast<Packet*>(buffer); // in c++
(buffer's alignment is manually set as 64)

p := (Packet)(unsafe.Pointer(&buffer)) // in golang

Чтобы заставить их общаться, их выравнивание структуры должно оставаться прежним.

Здесь возникает вопрос: после распечатки их выравнивания я получаю это:

type Packet struct {
    length  uint32 // alignment 8
    nameLen uint8  // alignment 8
    data    []byte // alignment 8
}
struct Packet {
    uint32_t length;  // alignment 4
    uint8    nameLen; // alignment 4
    data     []byte;  // alignment 1
}

Они будут декодировать сообщение по-разному из-за различного выравнивания.

Я не могу изменить код C ++.

Q1: Есть лиЕсть ли способ установить выравнивание структурных полей в golang?

Q2: Есть ли лучший способ реализовать пакет golang, чтобы избежать несоответствия выравнивания при приведении буфера в пакет?

Ответы [ 2 ]

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

Вы можете сделать то же самое с c / c ++ небезопасным способом.

Просто используйте слишком большой байтовый массив фиксированной ширины.

При этом вам необходимо выполнить собственную проверку границ.

package main

import (
    "unsafe"
    "runtime"
    "reflect"
    "fmt"
)

type Packet struct {
    length  uint32     // alignment 4
    nameLen uint8      // alignment 1
    data    [2048]byte // alignment 1
}

func NewPacketFromBuffer(buffer []byte) *Packet {
    sh := (*reflect.SliceHeader)(unsafe.Pointer(&buffer))
    packet := (*Packet)(unsafe.Pointer(sh.Data))
    runtime.KeepAlive(buffer) // So the buffer will not be garbage collected
    return packet
}


func main () {
   buffer := []byte{9,0,0,0,4,'d','a','t','a'};
   p := NewPacketFromBuffer(buffer)
   fmt.Printf("length : %d, nameLen : %d, name: %s\n", p.length, 
    p.nameLen, p.data[0:p.nameLen] )
}
0 голосов
/ 24 декабря 2018

Согласно комментарию Адриана и Фолькера,

Q1: Нет

Q2: Выполните программирование для распаковки полей по отдельности.

В кодировке / двоичном пакете мы имеем

func PutUVarint

Он кодирует uint64 в buf и возвращает количество записанных байтов.Каким-то образом этот пакет не имеет публичной функции для кодирования uint32.Итак, я сделал нечто подобное:

func PutUint32(b []byte, v uint32) {
    _ = b[3] // early check
    b[0] = byte(v)
    b[1] = byte(v >> 8)
    b[2] = byte(v >> 16)
    b[3] = byte(v >> 24)
} // assume it's littleEndian

// to store packet length into buffer.
PutUint32(buffer, pkt.length)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...