github.com/goplusjs/reflectx@v0.5.4/name.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 // The next two bytes are the data length: 18 // 19 // l := uint16(data[1])<<8 | uint16(data[2]) 20 // 21 // Bytes [3:3+l] are the string data. 22 // 23 // If tag data follows then bytes 3+l and 3+l+1 are the tag length, 24 // with the data following. 25 // 26 // If the import path follows, then 4 bytes at the end of 27 // the data form a nameOff. The import path is only set for concrete 28 // methods that are defined in a different package than their type. 29 // 30 // If a name starts with "*", then the exported bit represents 31 // whether the pointed to type is exported. 32 type name struct { 33 bytes *byte 34 } 35 36 func (n name) data(off int, whySafe string) *byte { 37 return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off), whySafe)) 38 } 39 40 func (n name) isExported() bool { 41 return (*n.bytes)&(1<<0) != 0 42 } 43 44 func (n name) nameLen() int { 45 return int(uint16(*n.data(1, "name len field"))<<8 | uint16(*n.data(2, "name len field"))) 46 } 47 48 func (n name) tagLen() int { 49 if *n.data(0, "name flag field")&(1<<1) == 0 { 50 return 0 51 } 52 off := 3 + n.nameLen() 53 return int(uint16(*n.data(off, "name taglen field"))<<8 | uint16(*n.data(off+1, "name taglen field"))) 54 } 55 56 func (n name) name() (s string) { 57 if n.bytes == nil { 58 return 59 } 60 b := (*[4]byte)(unsafe.Pointer(n.bytes)) 61 62 hdr := (*stringHeader)(unsafe.Pointer(&s)) 63 hdr.Data = unsafe.Pointer(&b[3]) 64 hdr.Len = int(b[1])<<8 | int(b[2]) 65 return s 66 } 67 68 func (n name) tag() (s string) { 69 tl := n.tagLen() 70 if tl == 0 { 71 return "" 72 } 73 nl := n.nameLen() 74 hdr := (*stringHeader)(unsafe.Pointer(&s)) 75 hdr.Data = unsafe.Pointer(n.data(3+nl+2, "non-empty string")) 76 hdr.Len = tl 77 return s 78 } 79 80 func (n name) pkgPath() string { 81 if n.bytes == nil || *n.data(0, "name flag field")&(1<<2) == 0 { 82 return "" 83 } 84 off := 3 + n.nameLen() 85 if tl := n.tagLen(); tl > 0 { 86 off += 2 + tl 87 } 88 var nameOff int32 89 90 // copy((*[4]byte)(unsafe.Pointer(n.data(off, "name offset field")))[:], (*[4]byte)(unsafe.Pointer(&nameOff))[:]) 91 92 // Note that this field may not be aligned in memory, 93 // so we cannot use a direct int32 assignment here. 94 copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off, "name offset field")))[:]) 95 pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n.bytes), nameOff))} 96 return pkgPathName.name() 97 } 98 99 func (n name) setPkgPath(pkgpath nameOff) bool { 100 if n.bytes == nil || *n.data(0, "name flag field")&(1<<2) == 0 { 101 return false 102 } 103 off := 3 + n.nameLen() 104 if tl := n.tagLen(); tl > 0 { 105 off += 2 + tl 106 } 107 copy((*[4]byte)(unsafe.Pointer(n.data(off, "name offset field")))[:], (*[4]byte)(unsafe.Pointer(&pkgpath))[:]) 108 return true 109 } 110 111 func newNameEx(n, tag string, exported bool, pkgpath bool) name { 112 if len(n) > 1<<16-1 { 113 panic("reflect.nameFrom: name too long: " + n) 114 } 115 if len(tag) > 1<<16-1 { 116 panic("reflect.nameFrom: tag too long: " + tag) 117 } 118 119 var bits byte 120 l := 1 + 2 + len(n) 121 if exported { 122 bits |= 1 << 0 123 } 124 if len(tag) > 0 { 125 l += 2 + len(tag) 126 bits |= 1 << 1 127 } 128 if !exported && pkgpath { 129 bits |= 1 << 2 130 l += 4 131 } 132 133 b := make([]byte, l) 134 b[0] = bits 135 b[1] = uint8(len(n) >> 8) 136 b[2] = uint8(len(n)) 137 copy(b[3:], n) 138 if len(tag) > 0 { 139 tb := b[3+len(n):] 140 tb[0] = uint8(len(tag) >> 8) 141 tb[1] = uint8(len(tag)) 142 copy(tb[2:], tag) 143 } 144 return name{bytes: &b[0]} 145 }