gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }