dpzo13732
2018-12-13 20:49
浏览 80
已采纳

Golang中的对齐

I am implementing a network packet in golang. It was already implemented in C++. The purpose is to make the golang implemented client communicate with C++ implemented server.

They will communicate by packet. The packet struct is:

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

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

Their underlined structure is byte array. When receiving a message in byte array format. We need to translate it into Packet.

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

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

To make them communicate, their struct alignment should keep the same.

Here comes the question: After printing out their alignment, I get this:

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
}

They will decode the message differently due to the alignment is different.

I can not change the C++ code.

Q1: Is there any way to set struct fields alignment in golang?

Q2: Is there a better way to implement golang Packet to avoid alignment mismatch when casting buffer into packet?

  • 写回答
  • 好问题 提建议
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • doufei1988 2018-12-24 20:33
    已采纳

    According to Adrian and Volker's comment,

    Q1: No

    Q2: Do some programming to unpack fields individually.

    In encoding/binary package, we have

    func PutUVarint
    

    It encodes a uint64 into buf and returns the number of bytes written. Somehow this package dont have public function to encode uint32. So I did something similar:

    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)
    
    已采纳该答案
    评论
    解决 无用
    打赏 举报
  • doumeng3345 2019-04-16 15:18

    You can do the same as c/c++ in an unsafe manner.

    Just use an oversized fixed width byte array.

    With that said you need to do your own bounds check.

    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
    ", p.length, 
        p.nameLen, p.data[0:p.nameLen] )
    }
    
    评论
    解决 无用
    打赏 举报

相关推荐 更多相似问题