Упакованные структуры в (gcc) go - PullRequest
8 голосов
/ 13 июля 2011

У меня есть старый C-код, который довольно интенсивно использует упакованные структуры. Я пытаюсь использовать Go в качестве оболочки для этого кода, но мне сложно найти способ передать или даже написать определения для этих структур.

Пример:

import "unsafe";

type AlignTest struct {
    c byte;
    y int16;
    z int16;
    q int32;
}

func main() {

    vr := new(AlignTest);

    fmt.Println(unsafe.Sizeof(*vr),  "\n");

}

Возвращает 12, а не 1 + 2 + 2 + 4 = 9, который я хотел бы получить с упакованной / невыровненной структурой.

Я знаю, что мог бы просто создать байтовый массив и выполнить синтаксический анализ вручную, но это кажется очень хрупким и подверженным ошибкам ...

Ответы [ 4 ]

5 голосов
/ 13 июля 2011

Вы можете попробовать что-то вроде этого.

package main

import (
    "encoding/binary"
    "bytes"
    "fmt"
)

type Unpacked struct {
    C byte
    Y int16
    Z int16
    Q int32
}

type Packed struct {
    B [9]byte
}

func main() {
    var u Unpacked
    var p Packed
    var buf = bytes.NewBuffer(make([]byte, 0, len(p.B)))
    // Unpacked to Packed 
    u = Unpacked{1, 2, 3, 4}
    if err := binary.Write(buf, binary.BigEndian, &u); err != nil {
        fmt.Println(err)
    }
    if err := binary.Read(buf, binary.BigEndian, &p); err != nil {
        fmt.Println(err)
    }
    fmt.Println("u", u, "to", "p", p)
    // Packed to Unpacked
    p = Packed{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9}}
    if err := binary.Write(buf, binary.BigEndian, &p); err != nil {
        fmt.Println(err)
    }
    if err := binary.Read(buf, binary.BigEndian, &u); err != nil {
        fmt.Println(err)
    }
    fmt.Println("p", p, "to", "u", u)
}

.

Output:
u {1 2 3 4} to p {[1 0 2 0 3 0 0 0 4]}
p {[1 2 3 4 5 6 7 8 9]} to u {1 515 1029 101124105}
5 голосов
/ 08 ноября 2012

Возможно, вы захотите переосмыслить свою архитектуру - попробуйте передать бинарный ввод на уровень C и использовать существующие структуры (вы не нарушите то, что не изменили). Я предполагаю, что структура упаковки выглядит примерно так:

#ifdef WINDOWS
#pragma pack(push)
#endif
#pragma pack(BYTEALIGNMENT) // e.g. "#pragma pack(1)" or "#pragma pack(8)"

//--- Some packed structs

#ifdef WINDOWS
#pragma pack(pop)
#endif
#ifdef POSIX
#pragma pack()
#endif

Затем все нижележащие или сторонние библиотеки берут некоторый void * или const char * и типизируют его им. Поэтому, если возможно, попробуйте перенаправить эти данные в слой C (где вы можете получить указатели) и вообще не раскрывать структуры.

2 голосов
/ 13 июля 2011

Нет способа указать gccgo компилировать упакованные структуры.Лучшее решение, которое я могу придумать, - это вручную добавить отступы:

type AlignTest struct {
    c byte
    _ [3]byte // anonymous padding
    y int16
    z int16
    q int32
}
1 голос
/ 22 августа 2015

Это работает:

package main

import "unsafe"
import "fmt"

/*
struct test {
    char c;
    short i;
    short j;
    int k;
} __attribute__((packed));
*/
import "C"

type AlignTest struct {
    c byte
    y int16
    z int16
    q int32
}

func main() {

    vr := new(AlignTest)
    v := new(C.struct_test)

    fmt.Println(unsafe.Sizeof(*vr), "\n")
    fmt.Println(unsafe.Sizeof(*v), "\n")

}

Выход:

12

9
...