github.com/goplus/reflectx@v1.2.2/name_go117.go (about)

     1  //go:build go1.17 && (!js || (js && wasm))
     2  // +build go1.17
     3  // +build !js js,wasm
     4  
     5  package reflectx
     6  
     7  import "unsafe"
     8  
     9  // name is an encoded type name with optional extra data.
    10  //
    11  // The first byte is a bit field containing:
    12  //
    13  //	1<<0 the name is exported
    14  //	1<<1 tag data follows the name
    15  //	1<<2 pkgPath nameOff follows the name and tag
    16  //
    17  // Following that, there is a varint-encoded length of the name,
    18  // followed by the name itself.
    19  //
    20  // If tag data is present, it also has a varint-encoded length
    21  // followed by the tag itself.
    22  //
    23  // If the import path follows, then 4 bytes at the end of
    24  // the data form a nameOff. The import path is only set for concrete
    25  // methods that are defined in a different package than their type.
    26  //
    27  // If a name starts with "*", then the exported bit represents
    28  // whether the pointed to type is exported.
    29  //
    30  // Note: this encoding must match here and in:
    31  //   cmd/compile/internal/reflectdata/reflect.go
    32  //   runtime/type.go
    33  //   internal/reflectlite/type.go
    34  //   cmd/link/internal/ld/decodesym.go
    35  
    36  type name struct {
    37  	bytes *byte
    38  }
    39  
    40  func (n name) data(off int, whySafe string) *byte {
    41  	return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off), whySafe))
    42  }
    43  
    44  func (n name) isExported() bool {
    45  	return (*n.bytes)&(1<<0) != 0
    46  }
    47  
    48  // go1.19
    49  func (n name) embedded() bool {
    50  	return (*n.bytes)&(1<<3) != 0
    51  }
    52  
    53  // go1.19
    54  func (n name) setEmbedded() {
    55  	(*n.bytes) |= 1 << 3
    56  }
    57  
    58  func (n name) hasTag() bool {
    59  	return (*n.bytes)&(1<<1) != 0
    60  }
    61  
    62  // readVarint parses a varint as encoded by encoding/binary.
    63  // It returns the number of encoded bytes and the encoded value.
    64  func (n name) readVarint(off int) (int, int) {
    65  	v := 0
    66  	for i := 0; ; i++ {
    67  		x := *n.data(off+i, "read varint")
    68  		v += int(x&0x7f) << (7 * i)
    69  		if x&0x80 == 0 {
    70  			return i + 1, v
    71  		}
    72  	}
    73  }
    74  
    75  // writeVarint writes n to buf in varint form. Returns the
    76  // number of bytes written. n must be nonnegative.
    77  // Writes at most 10 bytes.
    78  func writeVarint(buf []byte, n int) int {
    79  	for i := 0; ; i++ {
    80  		b := byte(n & 0x7f)
    81  		n >>= 7
    82  		if n == 0 {
    83  			buf[i] = b
    84  			return i + 1
    85  		}
    86  		buf[i] = b | 0x80
    87  	}
    88  }
    89  
    90  func (n name) name() (s string) {
    91  	if n.bytes == nil {
    92  		return
    93  	}
    94  	i, l := n.readVarint(1)
    95  	hdr := (*stringHeader)(unsafe.Pointer(&s))
    96  	hdr.Data = unsafe.Pointer(n.data(1+i, "non-empty string"))
    97  	hdr.Len = l
    98  	return
    99  }
   100  
   101  func (n name) tag() (s string) {
   102  	if !n.hasTag() {
   103  		return ""
   104  	}
   105  	i, l := n.readVarint(1)
   106  	i2, l2 := n.readVarint(1 + i + l)
   107  	hdr := (*stringHeader)(unsafe.Pointer(&s))
   108  	hdr.Data = unsafe.Pointer(n.data(1+i+l+i2, "non-empty string"))
   109  	hdr.Len = l2
   110  	return
   111  }
   112  
   113  func (n name) pkgPath() string {
   114  	if n.bytes == nil || *n.data(0, "name flag field")&(1<<2) == 0 {
   115  		return ""
   116  	}
   117  	i, l := n.readVarint(1)
   118  	off := 1 + i + l
   119  	if n.hasTag() {
   120  		i2, l2 := n.readVarint(off)
   121  		off += i2 + l2
   122  	}
   123  	var nameOff int32
   124  	// Note that this field may not be aligned in memory,
   125  	// so we cannot use a direct int32 assignment here.
   126  	copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off, "name offset field")))[:])
   127  	pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n.bytes), nameOff))}
   128  	return pkgPathName.name()
   129  }
   130  
   131  func (n name) setPkgPath(pkgpath string) {
   132  	if n.bytes == nil || *n.data(0, "name flag pkgPath")&(1<<2) == 0 {
   133  		return
   134  	}
   135  	i, l := n.readVarint(1)
   136  	off := 1 + i + l
   137  	if n.hasTag() {
   138  		i2, l2 := n.readVarint(off)
   139  		off += i2 + l2
   140  	}
   141  	v := resolveReflectName(newName(pkgpath, "", false))
   142  	copy((*[4]byte)(unsafe.Pointer(n.data(off, "name offset pkgPath")))[:], (*[4]byte)(unsafe.Pointer(&v))[:])
   143  }
   144  
   145  func newNameEx(n, tag string, exported bool, pkgpath bool) name {
   146  	if len(n) >= 1<<29 {
   147  		panic("reflect.nameFrom: name too long: " + n[:1024] + "...")
   148  	}
   149  	if len(tag) >= 1<<29 {
   150  		panic("reflect.nameFrom: tag too long: " + tag[:1024] + "...")
   151  	}
   152  	var nameLen [10]byte
   153  	var tagLen [10]byte
   154  	nameLenLen := writeVarint(nameLen[:], len(n))
   155  	tagLenLen := writeVarint(tagLen[:], len(tag))
   156  
   157  	var bits byte
   158  	l := 1 + nameLenLen + len(n)
   159  	if exported {
   160  		bits |= 1 << 0
   161  	}
   162  	if len(tag) > 0 {
   163  		l += tagLenLen + len(tag)
   164  		bits |= 1 << 1
   165  	}
   166  	if !exported && pkgpath {
   167  		bits |= 1 << 2
   168  		l += 4
   169  	}
   170  
   171  	b := make([]byte, l)
   172  	b[0] = bits
   173  	copy(b[1:], nameLen[:nameLenLen])
   174  	copy(b[1+nameLenLen:], n)
   175  	if len(tag) > 0 {
   176  		tb := b[1+nameLenLen+len(n):]
   177  		copy(tb, tagLen[:tagLenLen])
   178  		copy(tb[tagLenLen:], tag)
   179  	}
   180  
   181  	return name{bytes: &b[0]}
   182  }