Search code examples
cgostructpack

golang: pack a struct


Please consider this sample go code:

package main

//#include <stdio.h>
//#include <stdint.h>
//#pragma pack(push, 1)
//struct Packed_Struct {
//  uint16_t A;
//  uint16_t B;
//  uint32_t C;
//  uint16_t D;
//};
//#pragma pack(pop)
//
//struct UnPacked_Struct {
//  uint16_t A;
//  uint16_t B;
//  uint32_t C;
//  uint16_t D;
//};
//
//
//void print_C_struct_size(){
//  struct Packed_Struct Packed_Struct;
//  struct UnPacked_Struct UnPacked_Struct;
//  printf("Sizeof Packed_Struct: %lu\n", sizeof(Packed_Struct) );
//  printf("Sizeof UnPacked_Struct: %lu\n", sizeof(UnPacked_Struct) );
//  return;
//}
//
import "C"



import(
    "fmt"
    "unsafe"
)

type GoStruct struct{
    A   uint16
    B   uint16
    C   uint32
    D   uint16
}


func main(){
    fmt.Println("Hello world!")
    meh := C.print_C_struct_size()
    var GoStruct GoStruct
    fmt.Printf("Sizeof GoStruct : %d\n", unsafe.Sizeof(GoStruct) ) 
    fmt.Printf("meh type: %T\n", meh)
}

The output is:

$ go run cgo.go 
Hello world!
Sizeof Packed_Struct: 10
Sizeof UnPacked_Struct: 12
Sizeof GoStruct : 12
meh type: main._Ctype_void

Notice that the struct when packed takes 10 bytes, 12 otherwise. By default, my tries in Go seems to take 12 bytes as shown here.

Is there a Go-ish way to pack this struct so it uses only 10 bytes?


Solution

  • So, per https://github.com/golang/go/wiki/cgo#struct-alignment-issues:

    Go doesn't support packed struct (e.g., structs where maximum alignment is 1 byte), you can't use packed C struct in Go. Even if your program passes compilation, it won't do what you want. To use it, you have to read/write the struct as byte array/slice.

    From "what" I understood, I've put together this: https://play.golang.org/p/OmdMVDMikSn.

    Essentially, set up the struct, and binary.Write it into a byte slice, the slice shows 10 bytes.

    package main
    
    import(
            "fmt"
            "bytes"
            "encoding/binary"
            "unsafe"
    )
    
    type SomeStruct struct{
        A   uint16
        B   uint16
        C   uint32
        D   uint16
    }
    
    func main(){
            var myStruct SomeStruct
            myStruct.A = 1
            myStruct.B = 2
            myStruct.C = 3
            myStruct.D = 4
            buf := &bytes.Buffer{}
            err := binary.Write(buf, binary.LittleEndian, myStruct)
            fmt.Printf("Error: %v\n", err)
            fmt.Printf("Sizeof myStruct: %d, Sizeof buf: %d, Len of buf: %d\n", unsafe.Sizeof(myStruct), unsafe.Sizeof(buf), buf.Len() )
    }