Eric Lagergren
Eric Lagergren

Reputation: 511

Why isn't binary.Read() reading integers correctly?

I'm trying to read a binary file in Go.

Essentially I have a struct like this:

type foo struct {
    A int16
    B int32
    C [32]byte
    // and so on...
}

and I'm reading from the file into the struct like this:

fi, err := os.Open(fname)
// error checking, defer close, etc.
var bar foo
binary.Read(fi, binary.LittleEndian, &bar)

Now, that should work, but I'm getting some weird results. For instance, when I read into the struct I should get this:

A: 7
B: 8105
C: // some string

but what I get is this:

A: 7
B: 531169280
C: // some correct string

The reason for this is because when binary.Read() is reading the file, after it reads the []byte{7, 0} as int16(7) (the correct value for A), it comes across the slice []byte{0, 0, 169, 31} and tries to convert it into an int32. However, binary.Read()'s conversion does this:

uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 where b is the byte slice.

But what really confuses me is doing the exact same thing in C works perfectly fine.

If I write this in C:

int main()
{
    int fd;
    struct cool_struct {
        short int A;
        int32_t B;
        char C[32];
        // you get the picture...
    } foo;
    int sz = sizeof(struct cool_struct);
    const char* file_name = "/path/to/my/file"

    fd = open(file_name, O_RDONLY);
    // more code
    read(fd, &foo, sz);
    // print values
}

I get the correct results. Why is my C code getting this correct while my Go code isn't?

Upvotes: 1

Views: 116

Answers (2)

user3629249
user3629249

Reputation: 16540

given:

0000000: 0700 0000 a91f 0000 7074 732f 3300 0000 ........pts/3...

the fields, per the struct, are:
0700h that will be the short int field, little endian format =  7

0000a91fh that will be the  int field, little endian format = the big number
...

your struct needs a second short field to absorb the 0000h
then 
0700h = 7
0000h = 0 in new field
a91f0000 = 8105
....

which indicates (amongst other things) that the struct is missing 
the expected 2 byte padding between the short and the int fields
does the C code have #pragma pack?

Upvotes: 1

Jasen
Jasen

Reputation: 12412

Assuming the first two characters of the string aren't '\000'

what you've got there is an alignment problem, your C compiler is putting an extra two bytes of padding after the int16, Go isn't

easiest fix is probably just to add a dummy (padding) int16 after 'A'

type foo struct 
{
    A int16
    A_pad int16
    B int32
    C [32]byte
}

or the may be a way to tell go that the int32 needs to be "4-byte aligned"

if you know of one please edit this answer or post a comment

Upvotes: 6

Related Questions