codeberg.org/gruf/go-mangler@v1.3.0/mangle.go (about) 1 package mangler 2 3 import ( 4 "reflect" 5 "sync" 6 "unsafe" 7 ) 8 9 // manglers is a map of runtime 10 // type ptrs => Mangler functions. 11 var manglers sync.Map 12 13 // Mangled is an interface that allows any type to implement a custom 14 // Mangler function to improve performance when mangling this type. 15 type Mangled interface{ Mangle(buf []byte) []byte } 16 17 // Mangler is a function that will take an input interface value of known 18 // type, and append it in mangled serialized form to the given byte buffer. 19 // While the value type is an interface, the Mangler functions are accessed 20 // by the value's runtime type pointer, allowing the input value type to be known. 21 type Mangler func(buf []byte, value any) []byte 22 23 // Get will fetch the Mangler function for given runtime type. 24 // Note that the returned mangler will be a no-op in the case 25 // that an incorrect type is passed as the value argument. 26 func Get(t reflect.Type) Mangler { 27 var mng Mangler 28 29 // Get raw runtime type ptr 30 uptr := uintptr(eface_data(t)) 31 32 // Look for a cached mangler 33 v, ok := manglers.Load(uptr) 34 35 if !ok { 36 // Load mangler function 37 mng = loadMangler(nil, t) 38 } else { 39 // cast cached value 40 mng = v.(Mangler) 41 } 42 43 // Get platform int mangler func. 44 mangle_int := mangle_platform_int() 45 46 return func(buf []byte, value any) []byte { 47 // Type check passed against original type. 48 if vt := reflect.TypeOf(value); vt != t { 49 return buf 50 } 51 52 // First write the type ptr (this adds 53 // a unique prefix for each runtime type). 54 buf = mangle_int(buf, uptr) 55 56 // Finally, mangle value 57 return mng(buf, value) 58 } 59 } 60 61 // Register will register the given Mangler function for use with vars of given runtime type. This allows 62 // registering performant manglers for existing types not implementing Mangled (e.g. std library types). 63 // NOTE: panics if there already exists a Mangler function for given type. Register on init(). 64 func Register(t reflect.Type, m Mangler) { 65 if t == nil { 66 // Nil interface{} types cannot be searched by, do not accept 67 panic("cannot register mangler for nil interface{} type") 68 } 69 70 // Get raw runtime type ptr 71 uptr := uintptr(eface_data(t)) 72 73 // Ensure this is a unique encoder 74 if _, ok := manglers.Load(uptr); ok { 75 panic("already registered mangler for type: " + t.String()) 76 } 77 78 // Cache this encoder func 79 manglers.Store(uptr, m) 80 } 81 82 // Append will append the mangled form of input value 'a' to buffer 'b'. 83 // See mangler.String() for more information on mangled output. 84 func Append(b []byte, a any) []byte { 85 var mng Mangler 86 87 // Get reflect type of 'a' 88 t := reflect.TypeOf(a) 89 90 // Get raw runtime type ptr 91 uptr := uintptr(eface_data(t)) 92 93 // Look for a cached mangler 94 v, ok := manglers.Load(uptr) 95 96 if !ok { 97 // Load mangler into cache 98 mng = loadMangler(nil, t) 99 manglers.Store(uptr, mng) 100 } else { 101 // cast cached value 102 mng = v.(Mangler) 103 } 104 105 // Get platform int mangler func. 106 mangle_int := mangle_platform_int() 107 108 // First write the type ptr (this adds 109 // a unique prefix for each runtime type). 110 b = mangle_int(b, uptr) 111 112 // Finally, mangle value 113 return mng(b, a) 114 } 115 116 // String will return the mangled format of input value 'a'. This 117 // mangled output will be unique for all default supported input types 118 // during a single runtime instance. Uniqueness cannot be guaranteed 119 // between separate runtime instances (whether running concurrently, or 120 // the same application running at different times). 121 // 122 // The exact formatting of the output data should not be relied upon, 123 // only that it is unique given the above constraints. Generally though, 124 // the mangled output is the binary formatted text of given input data. 125 // 126 // Uniqueness is guaranteed for similar input data of differing types 127 // (e.g. string("hello world") vs. []byte("hello world")) by prefixing 128 // mangled output with the input data's runtime type pointer. 129 // 130 // Default supported types include: 131 // - string 132 // - bool 133 // - int,int8,int16,int32,int64 134 // - uint,uint8,uint16,uint32,uint64,uintptr 135 // - float32,float64 136 // - complex64,complex128 137 // - arbitrary structs 138 // - all type aliases of above 139 // - time.Time{} 140 // - url.URL{} 141 // - net.IPAddr{} 142 // - netip.Addr{}, netip.AddrPort{} 143 // - mangler.Mangled{} 144 // - fmt.Stringer{} 145 // - json.Marshaler{} 146 // - encoding.BinaryMarshaler{} 147 // - encoding.TextMarshaler{} 148 // - all pointers to the above 149 // - all slices / arrays of the above 150 // - all map keys / values of the above 151 func String(a any) string { 152 b := Append(make([]byte, 0, 32), a) 153 return *(*string)(unsafe.Pointer(&b)) 154 }