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 }