How can I iterate over a byte slice and assign them to the fields of a struct?
type s struct {
f1 []byte
f2 []byte
f3 []byte
}
func S s {
x := s{}
x.f1 = make([]byte, 4)
x.f1 = make([]byte, 2)
x.f1 = make([]byte, 2)
return x
}
func main() {
data := []byte{83, 117, 110, 83, 0, 1, 0, 65}
Z := S()
//pesudo code from here
i:= 0
for field in Z {
field = data[i:len(field)]
i = len(field)
}
Expecting:
- f1 = [83,117,110,83]
- f2 = [0,1]
- f3 = [0,65]
I've done this in C/C before but I can't figure out how to do it in Go. I need the assigning function to be generic as I'm going to have several different structs some of which may not exist in the stream.
Ideally I want to pass in the initialized struct and my code would iterate over the struct fields filling them in.
CodePudding user response:
You can use reflect.Copy
. Like the built-in copy
, it copies data into the destination up to its length. Make sure the fields you need to set are exported.
func main() {
data := []byte{83, 117, 110, 83, 0, 1, 0, 65}
z := S{
F1: make([]byte, 4),
F2: make([]byte, 2),
F3: make([]byte, 2),
}
SetBytes(&z, data)
fmt.Println(z) // {[83 117 110 83] [0 1] [110 83]}
}
func SetBytes(dst any, data []byte) {
v := reflect.ValueOf(dst)
if v.Kind() != reflect.Ptr {
panic("dst must be addressable")
}
v = v.Elem()
j := 0
for i := 0; i < v.NumField(); i {
field := v.Field(i)
if field.Kind() != reflect.Slice {
continue
}
j = reflect.Copy(v.Field(i), reflect.ValueOf(data[j:]))
}
}
Since data
is assumed to be always []byte
, you can subslice it directly.
Alternatively, you can use reflect.Value#Slice
:
d := reflect.ValueOf(data)
// and later
reflect.Copy(v.Field(i), d.Slice(j, d.Len()))
Playground: https://go.dev/play/p/eV59-aKQU1z
CodePudding user response:
Leverage the reflection code in the binary/encoding package.
Step 1: Declare the fields as arrays instead of slices.
type S struct {
F1 [4]byte
F2 [2]byte
F3 [2]byte
}
Step 2: Decode the data to the struct using binary.Read
var s S
data := []byte{83, 117, 110, 83, 0, 1, 0, 65}
err := binary.Read(bytes.NewReader(data), binary.LittleEndian, &s)
if err != nil {
log.Fatal(err)
}
Step 3: Done!
fmt.Print(s) // prints {[83 117 110 83] [0 1] [0 65]}