mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile, runtime: new static name encoding
Create a byte encoding designed for static Go names. It is intended to be a compact representation of a name and optional tag data that can be turned into a Go string without allocating, and describes whether or not it is exported without unicode table. The encoding is described in reflect/type.go: // The first byte is a bit field containing: // // 1<<0 the name is exported // 1<<1 tag data follows the name // 1<<2 pkgPath *string follow the name and tag // // The next two bytes are the data length: // // l := uint16(data[1])<<8 | uint16(data[2]) // // Bytes [3:3+l] are the string data. // // If tag data follows then bytes 3+l and 3+l+1 are the tag length, // with the data following. // // If the import path follows, then ptrSize bytes at the end of // the data form a *string. The import path is only set for concrete // methods that are defined in a different package than their type. Shrinks binary sizes: cmd/go: 164KB (1.6%) jujud: 1.0MB (1.5%) For #6853. Change-Id: I46b6591015b17936a443c9efb5009de8dfe8b609 Reviewed-on: https://go-review.googlesource.com/20968 Run-TryBot: David Crawshaw <crawshaw@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
0a82ed5d7c
commit
24ce64d1a9
6 changed files with 387 additions and 138 deletions
|
|
@ -288,11 +288,10 @@ type typeAlg struct {
|
|||
|
||||
// Method on non-interface type
|
||||
type method struct {
|
||||
name *string // name of method
|
||||
pkgPath *string // nil for exported Names; otherwise import path
|
||||
mtyp *rtype // method type (without receiver)
|
||||
ifn unsafe.Pointer // fn used in interface call (one-word receiver)
|
||||
tfn unsafe.Pointer // fn used for normal method call
|
||||
name name // name of method
|
||||
mtyp *rtype // method type (without receiver)
|
||||
ifn unsafe.Pointer // fn used in interface call (one-word receiver)
|
||||
tfn unsafe.Pointer // fn used for normal method call
|
||||
}
|
||||
|
||||
// uncommonType is present only for types with names or methods
|
||||
|
|
@ -347,14 +346,14 @@ type funcType struct {
|
|||
|
||||
// imethod represents a method on an interface type
|
||||
type imethod struct {
|
||||
name *string // name of method
|
||||
pkgPath *string // nil for exported Names; otherwise import path
|
||||
typ *rtype // .(*FuncType) underneath
|
||||
name name // name of method
|
||||
typ *rtype // .(*FuncType) underneath
|
||||
}
|
||||
|
||||
// interfaceType represents an interface type.
|
||||
type interfaceType struct {
|
||||
rtype `reflect:"interface"`
|
||||
pkgPath *string // import path
|
||||
methods []imethod // sorted by hash
|
||||
}
|
||||
|
||||
|
|
@ -388,17 +387,101 @@ type sliceType struct {
|
|||
|
||||
// Struct field
|
||||
type structField struct {
|
||||
name *string // nil for embedded fields
|
||||
pkgPath *string // nil for exported Names; otherwise import path
|
||||
typ *rtype // type of field
|
||||
tag *string // nil if no tag
|
||||
offset uintptr // byte offset of field within struct
|
||||
name name // name is empty for embedded fields
|
||||
typ *rtype // type of field
|
||||
offset uintptr // byte offset of field within struct
|
||||
}
|
||||
|
||||
// structType represents a struct type.
|
||||
type structType struct {
|
||||
rtype `reflect:"struct"`
|
||||
fields []structField // sorted by offset
|
||||
rtype `reflect:"struct"`
|
||||
pkgPath *string
|
||||
fields []structField // sorted by offset
|
||||
}
|
||||
|
||||
// name is an encoded type name with optional extra data.
|
||||
//
|
||||
// The first byte is a bit field containing:
|
||||
//
|
||||
// 1<<0 the name is exported
|
||||
// 1<<1 tag data follows the name
|
||||
// 1<<2 pkgPath *string follow the name and tag
|
||||
//
|
||||
// The next two bytes are the data length:
|
||||
//
|
||||
// l := uint16(data[1])<<8 | uint16(data[2])
|
||||
//
|
||||
// Bytes [3:3+l] are the string data.
|
||||
//
|
||||
// If tag data follows then bytes 3+l and 3+l+1 are the tag length,
|
||||
// with the data following.
|
||||
//
|
||||
// If the import path follows, then ptrSize bytes at the end of
|
||||
// the data form a *string. The pointer is aligned to its width.
|
||||
// The import path is only set for concrete methods that are defined
|
||||
// in a different package than their type.
|
||||
type name struct {
|
||||
bytes *byte
|
||||
}
|
||||
|
||||
func (n *name) data(off int) *byte {
|
||||
return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off)))
|
||||
}
|
||||
|
||||
func (n *name) isExported() bool {
|
||||
return (*n.bytes)&(1<<0) != 0
|
||||
}
|
||||
|
||||
func (n *name) nameLen() int {
|
||||
return int(uint16(*n.data(1))<<8 | uint16(*n.data(2)))
|
||||
}
|
||||
|
||||
func (n *name) tagLen() int {
|
||||
if *n.data(0)&(1<<1) == 0 {
|
||||
return 0
|
||||
}
|
||||
off := 3 + n.nameLen()
|
||||
return int(uint16(*n.data(off))<<8 | uint16(*n.data(off + 1)))
|
||||
}
|
||||
|
||||
func (n *name) name() (s string) {
|
||||
nl := n.nameLen()
|
||||
if nl == 0 {
|
||||
return ""
|
||||
}
|
||||
hdr := (*stringHeader)(unsafe.Pointer(&s))
|
||||
hdr.Data = unsafe.Pointer(n.data(3))
|
||||
hdr.Len = nl
|
||||
return s
|
||||
}
|
||||
|
||||
func (n *name) tag() (s string) {
|
||||
tl := n.tagLen()
|
||||
if tl == 0 {
|
||||
return ""
|
||||
}
|
||||
nl := n.nameLen()
|
||||
hdr := (*stringHeader)(unsafe.Pointer(&s))
|
||||
hdr.Data = unsafe.Pointer(n.data(3 + nl + 2))
|
||||
hdr.Len = tl
|
||||
return s
|
||||
}
|
||||
|
||||
func (n *name) pkgPath() *string {
|
||||
if *n.data(0)&(1<<2) == 0 {
|
||||
return nil
|
||||
}
|
||||
off := 3 + n.nameLen()
|
||||
if tl := n.tagLen(); tl > 0 {
|
||||
off += 2 + tl
|
||||
}
|
||||
off = int(round(uintptr(off), ptrSize))
|
||||
return *(**string)(unsafe.Pointer(n.data(off)))
|
||||
}
|
||||
|
||||
// round n up to a multiple of a. a must be a power of 2.
|
||||
func round(n, a uintptr) uintptr {
|
||||
return (n + a - 1) &^ (a - 1)
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -583,12 +666,14 @@ func (t *rtype) Method(i int) (m Method) {
|
|||
panic("reflect: Method index out of range")
|
||||
}
|
||||
p := &ut.methods[i]
|
||||
if p.name != nil {
|
||||
m.Name = *p.name
|
||||
}
|
||||
m.Name = p.name.name()
|
||||
fl := flag(Func)
|
||||
if p.pkgPath != nil {
|
||||
m.PkgPath = *p.pkgPath
|
||||
if !p.name.isExported() {
|
||||
pkgPath := p.name.pkgPath()
|
||||
if pkgPath == nil {
|
||||
pkgPath = ut.pkgPath
|
||||
}
|
||||
m.PkgPath = *pkgPath
|
||||
fl |= flagStickyRO
|
||||
}
|
||||
if p.mtyp != nil {
|
||||
|
|
@ -620,10 +705,9 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) {
|
|||
if ut == nil {
|
||||
return Method{}, false
|
||||
}
|
||||
var p *method
|
||||
for i := range ut.methods {
|
||||
p = &ut.methods[i]
|
||||
if p.name != nil && *p.name == name {
|
||||
p := &ut.methods[i]
|
||||
if p.name.name() == name {
|
||||
return t.Method(i), true
|
||||
}
|
||||
}
|
||||
|
|
@ -832,9 +916,13 @@ func (t *interfaceType) Method(i int) (m Method) {
|
|||
return
|
||||
}
|
||||
p := &t.methods[i]
|
||||
m.Name = *p.name
|
||||
if p.pkgPath != nil {
|
||||
m.PkgPath = *p.pkgPath
|
||||
m.Name = p.name.name()
|
||||
if !p.name.isExported() {
|
||||
pkgPath := p.name.pkgPath()
|
||||
if pkgPath == nil {
|
||||
pkgPath = t.pkgPath
|
||||
}
|
||||
m.PkgPath = *pkgPath
|
||||
}
|
||||
m.Type = toType(p.typ)
|
||||
m.Index = i
|
||||
|
|
@ -852,7 +940,7 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) {
|
|||
var p *imethod
|
||||
for i := range t.methods {
|
||||
p = &t.methods[i]
|
||||
if *p.name == name {
|
||||
if p.name.name() == name {
|
||||
return t.Method(i), true
|
||||
}
|
||||
}
|
||||
|
|
@ -950,8 +1038,8 @@ func (t *structType) Field(i int) (f StructField) {
|
|||
}
|
||||
p := &t.fields[i]
|
||||
f.Type = toType(p.typ)
|
||||
if p.name != nil {
|
||||
f.Name = *p.name
|
||||
if name := p.name.name(); name != "" {
|
||||
f.Name = name
|
||||
} else {
|
||||
t := f.Type
|
||||
if t.Kind() == Ptr {
|
||||
|
|
@ -960,11 +1048,12 @@ func (t *structType) Field(i int) (f StructField) {
|
|||
f.Name = t.Name()
|
||||
f.Anonymous = true
|
||||
}
|
||||
if p.pkgPath != nil {
|
||||
f.PkgPath = *p.pkgPath
|
||||
if t.pkgPath != nil && !p.name.isExported() {
|
||||
// Fields never have an import path in their name.
|
||||
f.PkgPath = *t.pkgPath
|
||||
}
|
||||
if p.tag != nil {
|
||||
f.Tag = StructTag(*p.tag)
|
||||
if tag := p.name.tag(); tag != "" {
|
||||
f.Tag = StructTag(tag)
|
||||
}
|
||||
f.Offset = p.offset
|
||||
|
||||
|
|
@ -1056,8 +1145,8 @@ func (t *structType) FieldByNameFunc(match func(string) bool) (result StructFiel
|
|||
// Find name and type for field f.
|
||||
var fname string
|
||||
var ntyp *rtype
|
||||
if f.name != nil {
|
||||
fname = *f.name
|
||||
if name := f.name.name(); name != "" {
|
||||
fname = name
|
||||
} else {
|
||||
// Anonymous field of type T or *T.
|
||||
// Name taken from type.
|
||||
|
|
@ -1122,11 +1211,12 @@ func (t *structType) FieldByName(name string) (f StructField, present bool) {
|
|||
if name != "" {
|
||||
for i := range t.fields {
|
||||
tf := &t.fields[i]
|
||||
if tf.name == nil {
|
||||
tfname := tf.name.name()
|
||||
if tfname == "" {
|
||||
hasAnon = true
|
||||
continue
|
||||
}
|
||||
if *tf.name == name {
|
||||
if tfname == name {
|
||||
return t.Field(i), true
|
||||
}
|
||||
}
|
||||
|
|
@ -1278,7 +1368,7 @@ func implements(T, V *rtype) bool {
|
|||
for j := 0; j < len(v.methods); j++ {
|
||||
tm := &t.methods[i]
|
||||
vm := &v.methods[j]
|
||||
if *vm.name == *tm.name && vm.pkgPath == tm.pkgPath && vm.typ == tm.typ {
|
||||
if vm.name.name() == tm.name.name() && vm.typ == tm.typ {
|
||||
if i++; i >= len(t.methods) {
|
||||
return true
|
||||
}
|
||||
|
|
@ -1295,7 +1385,7 @@ func implements(T, V *rtype) bool {
|
|||
for j := 0; j < len(v.methods); j++ {
|
||||
tm := &t.methods[i]
|
||||
vm := &v.methods[j]
|
||||
if *vm.name == *tm.name && vm.pkgPath == tm.pkgPath && vm.mtyp == tm.typ {
|
||||
if vm.name.name() == tm.name.name() && vm.mtyp == tm.typ {
|
||||
if i++; i >= len(t.methods) {
|
||||
return true
|
||||
}
|
||||
|
|
@ -1400,16 +1490,13 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool {
|
|||
for i := range t.fields {
|
||||
tf := &t.fields[i]
|
||||
vf := &v.fields[i]
|
||||
if tf.name != vf.name && (tf.name == nil || vf.name == nil || *tf.name != *vf.name) {
|
||||
return false
|
||||
}
|
||||
if tf.pkgPath != vf.pkgPath && (tf.pkgPath == nil || vf.pkgPath == nil || *tf.pkgPath != *vf.pkgPath) {
|
||||
if tf.name.name() != vf.name.name() {
|
||||
return false
|
||||
}
|
||||
if tf.typ != vf.typ {
|
||||
return false
|
||||
}
|
||||
if tf.tag != vf.tag && (tf.tag == nil || vf.tag == nil || *tf.tag != *vf.tag) {
|
||||
if tf.name.tag() != vf.name.tag() {
|
||||
return false
|
||||
}
|
||||
if tf.offset != vf.offset {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue