Go: Make StructDesc use slices for names and types instead of a map (#1741)

The map showed up in benchmarks...

Fixes #1716
This commit is contained in:
Erik Arvidsson
2016-06-06 16:58:40 -07:00
parent e4b7d13416
commit bc9f9d19e1
15 changed files with 151 additions and 151 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ func init() {
ValueField: types.ValueType,
}
commitType = types.MakeStructType(structName, fieldTypes)
commitType.Desc.(types.StructDesc).Fields[ParentsField] = types.MakeSetType(types.MakeRefType(commitType))
commitType.Desc.(types.StructDesc).SetField(ParentsField, types.MakeSetType(types.MakeRefType(commitType)))
}
func NewCommit() types.Struct {
+7 -4
View File
@@ -66,10 +66,12 @@ func findType(n string, ts []*types.Type) *types.Type {
// resolveReferences replaces references with the actual Type
func resolveReferences(i *intermediate, aliases map[string][]*types.Type) {
var rec func(t *types.Type) *types.Type
resolveFields := func(fields types.TypeMap) {
for name, t := range fields {
resolveFields := func(desc types.StructDesc) *types.Type {
fields := make(types.TypeMap, desc.Len())
desc.IterFields(func(name string, t *types.Type) {
fields[name] = rec(t)
}
})
return types.MakeStructType(desc.Name, fields)
}
rec = func(t *types.Type) *types.Type {
switch t.Kind() {
@@ -91,7 +93,8 @@ func resolveReferences(i *intermediate, aliases map[string][]*types.Type) {
elemTypes := t.Desc.(types.CompoundDesc).ElemTypes
return types.MakeMapType(rec(elemTypes[0]), rec(elemTypes[1]))
case types.StructKind:
resolveFields(t.Desc.(types.StructDesc).Fields)
return resolveFields(t.Desc.(types.StructDesc))
}
return t
}
+11 -11
View File
@@ -46,21 +46,21 @@ func isSubtype(requiredType, concreteType *Type) bool {
if requiredDesc.Name != "" && requiredDesc.Name != concreteDesc.Name {
return false
}
type Entry struct {
name string
t *Type
}
entries := make([]Entry, 0, len(requiredDesc.Fields))
requiredDesc.IterFields(func(name string, t *Type) {
entries = append(entries, Entry{name, t})
})
for _, entry := range entries {
at, ok := concreteDesc.Fields[entry.name]
if !ok || !isSubtype(entry.t, at) {
j := 0
for _, field := range requiredDesc.fields {
for ; j < concreteDesc.Len() && concreteDesc.fields[j].name != field.name; j++ {
}
if j == concreteDesc.Len() {
return false
}
f := concreteDesc.fields[j]
if !isSubtype(field.t, f.t) {
return false
}
}
return true
}
panic("unreachable")
+2 -2
View File
@@ -205,9 +205,9 @@ func TestAssertTypeStructSubtype(tt *testing.T) {
t11 := MakeStructType("Commit", TypeMap{
"value": NumberType,
"parents": MakeSetType(MakeRefType(NumberType /* placeholder */)),
"parents": NumberType, // placeholder MakeSetType(MakeRefType(NumberType /* placeholder */)),
})
t11.Desc.(StructDesc).Fields["parents"].Desc.(CompoundDesc).ElemTypes[0].Desc.(CompoundDesc).ElemTypes[0] = t11
t11.Desc.(StructDesc).SetField("parents", MakeSetType(MakeRefType(t1)))
assertSubtype(t11, c1)
c2 := NewStruct("Commit", structData{
+4 -4
View File
@@ -279,10 +279,10 @@ func TestRecursiveStruct(t *testing.T) {
"e": nil,
"f": a,
})
a.Desc.(StructDesc).Fields["b"] = a
a.Desc.(StructDesc).Fields["c"] = MakeListType(a)
a.Desc.(StructDesc).Fields["d"] = d
d.Desc.(StructDesc).Fields["e"] = d
a.Desc.(StructDesc).SetField("b", a)
a.Desc.(StructDesc).SetField("c", MakeListType(a))
a.Desc.(StructDesc).SetField("d", d)
d.Desc.(StructDesc).SetField("e", d)
assertWriteHRSEqual(t, `struct A {
b: Cycle<0>
c: List<Cycle<0>>
+1 -1
View File
@@ -462,7 +462,7 @@ func nomsTestWriteRecursiveStruct(t *testing.T) {
listType := MakeListType(structType)
// Mutate...
structType.Desc.(StructDesc).Fields["cs"] = listType
structType.Desc.(StructDesc).SetField("cs", listType)
assertEncoding(t,
[]interface{}{
+1 -1
View File
@@ -66,7 +66,7 @@ func newFieldPart(name string) fieldPart {
func (fp fieldPart) Resolve(v Value) Value {
if s, ok := v.(Struct); ok {
if fv, ok := s.data[fp.name]; ok {
if fv, ok := s.MaybeGet(fp.name); ok {
return fv
}
}
+37 -80
View File
@@ -16,14 +16,18 @@ import (
type structData map[string]Value
type Struct struct {
data structData
t *Type
h *hash.Hash
values []Value
t *Type
h *hash.Hash
}
func newStructFromData(data structData, t *Type) Struct {
d.Chk.True(t.Kind() == StructKind)
return Struct{data, t, &hash.Hash{}}
values := make([]Value, len(data))
for i, field := range t.Desc.(StructDesc).fields {
values[i] = data[field.name]
}
return Struct{values, t, &hash.Hash{}}
}
func NewStruct(name string, data structData) Struct {
@@ -38,15 +42,16 @@ func NewStruct(name string, data structData) Struct {
}
func NewStructWithType(t *Type, data structData) Struct {
newData := make(structData, len(data))
desc := t.Desc.(StructDesc)
for name, t := range desc.Fields {
v, ok := data[name]
d.Chk.True(ok, "Missing required field %s", name)
assertSubtype(t, v)
newData[name] = v
d.Chk.True(len(data) == len(desc.fields))
values := make([]Value, desc.Len())
for i, field := range desc.fields {
v, ok := data[field.name]
d.Chk.True(ok, "Missing required field %s", field.name)
assertSubtype(field.t, v)
values[i] = v
}
return newStructFromData(newData, t)
return Struct{values, t, &hash.Hash{}}
}
func (s Struct) hashPointer() *hash.Hash {
@@ -66,20 +71,13 @@ func (s Struct) Hash() hash.Hash {
return EnsureHash(s.h, s)
}
func (s Struct) ChildValues() (res []Value) {
s.desc().IterFields(func(name string, t *Type) {
v, ok := s.data[name]
d.Chk.True(ok)
res = append(res, v)
})
return
func (s Struct) ChildValues() []Value {
return s.values
}
func (s Struct) Chunks() (chunks []Ref) {
chunks = append(chunks, s.t.Chunks()...)
for name := range s.desc().Fields {
v, ok := s.data[name]
d.Chk.True(ok)
for _, v := range s.values {
chunks = append(chunks, v.Chunks()...)
}
@@ -95,82 +93,41 @@ func (s Struct) desc() StructDesc {
}
func (s Struct) MaybeGet(n string) (Value, bool) {
_, ok := s.findField(n)
if !ok {
_, i := s.desc().findField(n)
if i == -1 {
return nil, false
}
v, ok := s.data[n]
return v, ok
return s.values[i], true
}
func (s Struct) Get(n string) Value {
_, ok := s.findField(n)
d.Chk.True(ok, `Struct has no field "%s"`, n)
v, ok := s.data[n]
d.Chk.True(ok)
return v
f, i := s.desc().findField(n)
d.Chk.True(i != -1, `Struct has no field "%s"`, f.name)
return s.values[i]
}
func (s Struct) Set(n string, v Value) Struct {
t, ok := s.findField(n)
d.Chk.True(ok, "Struct has no field %s", n)
assertSubtype(t, v)
data := make(structData, len(s.data))
for k, v := range s.data {
data[k] = v
}
data[n] = v
f, i := s.desc().findField(n)
d.Chk.True(i != -1, "Struct has no field %s", n)
assertSubtype(f.t, v)
values := make([]Value, len(s.values))
copy(values, s.values)
values[i] = v
return newStructFromData(data, s.t)
}
func (s Struct) findField(n string) (*Type, bool) {
for name, typ := range s.desc().Fields {
if name == n {
return typ, true
}
}
return nil, false
}
func structBuilder(values []Value, t *Type) Value {
desc := t.Desc.(StructDesc)
data := make(map[string]Value, len(desc.Fields))
i := 0
desc.IterFields(func(name string, t *Type) {
data[name] = values[i]
i++
})
return newStructFromData(data, t)
}
func structReader(s Struct, t *Type) []Value {
d.Chk.True(t.Kind() == StructKind)
values := []Value{}
desc := t.Desc.(StructDesc)
desc.IterFields(func(name string, t *Type) {
v, ok := s.data[name]
d.Chk.True(ok)
values = append(values, v)
})
return values
return Struct{values, s.t, &hash.Hash{}}
}
// s1 & s2 must be of the same type. Returns the set of field names which have different values in the respective structs
func StructDiff(s1, s2 Struct) (changed []string) {
d.Chk.True(s1.Type().Equals(s2.Type()))
s1.desc().IterFields(func(name string, t *Type) {
v1 := s1.data[name]
v2 := s2.data[name]
fields := s1.desc().fields
for i, v1 := range s1.values {
v2 := s2.values[i]
if !v1.Equals(v2) {
changed = append(changed, name)
changed = append(changed, fields[i].name)
}
})
}
return
}
+6 -5
View File
@@ -126,15 +126,16 @@ func MakePrimitiveTypeByString(p string) *Type {
func MakeStructType(name string, fields map[string]*Type) *Type {
verifyStructName(name)
names := make([]string, len(fields))
fs := make(fieldSlice, len(fields))
i := 0
for fn := range fields {
for fn, t := range fields {
verifyFieldName(fn)
names[i] = fn
fs[i] = field{fn, t}
i++
}
sort.Strings(names)
return buildType(StructDesc{name, fields, names})
sort.Sort(fs)
return buildType(StructDesc{name, fs})
}
var fieldNameRe = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]*$`)
+52 -8
View File
@@ -4,6 +4,12 @@
package types
import (
"sort"
"github.com/attic-labs/noms/go/d"
)
// TypeDesc describes a type of the kind returned by Kind(), e.g. Map, Number, or a custom type.
type TypeDesc interface {
Kind() NomsKind
@@ -68,12 +74,22 @@ func (c CompoundDesc) Equals(other TypeDesc) bool {
type TypeMap map[string]*Type
type field struct {
name string
t *Type
}
type fieldSlice []field
func (s fieldSlice) Len() int { return len(s) }
func (s fieldSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s fieldSlice) Less(i, j int) bool { return s[i].name < s[j].name }
// StructDesc describes a custom Noms Struct.
// Structs can contain at most one anonymous union, so Union may be nil.
type StructDesc struct {
Name string
Fields TypeMap
sortedNames []string
Name string
fields []field
}
func (s StructDesc) Kind() NomsKind {
@@ -81,11 +97,12 @@ func (s StructDesc) Kind() NomsKind {
}
func (s StructDesc) Equals(other TypeDesc) bool {
if s.Kind() != other.Kind() || len(s.Fields) != len(other.(StructDesc).Fields) {
if s.Kind() != other.Kind() || len(s.fields) != len(other.(StructDesc).fields) {
return false
}
for i, t := range other.(StructDesc).Fields {
if !s.Fields[i].Equals(t) {
otherDesc := other.(StructDesc)
for i, field := range s.fields {
if field.name != otherDesc.fields[i].name || !field.t.Equals(otherDesc.fields[i].t) {
return false
}
}
@@ -93,7 +110,34 @@ func (s StructDesc) Equals(other TypeDesc) bool {
}
func (s StructDesc) IterFields(cb func(name string, t *Type)) {
for _, name := range s.sortedNames {
cb(name, s.Fields[name])
for _, field := range s.fields {
cb(field.name, field.t)
}
}
func (s StructDesc) Field(name string) *Type {
f, i := s.findField(name)
if i == -1 {
return nil
}
return f.t
}
func (s StructDesc) SetField(name string, t *Type) {
f, i := s.findField(name)
d.Chk.True(i != -1, "No such field %s", name)
f.t = t
}
func (s StructDesc) findField(name string) (*field, int) {
i := sort.Search(len(s.fields), func(i int) bool { return s.fields[i].name >= name })
if i == len(s.fields) || s.fields[i].name != name {
return nil, -1
}
return &s.fields[i], i
}
// Len returns the number of fields in the struct
func (s StructDesc) Len() int {
return len(s.fields)
}
+1 -1
View File
@@ -23,7 +23,7 @@ func TestTypes(t *testing.T) {
recType := MakeStructType("RecursiveStruct", TypeMap{
"self": nil,
})
recType.Desc.(StructDesc).Fields["self"] = recType
recType.Desc.(StructDesc).SetField("self", recType)
mRef := vs.WriteValue(mapType).TargetHash()
setRef := vs.WriteValue(setType).TargetHash()
+9 -18
View File
@@ -8,6 +8,7 @@ import (
"fmt"
"github.com/attic-labs/noms/go/d"
"github.com/attic-labs/noms/go/hash"
)
type valueDecoder struct {
@@ -173,37 +174,27 @@ func (r *valueDecoder) readValue() Value {
func (r *valueDecoder) readStruct(t *Type) Value {
// We've read `[StructKind, name, fields, unions` at this point
desc := t.Desc.(StructDesc)
count := len(desc.Fields)
values := make([]Value, count, count)
i := 0
desc.IterFields(func(name string, t *Type) {
count := desc.Len()
values := make([]Value, count)
for i := 0; i < count; i++ {
values[i] = r.readValue()
i++
})
}
return structBuilder(values, t)
return Struct{values, t, &hash.Hash{}}
}
func (r *valueDecoder) readStructType(parentStructTypes []*Type) *Type {
name := r.readString()
count := r.readUint32()
fields := make(TypeMap, count)
fieldNames := make([]string, count, count)
desc := StructDesc{name, fields, fieldNames}
fields := make(fieldSlice, count)
desc := StructDesc{name, fields}
st := buildType(desc)
parentStructTypes = append(parentStructTypes, st)
for i := uint32(0); i < count; i++ {
fieldName := r.readString()
fieldType := r.readType(parentStructTypes)
fields[fieldName] = fieldType
fieldNames[i] = fieldName
fields[i] = field{name: r.readString(), t: r.readType(parentStructTypes)}
}
desc.Fields = fields
desc.sortedNames = fieldNames
st.Desc = desc
return st
}
+7 -6
View File
@@ -171,7 +171,7 @@ func indexOfType(t *Type, ts []*Type) int {
}
func (w *valueEncoder) writeStruct(v Value, t *Type) {
for _, v := range structReader(v.(Struct), t) {
for _, v := range v.(Struct).values {
w.writeValue(v)
}
}
@@ -193,11 +193,12 @@ func (w *valueEncoder) writeStructType(t *Type, parentStructTypes []*Type) {
w.writeKind(StructKind)
w.writeString(t.Name())
count := len(t.Desc.(StructDesc).Fields)
count := t.Desc.(StructDesc).Len()
w.writeUint32(uint32(count))
t.Desc.(StructDesc).IterFields(func(name string, t *Type) {
w.writeString(name)
w.writeType(t, parentStructTypes)
})
fields := t.Desc.(StructDesc).fields
for _, field := range fields {
w.writeString(field.name)
w.writeType(field.t, parentStructTypes)
}
}
+5 -2
View File
@@ -103,7 +103,10 @@ func Read(r *csv.Reader, structName string, headers_raw []string, kinds KindSlic
valueChan := make(chan types.Value, 128) // TODO: Make this a function param?
listChan := types.NewStreamingList(vrw, valueChan)
structFields := t.Desc.(types.StructDesc).Fields
kindMap := make(map[string]types.NomsKind, len(headers))
t.Desc.(types.StructDesc).IterFields(func(name string, t *types.Type) {
kindMap[name] = t.Kind()
})
for {
row, err := r.Read()
@@ -118,7 +121,7 @@ func Read(r *csv.Reader, structName string, headers_raw []string, kinds KindSlic
for i, v := range row {
if i < len(headers) {
name := headers[i]
fields[name] = StringToType(v, structFields[name].Kind())
fields[name] = StringToType(v, kindMap[name])
}
}
valueChan <- types.NewStructWithType(t, fields)
+7 -7
View File
@@ -34,10 +34,10 @@ b,2,false
desc, ok := typ.Desc.(types.StructDesc)
assert.True(ok)
assert.Len(desc.Fields, 3)
assert.Equal(types.StringKind, desc.Fields["A"].Kind())
assert.Equal(types.NumberKind, desc.Fields["B"].Kind())
assert.Equal(types.BoolKind, desc.Fields["C"].Kind())
assert.Equal(desc.Len(), 3)
assert.Equal(types.StringKind, desc.Field("A").Kind())
assert.Equal(types.NumberKind, desc.Field("B").Kind())
assert.Equal(types.BoolKind, desc.Field("C").Kind())
assert.True(l.Get(0).(types.Struct).Get("A").Equals(types.NewString("a")))
assert.True(l.Get(1).(types.Struct).Get("A").Equals(types.NewString("b")))
@@ -65,9 +65,9 @@ func testTrailingHelper(t *testing.T, dataString string) {
desc, ok := typ.Desc.(types.StructDesc)
assert.True(ok)
assert.Len(desc.Fields, 2)
assert.Equal(types.StringKind, desc.Fields["A"].Kind())
assert.Equal(types.StringKind, desc.Fields["B"].Kind())
assert.Equal(desc.Len(), 2)
assert.Equal(types.StringKind, desc.Field("A").Kind())
assert.Equal(types.StringKind, desc.Field("B").Kind())
}
func TestReadTrailingHole(t *testing.T) {