diff options
Diffstat (limited to 'vendor/codeberg.org/gruf/go-mangler/v2')
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/LICENSE | 9 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/README.md | 31 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/array.go | 55 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/cache.go | 62 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/helpers.go | 43 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/load.go | 150 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/mangle.go | 93 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/manglers.go | 113 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/map.go | 80 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/method.go | 105 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/pointer.go | 81 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/slice.go | 95 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/struct.go | 75 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-mangler/v2/type.go | 20 |
14 files changed, 1012 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/LICENSE b/vendor/codeberg.org/gruf/go-mangler/v2/LICENSE new file mode 100644 index 000000000..dffbdf0c9 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2023 gruf + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/README.md b/vendor/codeberg.org/gruf/go-mangler/v2/README.md new file mode 100644 index 000000000..636c2494c --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/README.md @@ -0,0 +1,31 @@ +# go-mangler + +[Documentation](https://pkg.go.dev/codeberg.org/gruf/go-mangler). + +To put it simply is a bit of an odd library. It aims to provide incredibly fast, unique string outputs for all default supported input data types during a given runtime instance. See `mangler.String()`for supported types. + +It is useful, for example, for use as part of larger abstractions involving hashmaps. That was my particular usecase anyways... + +This package does make liberal use of the "unsafe" package. + +Benchmarks are below. Please note the more important thing to notice here is the relative difference in benchmark scores, the actual `ns/op`,`B/op`,`allocs/op` accounts for running through ~80 possible test cases, including some not-ideal situations. + +The choice of libraries in the benchmark are just a selection of libraries that could be used in a similar manner to this one, i.e. serializing in some manner. + +``` +go test -run=none -benchmem -gcflags=all='-l=4' -bench=.* +goos: linux +goarch: amd64 +pkg: codeberg.org/gruf/go-mangler +cpu: 11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz +BenchmarkMangle-8 1278526 966.0 ns/op 0 B/op 0 allocs/op +BenchmarkMangleKnown-8 3443587 345.9 ns/op 0 B/op 0 allocs/op +BenchmarkJSON-8 228962 4717 ns/op 1849 B/op 99 allocs/op +BenchmarkLoosy-8 307194 3447 ns/op 776 B/op 65 allocs/op +BenchmarkFmt-8 150254 7405 ns/op 1377 B/op 143 allocs/op +BenchmarkFxmackerCbor-8 364411 3037 ns/op 1224 B/op 105 allocs/op +BenchmarkMitchellhHashStructure-8 102272 11268 ns/op 8996 B/op 1000 allocs/op +BenchmarkCnfStructhash-8 6789 168703 ns/op 288301 B/op 5779 allocs/op +PASS +ok codeberg.org/gruf/go-mangler 11.715s +``` diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/array.go b/vendor/codeberg.org/gruf/go-mangler/v2/array.go new file mode 100644 index 000000000..6638a6e2d --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/array.go @@ -0,0 +1,55 @@ +package mangler + +import ( + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +// iterArrayType returns a Mangler capable of iterating +// and mangling the given array type currently in TypeIter{}. +// note this will fetch sub-Mangler for array element type. +func iterArrayType(t xunsafe.TypeIter) Mangler { + + // Array element type. + elem := t.Type.Elem() + + // Get nested elem TypeIter with appropriate flags. + flags := xunsafe.ReflectArrayElemFlags(t.Flag, elem) + et := t.Child(elem, flags) + + // Get elem mangler. + fn := loadOrGet(et) + if fn == nil { + return nil + } + + // Array element in-memory size. + esz := t.Type.Elem().Size() + + // No of elements. + n := t.Type.Len() + switch n { + case 0: + return empty_mangler + case 1: + return fn + default: + return func(buf []byte, ptr unsafe.Pointer) []byte { + for i := 0; i < n; i++ { + // Mangle data at array index. + offset := esz * uintptr(i) + eptr := add(ptr, offset) + buf = fn(buf, eptr) + buf = append(buf, ',') + } + + if n > 0 { + // Drop final comma. + buf = buf[:len(buf)-1] + } + + return buf + } + } +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/cache.go b/vendor/codeberg.org/gruf/go-mangler/v2/cache.go new file mode 100644 index 000000000..0f6f71cd0 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/cache.go @@ -0,0 +1,62 @@ +package mangler + +import ( + "sync/atomic" + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +var manglers cache + +// cache is a concurrency-safe map[xunsafe.TypeInfo]Mangler +// cache, designed for heavy reads but with unfortunately expensive +// writes. it is designed such that after some initial load period +// in which functions are cached by types, all future ops are reads. +type cache struct{ p unsafe.Pointer } + +// Get will check cache for mangler func under key. +func (c *cache) Get(t xunsafe.TypeInfo) Mangler { + if p := c.load(); p != nil { + return (*p)[t] + } + return nil +} + +// Put will place given mangler func in cache under key, if not already exists. +func (c *cache) Put(t xunsafe.TypeInfo, fn Mangler) { + for { + p := c.load() + + var cache map[xunsafe.TypeInfo]Mangler + + if p != nil { + if _, ok := (*p)[t]; ok { + return + } + + cache = make(map[xunsafe.TypeInfo]Mangler, len(*p)+1) + for key, value := range *p { + cache[key] = value + } + } else { + cache = make(map[xunsafe.TypeInfo]Mangler, 1) + } + + cache[t] = fn + + if c.cas(p, &cache) { + return + } + } +} + +// load is a typed wrapper around atomic.LoadPointer(). +func (c *cache) load() *map[xunsafe.TypeInfo]Mangler { + return (*map[xunsafe.TypeInfo]Mangler)(atomic.LoadPointer(&c.p)) +} + +// cas is a typed wrapper around atomic.CompareAndSwapPointer(). +func (c *cache) cas(old, new *map[xunsafe.TypeInfo]Mangler) bool { + return atomic.CompareAndSwapPointer(&c.p, unsafe.Pointer(old), unsafe.Pointer(new)) +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/helpers.go b/vendor/codeberg.org/gruf/go-mangler/v2/helpers.go new file mode 100644 index 000000000..0b07bba13 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/helpers.go @@ -0,0 +1,43 @@ +package mangler + +import ( + "unsafe" +) + +func append_uint16(b []byte, u uint16) []byte { + return append(b, // LE + byte(u), + byte(u>>8), + ) +} + +func append_uint32(b []byte, u uint32) []byte { + return append(b, // LE + byte(u), + byte(u>>8), + byte(u>>16), + byte(u>>24), + ) +} + +func append_uint64(b []byte, u uint64) []byte { + return append(b, // LE + byte(u), + byte(u>>8), + byte(u>>16), + byte(u>>24), + byte(u>>32), + byte(u>>40), + byte(u>>48), + byte(u>>56), + ) +} + +func empty_mangler(buf []byte, _ unsafe.Pointer) []byte { + return buf +} + +// add returns the ptr addition of starting ptr and a delta. +func add(ptr unsafe.Pointer, delta uintptr) unsafe.Pointer { + return unsafe.Pointer(uintptr(ptr) + delta) +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/load.go b/vendor/codeberg.org/gruf/go-mangler/v2/load.go new file mode 100644 index 000000000..8355c5f3c --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/load.go @@ -0,0 +1,150 @@ +package mangler + +import ( + "reflect" + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +// loadOrStore first checks the cache for a Mangler +// function, else generates one by calling get(). +// note: this does store generated funcs in cache. +func loadOrStore(t xunsafe.TypeIter) Mangler { + + // Get cache key. + key := t.TypeInfo + + // Check cache for func. + fn := manglers.Get(key) + + if fn == nil { + // Generate new mangler + // func for this type. + fn = get(t) + if fn == nil { + return nil + } + + // Store func in cache. + manglers.Put(key, fn) + } + + return fn +} + +// loadOrGet first checks the cache for a Mangler +// function, else generates one by calling get(). +// note: it does not store the function in cache. +func loadOrGet(t xunsafe.TypeIter) Mangler { + + // Check cache for mangler func. + fn := manglers.Get(t.TypeInfo) + + if fn == nil { + // Generate new mangler + // func for this type. + fn = get(t) + } + + return fn +} + +var ( + // reflectTypeType is the reflected type of the reflect type, + // used in fmt.get() to prevent iter of internal ABI structs. + reflectTypeType = reflect.TypeOf(reflect.TypeOf(0)) +) + +// get attempts to generate a new Mangler function +// capable of mangling a ptr of given type information. +func get(t xunsafe.TypeIter) (fn Mangler) { + defer func() { + if fn == nil { + // nothing more + // we can do. + return + } + + if t.Parent != nil { + // We're only interested + // in wrapping top-level. + return + } + + // Get reflected type ptr for prefix. + ptr := xunsafe.ReflectTypeData(t.Type) + uptr := uintptr(ptr) + + // Outer fn. + mng := fn + + // Wrap the mangler func to prepend type pointer. + fn = func(buf []byte, ptr unsafe.Pointer) []byte { + buf = append_uint64(buf, uint64(uptr)) + return mng(buf, ptr) + } + }() + + if t.Type == nil { + // nil type. + return nil + } + + if t.Type == reflectTypeType { + // DO NOT iterate down internal ABI + // types, some are in non-GC memory. + return nil + } + + // Check supports known method receiver. + if fn := getMethodType(t); fn != nil { + return fn + } + + if !visit(t) { + // On type recursion simply + // mangle as raw pointer. + return mangle_int + } + + // Get func for type kind. + switch t.Type.Kind() { + case reflect.Pointer: + return derefPointerType(t) + case reflect.Struct: + return iterStructType(t) + case reflect.Array: + return iterArrayType(t) + case reflect.Slice: + return iterSliceType(t) + case reflect.Map: + return iterMapType(t) + case reflect.String: + return mangle_string + case reflect.Bool: + return mangle_bool + case reflect.Int, + reflect.Uint, + reflect.Uintptr: + return mangle_int + case reflect.Int8, reflect.Uint8: + return mangle_8bit + case reflect.Int16, reflect.Uint16: + return mangle_16bit + case reflect.Int32, reflect.Uint32: + return mangle_32bit + case reflect.Int64, reflect.Uint64: + return mangle_64bit + case reflect.Float32: + return mangle_32bit + case reflect.Float64: + return mangle_64bit + case reflect.Complex64: + return mangle_64bit + case reflect.Complex128: + return mangle_128bit + default: + return nil + } +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/mangle.go b/vendor/codeberg.org/gruf/go-mangler/v2/mangle.go new file mode 100644 index 000000000..0c746fc95 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/mangle.go @@ -0,0 +1,93 @@ +package mangler + +import ( + "fmt" + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +// Mangler is a function that will take an input value of known type, +// and append it in mangled serialized form to the given byte buffer. +type Mangler func(buf []byte, ptr unsafe.Pointer) []byte + +// Get will fetch the Mangler function for given runtime type information. +// The required argument is of type xunsafe.TypeIter{} as unsafe pointer +// access requires further contextual information like type nesting. +func Get(t xunsafe.TypeIter) Mangler { + t.Parent = nil // enforce type prefix + fn := loadOrStore(t) + if fn == nil { + panic(fmt.Sprintf("cannot mangle type: %s", t.Type)) + } + return fn +} + +// GetNoLoad is functionally similar to Get(), +// without caching the resulting Mangler. +func GetNoLoad(t xunsafe.TypeIter) Mangler { + t.Parent = nil // enforce type prefix + fn := loadOrGet(t) + if fn == nil { + panic(fmt.Sprintf("cannot mangle type: %s", t.Type)) + } + return fn +} + +// Append will append the mangled form of input value 'a' to buffer 'b'. +// +// See mangler.String() for more information on mangled output. +func Append(b []byte, a any) []byte { + t := xunsafe.TypeIterFrom(a) + p := xunsafe.UnpackEface(a) + return Get(t)(b, p) +} + +// AppendMulti appends all mangled forms of input value(s) 'a' to buffer 'b' +// separated by colon characters. When all type manglers are currently cached +// for all types in 'a', this will be faster than multiple calls to Append(). +// +// See mangler.String() for more information on mangled output. +func AppendMulti(b []byte, a ...any) []byte { + if p := manglers.load(); p != nil { + b4 := len(b) + for _, a := range a { + t := xunsafe.TypeIterFrom(a) + m := (*p)[t.TypeInfo] + if m == nil { + b = b[:b4] + goto slow + } + b = m(b, xunsafe.UnpackEface(a)) + b = append(b, '.') + } + return b + } +slow: + for _, a := range a { + b = Append(b, a) + b = append(b, '.') + } + return b +} + +// String will return the mangled format of input value 'a'. This +// mangled output will be unique for all default supported input types +// during a single runtime instance. Uniqueness cannot be guaranteed +// between separate runtime instances (whether running concurrently, or +// the same application running at different times). +// +// The exact formatting of the output data should not be relied upon, +// only that it is unique given the above constraints. Generally though, +// the mangled output is the binary formatted text of given input data. +// +// Uniqueness is guaranteed for similar input data of differing types +// (e.g. string("hello world") vs. []byte("hello world")) by prefixing +// mangled output with the input data's runtime type pointer. +// +// Default supported types include all concrete (i.e. non-interface{}) +// data types, and interfaces implementing Mangleable{}. +func String(a any) string { + b := Append(make([]byte, 0, 32), a) + return *(*string)(unsafe.Pointer(&b)) +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/manglers.go b/vendor/codeberg.org/gruf/go-mangler/v2/manglers.go new file mode 100644 index 000000000..79bfe3c00 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/manglers.go @@ -0,0 +1,113 @@ +package mangler + +import ( + "unsafe" + _ "unsafe" +) + +// Notes: +// the use of unsafe conversion from the direct interface values to +// the chosen types in each of the below functions allows us to convert +// not only those types directly, but anything type-aliased to those +// types. e.g. `time.Duration` directly as int64. + +func mangle_string(buf []byte, ptr unsafe.Pointer) []byte { + return append(buf, *(*string)(ptr)...) +} + +func mangle_string_slice(buf []byte, ptr unsafe.Pointer) []byte { + s := *(*[]string)(ptr) + for _, s := range s { + buf = append(buf, s...) + buf = append(buf, ',') + } + if len(s) > 0 { + buf = buf[:len(buf)-1] + } + return buf +} + +func mangle_bool(buf []byte, ptr unsafe.Pointer) []byte { + if *(*bool)(ptr) { + return append(buf, '1') + } + return append(buf, '0') +} + +func mangle_bool_slice(buf []byte, ptr unsafe.Pointer) []byte { + for _, b := range *(*[]bool)(ptr) { + if b { + buf = append(buf, '1') + } else { + buf = append(buf, '0') + } + } + return buf +} + +func mangle_8bit(buf []byte, ptr unsafe.Pointer) []byte { + return append(buf, *(*uint8)(ptr)) +} + +func mangle_8bit_slice(buf []byte, ptr unsafe.Pointer) []byte { + return append(buf, *(*[]uint8)(ptr)...) +} + +func mangle_16bit(buf []byte, ptr unsafe.Pointer) []byte { + return append_uint16(buf, *(*uint16)(ptr)) +} + +func mangle_16bit_slice(buf []byte, ptr unsafe.Pointer) []byte { + for _, u := range *(*[]uint16)(ptr) { + buf = append_uint16(buf, u) + } + return buf +} + +func mangle_32bit(buf []byte, ptr unsafe.Pointer) []byte { + return append_uint32(buf, *(*uint32)(ptr)) +} + +func mangle_32bit_slice(buf []byte, ptr unsafe.Pointer) []byte { + for _, u := range *(*[]uint32)(ptr) { + buf = append_uint32(buf, u) + } + return buf +} + +func mangle_64bit(buf []byte, ptr unsafe.Pointer) []byte { + return append_uint64(buf, *(*uint64)(ptr)) +} + +func mangle_64bit_slice(buf []byte, ptr unsafe.Pointer) []byte { + for _, u := range *(*[]uint64)(ptr) { + buf = append_uint64(buf, u) + } + return buf +} + +func mangle_int(buf []byte, ptr unsafe.Pointer) []byte { + return append_uint64(buf, uint64(*(*uint)(ptr))) +} + +func mangle_int_slice(buf []byte, ptr unsafe.Pointer) []byte { + for _, u := range *(*[]uint)(ptr) { + buf = append_uint64(buf, uint64(u)) + } + return buf +} + +func mangle_128bit(buf []byte, ptr unsafe.Pointer) []byte { + u2 := *(*[2]uint64)(ptr) + buf = append_uint64(buf, u2[0]) + buf = append_uint64(buf, u2[1]) + return buf +} + +func mangle_128bit_slice(buf []byte, ptr unsafe.Pointer) []byte { + for _, u2 := range *(*[][2]uint64)(ptr) { + buf = append_uint64(buf, u2[0]) + buf = append_uint64(buf, u2[1]) + } + return buf +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/map.go b/vendor/codeberg.org/gruf/go-mangler/v2/map.go new file mode 100644 index 000000000..986aca4b0 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/map.go @@ -0,0 +1,80 @@ +package mangler + +import ( + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +// iterMapType returns a Mangler capable of iterating +// and mangling the given map type currently in TypeIter{}. +// note this will fetch sub-Manglers for key / value types. +func iterMapType(t xunsafe.TypeIter) Mangler { + + // Key / value types. + key := t.Type.Key() + elem := t.Type.Elem() + + // Get nested k / v TypeIters with appropriate flags. + flagsKey := xunsafe.ReflectMapKeyFlags(key) + flagsVal := xunsafe.ReflectMapElemFlags(elem) + kt := t.Child(key, flagsKey) + vt := t.Child(elem, flagsVal) + + // Get key mangler. + kfn := loadOrGet(kt) + if kfn == nil { + return nil + } + + // Get value mangler. + vfn := loadOrGet(vt) + if vfn == nil { + return nil + } + + // Final map type. + rtype := t.Type + flags := t.Flag + + return func(buf []byte, ptr unsafe.Pointer) []byte { + if ptr == nil || *(*unsafe.Pointer)(ptr) == nil { + // Append nil indicator. + buf = append(buf, '0') + return buf + } + + // Build reflect value, and then a map iterator. + v := xunsafe.BuildReflectValue(rtype, ptr, flags) + i := xunsafe.GetMapIter(v) + + // Before len. + l := len(buf) + + // Append not-nil flag. + buf = append(buf, '1') + + for i.Next() { + // Pass to map key func. + ptr = xunsafe.Map_Key(i) + buf = kfn(buf, ptr) + + // Add key seperator. + buf = append(buf, ':') + + // Pass to map elem func. + ptr = xunsafe.Map_Elem(i) + buf = vfn(buf, ptr) + + // Add comma seperator. + buf = append(buf, ',') + } + + if len(buf) != l { + // Drop final comma. + buf = buf[:len(buf)-1] + } + + return buf + } +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/method.go b/vendor/codeberg.org/gruf/go-mangler/v2/method.go new file mode 100644 index 000000000..7a938d6ae --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/method.go @@ -0,0 +1,105 @@ +package mangler + +import ( + "reflect" + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +var ( + // mangleable type for implement checks. + mangleableType = reflect.TypeFor[Mangleable]() +) + +type Mangleable interface { + Mangle(dst []byte) []byte +} + +// getMethodType returns a *possible* Mangler to handle case +// of a type that implements any known interface{} types, else nil. +func getMethodType(t xunsafe.TypeIter) Mangler { + switch { + case t.Type.Implements(mangleableType): + switch t.Type.Kind() { + case reflect.Interface: + return getInterfaceMangleableType(t) + default: + return getConcreteMangleableType(t) + } + default: + return nil + } +} + +// getInterfaceMangleableType returns a Mangler to handle case of an interface{} +// type that implements Mangleable{}, i.e. Mangleable{} itself and any superset of. +func getInterfaceMangleableType(t xunsafe.TypeIter) Mangler { + switch t.Indirect() && !t.IfaceIndir() { + case true: + return func(buf []byte, ptr unsafe.Pointer) []byte { + ptr = *(*unsafe.Pointer)(ptr) + if ptr == nil || (*xunsafe.Abi_NonEmptyInterface)(ptr).Data == nil { + buf = append(buf, '0') + return buf + } + v := *(*Mangleable)(ptr) + buf = append(buf, '1') + buf = v.Mangle(buf) + return buf + } + case false: + return func(buf []byte, ptr unsafe.Pointer) []byte { + if ptr == nil || (*xunsafe.Abi_NonEmptyInterface)(ptr).Data == nil { + buf = append(buf, '0') + return buf + } + v := *(*Mangleable)(ptr) + buf = append(buf, '1') + buf = v.Mangle(buf) + return buf + } + default: + panic("unreachable") + } +} + +// getConcreteMangleableType returns a Manlger to handle case of concrete +// (i.e. non-interface{}) type that has a Mangleable{} method receiver. +func getConcreteMangleableType(t xunsafe.TypeIter) Mangler { + itab := xunsafe.GetIfaceITab[Mangleable](t.Type) + switch { + case t.Indirect() && !t.IfaceIndir(): + return func(buf []byte, ptr unsafe.Pointer) []byte { + ptr = *(*unsafe.Pointer)(ptr) + if ptr == nil { + buf = append(buf, '0') + return buf + } + v := *(*Mangleable)(xunsafe.PackIface(itab, ptr)) + buf = append(buf, '1') + buf = v.Mangle(buf) + return buf + } + case t.Type.Kind() == reflect.Pointer && t.Type.Implements(mangleableType): + // if the interface implementation is received by + // value type, the pointer type will also support + // it but it requires an extra dereference check. + return func(buf []byte, ptr unsafe.Pointer) []byte { + if ptr == nil { + buf = append(buf, '0') + return buf + } + v := *(*Mangleable)(xunsafe.PackIface(itab, ptr)) + buf = append(buf, '1') + buf = v.Mangle(buf) + return buf + } + default: + return func(buf []byte, ptr unsafe.Pointer) []byte { + v := *(*Mangleable)(xunsafe.PackIface(itab, ptr)) + buf = v.Mangle(buf) + return buf + } + } +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/pointer.go b/vendor/codeberg.org/gruf/go-mangler/v2/pointer.go new file mode 100644 index 000000000..d732b2f67 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/pointer.go @@ -0,0 +1,81 @@ +package mangler + +import ( + "reflect" + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +// derefPointerType returns a Mangler capable of dereferencing +// and formatting the given pointer type currently in TypeIter{}. +// note this will fetch a sub-Mangler for resulting value type. +func derefPointerType(t xunsafe.TypeIter) Mangler { + var derefs int + var indirects int64 + rtype := t.Type + flags := t.Flag + + // Iteratively dereference pointer types. + for rtype.Kind() == reflect.Pointer { + + // Only if this is actual indirect memory do we + // perform a derefence, otherwise we just skip over + // and increase the dereference indicator, i.e. '1'. + if flags&xunsafe.Reflect_flagIndir != 0 { + indirects |= 1 << derefs + } + derefs++ + + // Get next elem type. + rtype = rtype.Elem() + + // Get next set of dereferenced element type flags. + flags = xunsafe.ReflectPointerElemFlags(flags, rtype) + } + + // Ensure this is a reasonable number of derefs. + if derefs > 4*int(unsafe.Sizeof(indirects)) { + return nil + } + + // Wrap value as TypeIter. + vt := t.Child(rtype, flags) + + // Get value mangler. + fn := loadOrGet(vt) + if fn == nil { + return nil + } + + return func(buf []byte, ptr unsafe.Pointer) []byte { + for i := 0; i < derefs; i++ { + switch { + case indirects&1<<i == 0: + // No dereference needed. + buf = append(buf, '1') + + case ptr == nil: + // Nil value, return here. + buf = append(buf, '0') + return buf + + default: + // Further deref ptr. + buf = append(buf, '1') + ptr = *(*unsafe.Pointer)(ptr) + } + } + + if ptr == nil { + // Final nil val check. + buf = append(buf, '0') + return buf + } + + // Mangle fully deref'd. + buf = append(buf, '1') + buf = fn(buf, ptr) + return buf + } +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/slice.go b/vendor/codeberg.org/gruf/go-mangler/v2/slice.go new file mode 100644 index 000000000..0a68c0575 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/slice.go @@ -0,0 +1,95 @@ +package mangler + +import ( + "reflect" + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +// iterSliceType returns a Mangler capable of iterating +// and mangling the given slice type currently in TypeIter{}. +// note this will fetch sub-Mangler for slice element type. +func iterSliceType(t xunsafe.TypeIter) Mangler { + + // Get nested element type. + elem := t.Type.Elem() + esz := elem.Size() + + // Get nested elem TypeIter{} with flags. + flags := xunsafe.ReflectSliceElemFlags(elem) + et := t.Child(elem, flags) + + // Prefer to use a known slice mangler func. + if fn := mangleKnownSlice(et); fn != nil { + return fn + } + + // Get elem mangler. + fn := loadOrGet(et) + if fn == nil { + return nil + } + + return func(buf []byte, ptr unsafe.Pointer) []byte { + // Get data as unsafe slice header. + hdr := (*xunsafe.Unsafeheader_Slice)(ptr) + if hdr == nil || hdr.Data == nil { + + // Append nil indicator. + buf = append(buf, '0') + return buf + } + + // Append not-nil flag. + buf = append(buf, '1') + + for i := 0; i < hdr.Len; i++ { + // Mangle at array index. + offset := esz * uintptr(i) + ptr = add(hdr.Data, offset) + buf = fn(buf, ptr) + buf = append(buf, ',') + } + + if hdr.Len > 0 { + // Drop final comma. + buf = buf[:len(buf)-1] + } + + return buf + } +} + +// mangleKnownSlice loads a Mangler function for a +// known slice-of-element type (in this case, primtives). +func mangleKnownSlice(t xunsafe.TypeIter) Mangler { + switch t.Type.Kind() { + case reflect.String: + return mangle_string_slice + case reflect.Bool: + return mangle_bool_slice + case reflect.Int, + reflect.Uint, + reflect.Uintptr: + return mangle_int_slice + case reflect.Int8, reflect.Uint8: + return mangle_8bit_slice + case reflect.Int16, reflect.Uint16: + return mangle_16bit_slice + case reflect.Int32, reflect.Uint32: + return mangle_32bit_slice + case reflect.Int64, reflect.Uint64: + return mangle_64bit_slice + case reflect.Float32: + return mangle_32bit_slice + case reflect.Float64: + return mangle_64bit_slice + case reflect.Complex64: + return mangle_64bit_slice + case reflect.Complex128: + return mangle_128bit_slice + default: + return nil + } +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/struct.go b/vendor/codeberg.org/gruf/go-mangler/v2/struct.go new file mode 100644 index 000000000..e9672bf2b --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/struct.go @@ -0,0 +1,75 @@ +package mangler + +import ( + "unsafe" + + "codeberg.org/gruf/go-xunsafe" +) + +// field stores the minimum necessary +// data for iterating and mangling +// each field in a given struct. +type field struct { + mangle Mangler + offset uintptr +} + +// iterStructType returns a Mangler capable of iterating +// and mangling the given struct type currently in TypeIter{}. +// note this will fetch sub-Manglers for each struct field. +func iterStructType(t xunsafe.TypeIter) Mangler { + + // Number of struct fields. + n := t.Type.NumField() + + // Gather mangler functions. + fields := make([]field, n) + for i := 0; i < n; i++ { + + // Get struct field at index. + sfield := t.Type.Field(i) + rtype := sfield.Type + + // Get nested field TypeIter with appropriate flags. + flags := xunsafe.ReflectStructFieldFlags(t.Flag, rtype) + ft := t.Child(sfield.Type, flags) + + // Get field mangler. + fn := loadOrGet(ft) + if fn == nil { + return nil + } + + // Set field info. + fields[i] = field{ + mangle: fn, + offset: sfield.Offset, + } + } + + // Handle no. fields. + switch len(fields) { + case 0: + return empty_mangler + case 1: + return fields[0].mangle + default: + return func(buf []byte, ptr unsafe.Pointer) []byte { + for i := range fields { + // Get struct field ptr via offset. + fptr := add(ptr, fields[i].offset) + + // Mangle the struct field data. + buf = fields[i].mangle(buf, fptr) + buf = append(buf, ',') + } + + if len(fields) > 0 { + // Drop final comma. + buf = buf[:len(buf)-1] + } + + return buf + } + } +} diff --git a/vendor/codeberg.org/gruf/go-mangler/v2/type.go b/vendor/codeberg.org/gruf/go-mangler/v2/type.go new file mode 100644 index 000000000..71be7f0b4 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-mangler/v2/type.go @@ -0,0 +1,20 @@ +package mangler + +import ( + "codeberg.org/gruf/go-xunsafe" +) + +// visit checks if current type has already +// appeared in the TypeIter{}'s parent heirarchy. +func visit(iter xunsafe.TypeIter) bool { + t := iter.Type + + // Check if type is already encountered further up tree. + for node := iter.Parent; node != nil; node = node.Parent { + if node.Type == t { + return false + } + } + + return true +} |
