mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-13 06:07:34 +00:00
util/cstruct: add package for decoding padded C structures (#5429)
I was working on my "dump iptables rules using only syscalls" branch and had a bunch of C structure decoding to do. Rather than manually calculating the padding or using unsafe trickery to actually cast variable-length structures to Go types, I'd rather use a helper package that deals with padding for me. Padding rules were taken from the following article: http://www.catb.org/esr/structure-packing/ Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
This commit is contained in:
75
util/cstruct/cstruct_example_test.go
Normal file
75
util/cstruct/cstruct_example_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Only built on 64-bit platforms to avoid complexity
|
||||
|
||||
//go:build amd64 || arm64 || mips64le || ppc64le || riscv64
|
||||
// +build amd64 arm64 mips64le ppc64le riscv64
|
||||
|
||||
package cstruct
|
||||
|
||||
import "fmt"
|
||||
|
||||
// This test provides a semi-realistic example of how you can
|
||||
// use this package to decode a C structure.
|
||||
func ExampleDecoder() {
|
||||
// Our example C structure:
|
||||
// struct mystruct {
|
||||
// char *p;
|
||||
// char c;
|
||||
// /* implicit: char _pad[3]; */
|
||||
// int x;
|
||||
// };
|
||||
//
|
||||
// The Go structure definition:
|
||||
type myStruct struct {
|
||||
Ptr uintptr
|
||||
Ch byte
|
||||
Intval uint32
|
||||
}
|
||||
|
||||
// Our "in-memory" version of the above structure
|
||||
buf := []byte{
|
||||
1, 2, 3, 4, 0, 0, 0, 0, // ptr
|
||||
5, // ch
|
||||
99, 99, 99, // padding
|
||||
78, 6, 0, 0, // x
|
||||
}
|
||||
d := NewDecoder(buf)
|
||||
|
||||
// Decode the structure; if one of these function returns an error,
|
||||
// then subsequent decoder functions will return the zero value.
|
||||
var x myStruct
|
||||
x.Ptr = d.Uintptr()
|
||||
x.Ch = d.Byte()
|
||||
x.Intval = d.Uint32()
|
||||
|
||||
// Note that per the Go language spec:
|
||||
// [...] when evaluating the operands of an expression, assignment,
|
||||
// or return statement, all function calls, method calls, and
|
||||
// (channel) communication operations are evaluated in lexical
|
||||
// left-to-right order
|
||||
//
|
||||
// Since each field is assigned via a function call, one could use the
|
||||
// following snippet to decode the struct.
|
||||
// x := myStruct{
|
||||
// Ptr: d.Uintptr(),
|
||||
// Ch: d.Byte(),
|
||||
// Intval: d.Uint32(),
|
||||
// }
|
||||
//
|
||||
// However, this means that reordering the fields in the initialization
|
||||
// statement–normally a semantically identical operation–would change
|
||||
// the way the structure is parsed. Thus we do it as above with
|
||||
// explicit ordering.
|
||||
|
||||
// After finishing with the decoder, check errors
|
||||
if err := d.Err(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Print the decoder offset and structure
|
||||
fmt.Printf("off=%d struct=%#v\n", d.Offset(), x)
|
||||
// Output: off=16 struct=cstruct.myStruct{Ptr:0x4030201, Ch:0x5, Intval:0x64e}
|
||||
}
|
Reference in New Issue
Block a user