Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang - Compute field index based on address

Tags:

go

Is it safe to compute a structure field index based on its address offset (platform/compiler independent)?

type T struct {
    F1 int
    F2 int
    F3 string
    F4 float64
    F5 bool
}

t := new(T)
    t.F1 = 1
    t.F2 = 2
    t.F3 = "hello wordl!"
    t.F4 = 1234.7283823
    t.F5 = true

    fmt.Println("T address:", &t)
    fmt.Println("t.F1 address:", &t.F1, "size: ", unsafe.Sizeof(t.F1), "offset: ", unsafe.Offsetof(t.F1), "value: ", t.F1)
    fmt.Println("t.F2 address:", &t.F2, "size: ", unsafe.Sizeof(t.F2), "offset: ", unsafe.Offsetof(t.F2), "value: ", t.F2)
    fmt.Println("t.F3 address:", &t.F3, "size: ", unsafe.Sizeof(t.F3), "offset: ", unsafe.Offsetof(t.F3), "value: ", t.F3)
    fmt.Println("t.F4 address:", &t.F4, "size: ", unsafe.Sizeof(t.F4), "offset: ", unsafe.Offsetof(t.F4), "value: ", t.F4)
    fmt.Println("t.F5 address:", &t.F5, "size: ", unsafe.Sizeof(t.F5), "offset: ", unsafe.Offsetof(t.F5), "value: ", t.F5)

Edit

Maybe the offset word makes the question a bit confusing. When the above code is run the address space is growing. What I want to know exaclty is if the struct fields are allocated in the order of their definition.

like image 797
Mihai H Avatar asked Feb 12 '23 18:02

Mihai H


2 Answers

"Is it safe" well, let me stop you there. You're using a package called unsafe.

"Can this generally be expected to work assuming no radical changes are made to things like the Garbage Collector?" Yeah, probably.

From the unsafe documentation for OffsetOf:

Offsetof returns the offset within the struct of the field represented by v, which must be of the form structValue.field. In other words, it returns the number of bytes between the start of the struct and the start of the field.

In other words, it's guaranteed to take into account issues of alignment and such. unsafe.Pointer(uintptr(&t) + unsafe.OffsetOf(t.F1)) is guaranteed to be the actual unsafe pointer to the actual unsafe address of the field F1 of that specific instance of t at that precise moment in time.

You still have to be very careful about unsafe, unsafe pointers don't generally count as "references" to an object for garbage collection purposes. So if your only reference to that object is that unsafe pointer to that field... weird things may happen. In addition, there's been talk of implementing a "compacting GC" which means that on Garbage Collection memory may be shuffled around, which means that unless you pin the object (which will be added with the compacting GC), the unsafe pointer could spontaneously become invalid without you knowing (regular pointers would remain in tact of course).

If you're sure you'll keep a reference to it, and you're okay with future changes totally breaking your code, go for it. I mean, make absolutely sure you need to use unsafe, but if you've deemed this is the best or only possible solution to your problem, go for it assuming you accept those risks.

Edit: As for "memory order" business. Well, let's just say that I can't find a reference in the spec that guarantees structs are laid out in field order. That said, certain things like encoding/binary.Read would make... shall we say... less than a lot of sense if they weren't.

So don't "rely on it" in a super technical sense, but for any mainstream Go compiler, you can probably be assured fields are in memory order -- with all the usual caveats about word alignment and packing that brings.

like image 186
Linear Avatar answered Mar 08 '23 15:03

Linear


Maybe the offset word makes the question a bit confusing. When the above code is run the address space is growing. What I want to know exaclty is if the struct fields are allocated in the order of their definition.

This should generally hold, but watch out for corner cases like this:

type foo struct {
    a int
    b [0]byte
    c int
}

Also, Go technically doesn't guarantee this. I don't see any reason why any standard Go implementation would change the field-order. Notice however, that there are non-standard implementations (e.g. some implementations that compile Go code to JavaScript) of the Go runtime that do not implement structs as consecutive memory locations, where the notion of unsafe.Offsetof becomes meaningless. These implementations don't provide the unsafe package as far as I know, so your code will not compile anyway.

TL;DR: Yes.

like image 21
fuz Avatar answered Mar 08 '23 15:03

fuz