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  }