package mangler import ( "encoding" "net/url" "reflect" "time" ) // loadMangler is the top-most Mangler load function. It guarantees that a Mangler // function will be returned for given value interface{} and reflected type. Else panics. func loadMangler(a any, t reflect.Type) Mangler { // Load mangler function mng, rmng := load(a, t) if rmng != nil { // Wrap reflect mangler to handle iface return func(buf []byte, a any) []byte { return rmng(buf, reflect.ValueOf(a)) } } if mng == nil { // No mangler function could be determined panic("cannot mangle type: " + t.String()) } return mng } // load will load a Mangler or reflect Mangler for given type and iface 'a'. // Note: allocates new interface value if nil provided, i.e. if coming via reflection. func load(a any, t reflect.Type) (Mangler, rMangler) { if t == nil { // There is no reflect type to search by panic("cannot mangle nil interface{} type") } if a == nil { // Alloc new iface instance v := reflect.New(t).Elem() a = v.Interface() } // Check in fast iface type switch if mng := loadIface(a); mng != nil { return mng, nil } // Search by reflection return loadReflect(t) } // loadIface is used as a first-resort interface{} type switcher loader // for types implementing Mangled and providing performant alternative // Mangler functions for standard library types to avoid reflection. func loadIface(a any) Mangler { switch a.(type) { case Mangled: return mangle_mangled case time.Time: return mangle_time case *time.Time: return mangle_time_ptr case *url.URL: return mangle_stringer case encoding.BinaryMarshaler: return mangle_binary // NOTE: // we don't just handle ALL fmt.Stringer types as often // the output is large and unwieldy and this interface // switch is for types it would be faster to avoid reflection. // If they want better performance they can implement Mangled{}. default: return nil } } // loadReflect will load a Mangler (or rMangler) function for the given reflected type info. // NOTE: this is used as the top level load function for nested reflective searches. func loadReflect(t reflect.Type) (Mangler, rMangler) { switch t.Kind() { case reflect.Pointer: return loadReflectPtr(t.Elem()) case reflect.String: return mangle_string, nil case reflect.Array: return nil, loadReflectArray(t.Elem()) case reflect.Slice: // Element type et := t.Elem() // Preferably look for known slice mangler func if mng := loadReflectKnownSlice(et); mng != nil { return mng, nil } // Else handle as array elements return nil, loadReflectArray(et) case reflect.Map: return nil, loadReflectMap(t.Key(), t.Elem()) case reflect.Bool: return mangle_bool, nil case reflect.Int, reflect.Uint, reflect.Uintptr: return mangle_platform_int, nil case reflect.Int8, reflect.Uint8: return mangle_8bit, nil case reflect.Int16, reflect.Uint16: return mangle_16bit, nil case reflect.Int32, reflect.Uint32: return mangle_32bit, nil case reflect.Int64, reflect.Uint64: return mangle_64bit, nil case reflect.Float32: return mangle_32bit, nil case reflect.Float64: return mangle_64bit, nil case reflect.Complex64: return mangle_64bit, nil case reflect.Complex128: return mangle_128bit, nil default: return nil, nil } } // loadReflectPtr loads a Mangler (or rMangler) function for a ptr's element type. // This also handles further dereferencing of any further ptr indrections (e.g. ***int). func loadReflectPtr(et reflect.Type) (Mangler, rMangler) { count := 1 // Iteratively dereference ptrs for et.Kind() == reflect.Pointer { et = et.Elem() count++ } if et.Kind() == reflect.Array { // Special case of addressable (sliceable) array if mng := loadReflectKnownSlice(et); mng != nil { if count == 1 { return mng, nil } return nil, deref_ptr_mangler(mng, count-1) } // Look for an array mangler function, this will // access elements by index using reflect.Value and // pass each one to a separate mangler function. if rmng := loadReflectArray(et); rmng != nil { return nil, deref_ptr_rmangler(rmng, count) } return nil, nil } // Try remove a layer of derefs by loading a mangler // for a known ptr kind. The less reflection the better! if mng := loadReflectKnownPtr(et); mng != nil { if count == 1 { return mng, nil } return nil, deref_ptr_mangler(mng, count-1) } // Search for ptr elemn type mangler if mng, rmng := load(nil, et); mng != nil { return nil, deref_ptr_mangler(mng, count) } else if rmng != nil { return nil, deref_ptr_rmangler(rmng, count) } return nil, nil } // loadReflectKnownPtr loads a Mangler function for a known ptr-of-element type (in this case, primtive ptrs). func loadReflectKnownPtr(et reflect.Type) Mangler { switch et.Kind() { case reflect.String: return mangle_string_ptr case reflect.Bool: return mangle_bool_ptr case reflect.Int, reflect.Uint, reflect.Uintptr: return mangle_platform_int_ptr case reflect.Int8, reflect.Uint8: return mangle_8bit_ptr case reflect.Int16, reflect.Uint16: return mangle_16bit_ptr case reflect.Int32, reflect.Uint32: return mangle_32bit_ptr case reflect.Int64, reflect.Uint64: return mangle_64bit_ptr case reflect.Float32: return mangle_32bit_ptr case reflect.Float64: return mangle_64bit_ptr case reflect.Complex64: return mangle_64bit_ptr case reflect.Complex128: return mangle_128bit_ptr default: return nil } } // loadReflectKnownSlice loads a Mangler function for a known slice-of-element type (in this case, primtives). func loadReflectKnownSlice(et reflect.Type) Mangler { switch et.Kind() { case reflect.String: return mangle_string_slice case reflect.Bool: return mangle_bool_slice case reflect.Int, reflect.Uint, reflect.Uintptr: return mangle_platform_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 } } // loadReflectArray loads an rMangler function for an array (or slice) or given element type. func loadReflectArray(et reflect.Type) rMangler { // Search via reflected array element type if mng, rmng := load(nil, et); mng != nil { return iter_array_mangler(mng) } else if rmng != nil { return iter_array_rmangler(rmng) } return nil } // loadReflectMap ... func loadReflectMap(kt, vt reflect.Type) rMangler { var kmng, vmng rMangler // Search for mangler for key type mng, rmng := load(nil, kt) switch { // Wrap key mangler to reflect case mng != nil: mng := mng // take our own ptr kmng = func(buf []byte, v reflect.Value) []byte { return mng(buf, v.Interface()) } // Use reflect key mangler as-is case rmng != nil: kmng = rmng // No mangler found default: return nil } // Search for mangler for value type mng, rmng = load(nil, vt) switch { // Wrap key mangler to reflect case mng != nil: mng := mng // take our own ptr vmng = func(buf []byte, v reflect.Value) []byte { return mng(buf, v.Interface()) } // Use reflect key mangler as-is case rmng != nil: vmng = rmng // No mangler found default: return nil } // Wrap key/value manglers in map iter return iter_map_rmangler(kmng, vmng) }