mirror of
https://github.com/dolthub/dolt.git
synced 2026-05-03 03:10:26 -05:00
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:
+1
-1
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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")
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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>>
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user