From d7f9236b387a7744aef39eb808eca3924fd9c1a3 Mon Sep 17 00:00:00 2001 From: Judah Caruso Date: Tue, 18 Nov 2025 17:50:19 -0700 Subject: [PATCH] init --- doc.go | 4 ++++ go.mod | 3 +++ utils.go | 49 ++++++++++++++++++++++++++++++++++++++++ utils_test.go | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 doc.go create mode 100644 go.mod create mode 100644 utils.go create mode 100644 utils_test.go diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..2d508eb --- /dev/null +++ b/doc.go @@ -0,0 +1,4 @@ +// Package xx contains various experiments. +// +// Everything in here should be expected to be unstable. +package xx diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ce33273 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.brut.systems/judah/xx + +go 1.25.0 diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..d712129 --- /dev/null +++ b/utils.go @@ -0,0 +1,49 @@ +package xx + +import ( + "unsafe" +) + +// New returns a newly allocated value with an initial value. +func New[T any](expr T) *T { + p := new(T) + *p = expr + return p +} + +// Bitcast performs a bit conversion between two types of the same size. +// +// Bitcast panics if the sizes of the types differ. +func Bitcast[TOut any, TIn any](value TIn) TOut { + if SizeOf[TOut]() != SizeOf[TIn]() { + panic("bitcast: sizes of types must match") + } + return *((*TOut)(unsafe.Pointer(&value))) +} + +// Copy copies src number of bytes into dst. +// Returns dst. +// +// Copy panics if src is smaller than dst. +func Copy[TDst any, TSrc any](dst *TDst, src *TSrc) *TDst { + if SizeOf[TSrc]() < SizeOf[TDst]() { + panic("copy: size of src must be >= dst") + } + MemCopy(unsafe.Pointer(dst), unsafe.Pointer(src), SizeOf[TDst]()) + return dst +} + +// MemCopy copies size number of bytes from src into dst. +// Returns dst. +func MemCopy(dst, src unsafe.Pointer, size uintptr) unsafe.Pointer { + copy(unsafe.Slice((*byte)(dst), size), unsafe.Slice((*byte)(src), size)) + return dst +} + +// SizeOf returns the size in bytes of the given type. +// +// Not to be confused with [unsafe.Sizeof] which returns the size of an expression. +func SizeOf[T any]() uintptr { + var zero T + return unsafe.Sizeof(zero) +} diff --git a/utils_test.go b/utils_test.go new file mode 100644 index 0000000..7e5141c --- /dev/null +++ b/utils_test.go @@ -0,0 +1,62 @@ +package xx_test + +import ( + "testing" + "unsafe" + + "git.brut.systems/judah/xx" +) + +func TestNew(t *testing.T) { + a := xx.New(uint32(1024)) + if *a != 1024 { + t.Fail() + } + + if unsafe.Sizeof(*a) != xx.SizeOf[uint32]() { + t.Fail() + } + + b := xx.New(struct{ x, y, z float32 }{10, 20, 30}) + if b.x != 10 { + t.Fail() + } + if b.y != 20 { + t.Fail() + } + if b.z != 30 { + t.Fail() + } + + c := xx.New(b) + if c == &b { + t.Fail() + } + if (*c).x != 10 { + t.Fail() + } + if (*c).y != 20 { + t.Fail() + } + if (*c).z != 30 { + t.Fail() + } +} + +func TestBitcast(t *testing.T) { + a := uint32(0xFFFF_FFFF) + b := xx.Bitcast[float32](a) + c := xx.Bitcast[uint32](b) + if a != c { + t.Fail() + } + + d := xx.Bitcast[int8](uint8(0xFF)) + if d != -1 { + t.Fail() + } + e := xx.Bitcast[uint8](d) + if e != 255 { + t.Fail() + } +}