diff --git a/examples/basic/unsafes/README.md b/examples/basic/unsafes/README.md index a1f0878e..f6893855 100644 --- a/examples/basic/unsafes/README.md +++ b/examples/basic/unsafes/README.md @@ -4,9 +4,11 @@ | file | example name | note | | ---------------------- | ------------------- | -------------------------------------------------------------------- | -| unsafe_sizeof.go | unsafe_sizeof | unsafe.Sizeof() についてのサンプルです. | | unsafe_string.go | unsafe_string | unsafe.String() のサンプルです. | | unsafe_stringdata.go | unsafe_stringdata | unsafe.StringData() のサンプルです. | | unsafe_pointer_cast.go | unsafe_pointer_cast | unsafeパッケージを用いてポインタを任意の型にキャストするサンプルです | -| unsafe_add.go | usnafe_add | unsafe.Add関数を利用してポインタ演算するサンプルです | -| unsafe_slice.go | usnafe_slice | unsafe.SliceData() と unsafe.Slice() のサンプルです | \ No newline at end of file +| unsafe_add.go | unsafe_add | unsafe.Add関数を利用してポインタ演算するサンプルです | +| unsafe_slice.go | unsafe_slice | unsafe.SliceData() と unsafe.Slice() のサンプルです | +| unsafe_sizeof.go | unsafe_sizeof | unsafe.Sizeof() についてのサンプルです. | +| unsafe_offsetof.go | unsafe_offsetof | unsafe.Offsetof() のサンプルです | +| unsafe_dump.go | unsafe_dump | unsafeパッケージを使って構造体のメモリダンプを出力するサンプルです | diff --git a/examples/basic/unsafes/examples.go b/examples/basic/unsafes/examples.go index c8befb16..955fbf60 100644 --- a/examples/basic/unsafes/examples.go +++ b/examples/basic/unsafes/examples.go @@ -19,4 +19,6 @@ func (r *register) Regist(m mapping.ExampleMapping) { m["unsafe_pointer_cast"] = PointerCast m["unsafe_add"] = Add m["unsafe_slice"] = Slice + m["unsafe_offsetof"] = Offsetof + m["unsafe_dump"] = Dump } diff --git a/examples/basic/unsafes/unsafe_dump.go b/examples/basic/unsafes/unsafe_dump.go new file mode 100644 index 00000000..ef25204f --- /dev/null +++ b/examples/basic/unsafes/unsafe_dump.go @@ -0,0 +1,77 @@ +package unsafes + +import ( + "encoding/hex" + "fmt" + "math" + "os" + "unsafe" +) + +// Dump は、unsafeパッケージを使って構造体のメモリダンプを出力するサンプルです。 +func Dump() error { + type ( + // わざとパディングが入る構造体とする + // size: 24bytes + S1 struct { + A uint8 // offset=0, size=1, padding=3 + B uint32 // offset=4, size=4, padding=0 + C uint64 // offset=8, size=8, padding=0 + D uint16 // offset=16, size=2, padding=6 + } + ) + + // 構造体のメモリダンプ + // 尚、実行すると以下のように + // 00000000 ff 62 a6 00 ff ff ff ff ff ff ff ff ff ff ff ff + // 00000010 ff ff 1e 00 c0 00 00 00 + // 62 a6 のようなゴミが見える。これはGoはパディング部分をゼロ初期化しないため。 + // Goの仕様では、構造体のフィールドは初期化されるが、アライメントのためのパディング領域は未定義となっている。 + var ( + s1 = S1{math.MaxUint8, math.MaxUint32, math.MaxUint64, math.MaxUint16} + size = unsafe.Sizeof(s1) + ptr = unsafe.Pointer(&s1) + bytePtr = (*byte)(ptr) + byteSlice = (([]byte)(unsafe.Slice(bytePtr, size))) + ) + hex.Dumper(os.Stdout).Write(byteSlice) + fmt.Println("") + + // ゼロクリアしてから値を再設定して確認してみる + // 以下のループはmemset(ptr, 0, size) と同じ感じ。 + for i := range int(size) { + if i == 0 { + *bytePtr = 0 + } + + ptr = unsafe.Add(ptr, 1) + bytePtr = (*byte)(ptr) + *bytePtr = 0 + } + + // 構造体の値を設定しなおし + s1.A = math.MaxUint8 + s1.B = math.MaxUint32 + s1.C = math.MaxUint64 + s1.D = math.MaxUint16 + + hex.Dumper(os.Stdout).Write(byteSlice) + + return nil + + /* + $ task + task: [build] go build -o "/home/dev/dev/github/try-golang/try-golang" . + task: [run] ./try-golang -onetime + + ENTER EXAMPLE NAME: unsafe_dump + + [Name] "unsafe_dump" + 00000000 ff 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000010 ff ff 18 00 c0 00 00 00 + 00000000 ff 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00000010 ff ff 00 00 00 00 00 00 + + [Elapsed] 88.436µs + */ +} diff --git a/examples/basic/unsafes/unsafe_offsetof.go b/examples/basic/unsafes/unsafe_offsetof.go new file mode 100644 index 00000000..b8f1f9d9 --- /dev/null +++ b/examples/basic/unsafes/unsafe_offsetof.go @@ -0,0 +1,65 @@ +package unsafes + +import ( + "fmt" + "math" + "unsafe" +) + +// Offsetof は、unsafe.Offsetof() のサンプルです。 +// +// > Offsetof returns the offset within the struct of the field represented by x, 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. +// > The return value of Offsetof is a Go constant if the type of the argument x does not have variable size. +// +// > Offsetof は、引数 x によって表される構造体内のフィールドのオフセットを返します。引数 x は、必ず structValue.field の形式でなければなりません。 +// > 言い換えると、これは構造体の先頭からそのフィールドの先頭までのバイト数を返します。 +// > Offsetof の戻り値は、引数 x の型が可変サイズを持たない場合、Goの定数になります。 +// +// REFERENCES: +// - https://pkg.go.dev/unsafe@go1.25.3#Offsetof +func Offsetof() error { + type ( + // わざとパディングが入る構造体とする + // size: 24bytes + S1 struct { + A uint8 // offset=0, size=1, padding=3 + B uint32 // offset=4, size=4, padding=0 + C uint64 // offset=8, size=8, padding=0 + D uint16 // offset=16, size=2, padding=6 + } + ) + var ( + s1 = S1{math.MaxUint8, math.MaxUint32, math.MaxUint64, math.MaxUint16} + off1 = unsafe.Offsetof(s1.A) + off2 = unsafe.Offsetof(s1.B) + off3 = unsafe.Offsetof(s1.C) + off4 = unsafe.Offsetof(s1.D) + size = unsafe.Sizeof(s1) + ) + fmt.Printf("S1.A: %d\n", off1) + fmt.Printf("S1.B: %d\n", off2) + fmt.Printf("S1.C: %d\n", off3) + fmt.Printf("S1.D: %d\n", off4) + fmt.Printf("size: %d\n", size) + + return nil + + /* + $ task + task: [build] go build -o "/home/dev/dev/github/try-golang/try-golang" . + task: [run] ./try-golang -onetime + + ENTER EXAMPLE NAME: unsafe_offsetof + + [Name] "unsafe_offsetof" + S1.A: 0 + S1.B: 4 + S1.C: 8 + S1.D: 16 + size: 24 + + + [Elapsed] 11.773µs + */ +}