github.com/goplusjs/reflectx@v0.5.4/name_go117.go (about)

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