gomemoryalignment

Alignment in 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?


Solution

  • 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)