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