Compare commits
No commits in common. "41b74c5b1410fac0dfc272aa987faceba02306a7" and "150a982f7807229f92a06a77f97666247e2e1286" have entirely different histories.
41b74c5b14
...
150a982f78
5 changed files with 38 additions and 56 deletions
|
|
@ -63,6 +63,7 @@ func (x *Xar[T]) AppendMany(values ...T) *T {
|
||||||
}
|
}
|
||||||
|
|
||||||
first := x.Append(values[0])
|
first := x.Append(values[0])
|
||||||
|
|
||||||
if len(values) > 1 {
|
if len(values) > 1 {
|
||||||
for _, v := range values[1:] {
|
for _, v := range values[1:] {
|
||||||
x.Append(v)
|
x.Append(v)
|
||||||
|
|
|
||||||
|
|
@ -40,13 +40,6 @@ func BitCast[TOut any, TIn any](value *TIn) TOut {
|
||||||
return *((*TOut)(unsafe.Pointer(value)))
|
return *((*TOut)(unsafe.Pointer(value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func BitCastValue[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 size number of bytes from src into dst.
|
// Copy copies size number of bytes from src into dst.
|
||||||
//
|
//
|
||||||
// Returns dst.
|
// Returns dst.
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@
|
||||||
// }
|
// }
|
||||||
package osthread
|
package osthread
|
||||||
|
|
||||||
import (
|
import "runtime"
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Start allows arbitrary functions to be run on the main operating system thread.
|
// Start allows arbitrary functions to be run on the main operating system thread.
|
||||||
//
|
//
|
||||||
|
|
@ -26,14 +24,14 @@ func Start(entrypoint func()) {
|
||||||
|
|
||||||
done := make(chan any)
|
done := make(chan any)
|
||||||
|
|
||||||
// Immediately queue entrypoint
|
// Run entrypoint in a separate goroutine.
|
||||||
Go(func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
done <- nil
|
done <- nil
|
||||||
}()
|
}()
|
||||||
|
|
||||||
entrypoint()
|
entrypoint()
|
||||||
})
|
}()
|
||||||
|
|
||||||
// Call functions in our queue until entrypoint returns.
|
// Call functions in our queue until entrypoint returns.
|
||||||
// These functions are called on the main operating system thread.
|
// These functions are called on the main operating system thread.
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ type anystruct any
|
||||||
// float32
|
// float32
|
||||||
// })
|
// })
|
||||||
type Of[T anystruct] struct {
|
type Of[T anystruct] struct {
|
||||||
typ reflect.Type
|
typ reflect.Kind
|
||||||
mem []byte
|
mem []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,25 +47,39 @@ func (u Of[T]) Size() uintptr {
|
||||||
// String returns the string representation of a union.
|
// String returns the string representation of a union.
|
||||||
func (u Of[T]) String() string {
|
func (u Of[T]) String() string {
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
|
b.WriteString("union[")
|
||||||
fmt.Fprintf(&b, "union[%s] = ", reflect.TypeFor[T]().String())
|
if u.typ == reflect.Invalid {
|
||||||
if u.typ == nil {
|
|
||||||
b.WriteString("none")
|
b.WriteString("none")
|
||||||
} else {
|
} else {
|
||||||
b.WriteString(u.typ.String())
|
b.WriteString(u.typ.String())
|
||||||
}
|
}
|
||||||
|
b.WriteString("] {")
|
||||||
|
|
||||||
|
t := reflect.TypeFor[T]()
|
||||||
|
if t.Kind() == reflect.Struct {
|
||||||
|
b.WriteByte(' ')
|
||||||
|
fields := getInternalFields(u)
|
||||||
|
for i, field := range fields {
|
||||||
|
b.WriteString(field.Type.String())
|
||||||
|
if i < len(fields)-1 {
|
||||||
|
b.WriteString("; ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.WriteByte(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteByte('}')
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is returns true if the given type is currently stored in the union.
|
// Is returns true if the given type is currently stored in the union.
|
||||||
func Is[E any, T anystruct](u Of[T]) bool {
|
func Is[E any, T anystruct](u Of[T]) bool {
|
||||||
// Explicit invalid check to make sure invalid types don't result in false-positives.
|
// Explicit invalid check to make sure invalid types don't result in false-positives.
|
||||||
if u.typ == nil {
|
if u.typ == reflect.Invalid {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return u.typ == reflect.TypeFor[E]()
|
return u.typ == reflect.TypeFor[E]().Kind()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set overwrites the backing memory of a union with the given value; initializing the union if uninitialized.
|
// Set overwrites the backing memory of a union with the given value; initializing the union if uninitialized.
|
||||||
|
|
@ -77,8 +91,8 @@ func Set[V any, T anystruct](u *Of[T], value V) {
|
||||||
u.mem = make([]byte, mem.Sizeof[T]())
|
u.mem = make([]byte, mem.Sizeof[T]())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe.Slice((*V)(unsafe.Pointer(&u.mem[0])), 1)[0] = value
|
*(*V)(unsafe.Pointer(&u.mem[0])) = value
|
||||||
u.typ = reflect.TypeFor[V]()
|
u.typ = reflect.TypeFor[V]().Kind()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSafe overwrites the backing memory of a union with the given value,
|
// SetSafe overwrites the backing memory of a union with the given value,
|
||||||
|
|
@ -93,8 +107,8 @@ func SetSafe[V any, T anystruct](u *Of[T], value V) error {
|
||||||
vt := reflect.TypeFor[V]()
|
vt := reflect.TypeFor[V]()
|
||||||
for _, field := range getInternalFields(*u) {
|
for _, field := range getInternalFields(*u) {
|
||||||
if field.Type == vt {
|
if field.Type == vt {
|
||||||
unsafe.Slice((*V)(unsafe.Pointer(&u.mem[0])), 1)[0] = value
|
*(*V)(unsafe.Pointer(&u.mem[0])) = value
|
||||||
u.typ = reflect.TypeFor[V]()
|
u.typ = reflect.TypeFor[V]().Kind()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +125,7 @@ func Get[V any, T anystruct](u Of[T]) V {
|
||||||
panic(ErrUninitializedAccess)
|
panic(ErrUninitializedAccess)
|
||||||
}
|
}
|
||||||
|
|
||||||
return unsafe.Slice((*V)(unsafe.Pointer(&u.mem[0])), 1)[0]
|
return *(*V)(unsafe.Pointer(&u.mem[0]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSafe returns the union's backing memory interpreted as a value of type V, returning an error if the type
|
// GetSafe returns the union's backing memory interpreted as a value of type V, returning an error if the type
|
||||||
|
|
@ -126,7 +140,7 @@ func GetSafe[V any, T anystruct](u Of[T]) (V, error) {
|
||||||
vt := reflect.TypeFor[V]()
|
vt := reflect.TypeFor[V]()
|
||||||
for _, field := range getInternalFields(u) {
|
for _, field := range getInternalFields(u) {
|
||||||
if field.Type == vt {
|
if field.Type == vt {
|
||||||
return unsafe.Slice((*V)(unsafe.Pointer(&u.mem[0])), 1)[0], nil
|
return *(*V)(unsafe.Pointer(&u.mem[0])), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
38
xx.go
38
xx.go
|
|
@ -1,13 +1,18 @@
|
||||||
package xx
|
package xx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"git.brut.systems/judah/xx/mem"
|
"git.brut.systems/judah/xx/mem"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// New returns a newly allocated value with an initial value.
|
||||||
|
func New[T any](expr T) *T {
|
||||||
|
p := new(T)
|
||||||
|
*p = expr
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
// Copy copies src number of bytes into dst.
|
// Copy copies src number of bytes into dst.
|
||||||
// Returns dst.
|
// Returns dst.
|
||||||
//
|
//
|
||||||
|
|
@ -29,32 +34,3 @@ func Clone[T any](value *T) *T {
|
||||||
func BoolUint(b bool) uint {
|
func BoolUint(b bool) uint {
|
||||||
return uint(*(*uint8)(unsafe.Pointer(&b)))
|
return uint(*(*uint8)(unsafe.Pointer(&b)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallerLocation returns the source location of the function CallerLocation is called in.
|
|
||||||
func CallerLocation() (file string, line int) {
|
|
||||||
_, file, line, _ = runtime.Caller(2)
|
|
||||||
|
|
||||||
// @todo: I'm sure there's a better way to do this
|
|
||||||
// Special-case when CallerLocation is called from main
|
|
||||||
if strings.Contains(file, "runtime") && strings.Contains(file, "proc.go") {
|
|
||||||
_, file, line, _ = runtime.Caller(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// HashLocation returns a hash of file and line, most likely returned from [CallerLocation].
|
|
||||||
func HashLocation(file string, line int) uint64 {
|
|
||||||
const (
|
|
||||||
FNV64_PRIME uint64 = 0x100000001B3
|
|
||||||
FNV64_BIAS uint64 = 0xCBF29CE484222325
|
|
||||||
)
|
|
||||||
|
|
||||||
h := FNV64_BIAS
|
|
||||||
for _, c := range file {
|
|
||||||
h = (h ^ uint64(c)) * FNV64_PRIME
|
|
||||||
}
|
|
||||||
|
|
||||||
h = (h ^ uint64(line)) * FNV64_PRIME
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue