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  }