github.com/timandy/routine@v1.1.4-0.20240507073150-e4a3e1fe2ba5/g/reflect.go (about)

     1  // Copyright 2021-2024 TimAndy. All rights reserved.
     2  // Licensed under the Apache-2.0 license that can be found in the LICENSE file.
     3  
     4  package g
     5  
     6  import (
     7  	"reflect"
     8  	"unsafe"
     9  )
    10  
    11  // eface The empty interface struct.
    12  type eface struct {
    13  	_type unsafe.Pointer
    14  	data  unsafe.Pointer
    15  }
    16  
    17  // iface The interface struct.
    18  type iface struct {
    19  	tab  unsafe.Pointer
    20  	data unsafe.Pointer
    21  }
    22  
    23  // typelinks returns a slice of the sections in each module, and a slice of *rtype offsets in each module. The types in each module are sorted by string.
    24  //
    25  //go:linkname typelinks reflect.typelinks
    26  func typelinks() (sections []unsafe.Pointer, offset [][]int32)
    27  
    28  // resolveTypeOff resolves an *rtype offset from a base type.
    29  //
    30  //go:linkname resolveTypeOff reflect.resolveTypeOff
    31  func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer
    32  
    33  // packEface returns an empty interface representing a value of the specified type, using p as the pointer to the data.
    34  func packEface(typ reflect.Type, p unsafe.Pointer) (i interface{}) {
    35  	t := (*iface)(unsafe.Pointer(&typ))
    36  	e := (*eface)(unsafe.Pointer(&i))
    37  	e._type = t.data
    38  	e.data = p
    39  	return
    40  }
    41  
    42  // typeByString returns the type whose 'String' property equals to the given string, or nil if not found.
    43  func typeByString(str string) reflect.Type {
    44  	// The s is search target
    45  	s := str
    46  	if len(str) == 0 || str[0] != '*' {
    47  		s = "*" + s
    48  	}
    49  	// The typ is a struct iface{tab(ptr->reflect.Type), data(ptr->rtype)}
    50  	typ := reflect.TypeOf(0)
    51  	face := (*iface)(unsafe.Pointer(&typ))
    52  	// Find the specified target through binary search algorithm
    53  	sections, offset := typelinks()
    54  	for offsI, offs := range offset {
    55  		section := sections[offsI]
    56  		// We are looking for the first index i where the string becomes >= s.
    57  		// This is a copy of sort.Search, with f(h) replaced by (*typ[h].String() >= s).
    58  		i, j := 0, len(offs)
    59  		for i < j {
    60  			h := i + (j-i)/2 // avoid overflow when computing h
    61  			// i ≤ h < j
    62  			face.data = resolveTypeOff(section, offs[h])
    63  			if !(typ.String() >= s) {
    64  				i = h + 1 // preserves f(i-1) == false
    65  			} else {
    66  				j = h // preserves f(j) == true
    67  			}
    68  		}
    69  		// i == j, f(i-1) == false, and f(j) (= f(i)) == true  =>  answer is i.
    70  		// Having found the first, linear scan forward to find the last.
    71  		// We could do a second binary search, but the caller is going
    72  		// to do a linear scan anyway.
    73  		if i < len(offs) {
    74  			face.data = resolveTypeOff(section, offs[i])
    75  			if typ.Kind() == reflect.Ptr {
    76  				if typ.String() == str {
    77  					return typ
    78  				}
    79  				elem := typ.Elem()
    80  				if elem.String() == str {
    81  					return elem
    82  				}
    83  			}
    84  		}
    85  	}
    86  	return nil
    87  }