Skip to content

Commit 7696497

Browse files
authored
Merge pull request #93 from lavalamp/fixvalue
improved memory management
2 parents 0c1d754 + bea3b75 commit 7696497

File tree

4 files changed

+107
-30
lines changed

4 files changed

+107
-30
lines changed

typed/helpers.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ func (ef *errorFormatter) descend(pe fieldpath.PathElement) {
6767
ef.path = append(ef.path, pe)
6868
}
6969

70+
// parent returns the parent, for the purpose of buffer reuse. It's an error to
71+
// call this if there is no parent.
72+
func (ef *errorFormatter) parent() errorFormatter {
73+
return errorFormatter{
74+
path: ef.path[:len(ef.path)-1],
75+
}
76+
}
77+
7078
func (ef errorFormatter) errorf(format string, args ...interface{}) ValidationErrors {
7179
return ValidationErrors{{
7280
Path: append(fieldpath.Path{}, ef.path...),

typed/merge.go

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ type mergingWalker struct {
4343

4444
// internal housekeeping--don't set when constructing.
4545
inLeaf bool // Set to true if we're in a "big leaf"--atomic map/list
46+
47+
// Allocate only as many walkers as needed for the depth by storing them here.
48+
spareWalkers *[]*mergingWalker
4649
}
4750

4851
// merge rules examine w.lhs and w.rhs (up to one of which may be nil) and
@@ -117,13 +120,30 @@ func (w *mergingWalker) doScalar(t schema.Scalar) (errs ValidationErrors) {
117120
}
118121

119122
func (w *mergingWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeRef) *mergingWalker {
120-
w2 := *w
123+
if w.spareWalkers == nil {
124+
// first descent.
125+
w.spareWalkers = &[]*mergingWalker{}
126+
}
127+
var w2 *mergingWalker
128+
if n := len(*w.spareWalkers); n > 0 {
129+
w2, *w.spareWalkers = (*w.spareWalkers)[n-1], (*w.spareWalkers)[:n-1]
130+
} else {
131+
w2 = &mergingWalker{}
132+
}
133+
*w2 = *w
121134
w2.typeRef = tr
122135
w2.errorFormatter.descend(pe)
123136
w2.lhs = nil
124137
w2.rhs = nil
125138
w2.out = nil
126-
return &w2
139+
return w2
140+
}
141+
142+
func (w *mergingWalker) finishDescent(w2 *mergingWalker) {
143+
// if the descent caused a realloc, ensure that we reuse the buffer
144+
// for the next sibling.
145+
w.errorFormatter = w2.errorFormatter.parent()
146+
*w.spareWalkers = append(*w.spareWalkers, w2)
127147
}
128148

129149
func (w *mergingWalker) derefMap(prefix string, v *value.Value, dest **value.Map) (errs ValidationErrors) {
@@ -198,6 +218,7 @@ func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (err
198218
} else if w2.out != nil {
199219
out.Items = append(out.Items, *w2.out)
200220
}
221+
w.finishDescent(w2)
201222
// Keep track of children that have been handled
202223
delete(observedRHS, keyStr)
203224
}
@@ -212,6 +233,7 @@ func (w *mergingWalker) visitListItems(t schema.List, lhs, rhs *value.List) (err
212233
} else if w2.out != nil {
213234
out.Items = append(out.Items, *w2.out)
214235
}
236+
w.finishDescent(w2)
215237
}
216238
}
217239

@@ -272,13 +294,13 @@ func (w *mergingWalker) visitMapItems(t schema.Map, lhs, rhs *value.Map) (errs V
272294
}
273295

274296
if lhs != nil {
275-
for _, litem := range lhs.Items {
276-
name := litem.Name
297+
for i := range lhs.Items {
298+
litem := &lhs.Items[i]
277299
fieldType := t.ElementType
278-
if ft, ok := fieldTypes[name]; ok {
300+
if ft, ok := fieldTypes[litem.Name]; ok {
279301
fieldType = ft
280302
}
281-
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &name}, fieldType)
303+
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &litem.Name}, fieldType)
282304
w2.lhs = &litem.Value
283305
if rhs != nil {
284306
if ritem, ok := rhs.Get(litem.Name); ok {
@@ -288,31 +310,33 @@ func (w *mergingWalker) visitMapItems(t schema.Map, lhs, rhs *value.Map) (errs V
288310
if newErrs := w2.merge(); len(newErrs) > 0 {
289311
errs = append(errs, newErrs...)
290312
} else if w2.out != nil {
291-
out.Set(name, *w2.out)
313+
out.Items = append(out.Items, value.Field{litem.Name, *w2.out})
292314
}
315+
w.finishDescent(w2)
293316
}
294317
}
295318

296319
if rhs != nil {
297-
for _, ritem := range rhs.Items {
320+
for j := range rhs.Items {
321+
ritem := &rhs.Items[j]
298322
if lhs != nil {
299323
if _, ok := lhs.Get(ritem.Name); ok {
300324
continue
301325
}
302326
}
303327

304-
name := ritem.Name
305328
fieldType := t.ElementType
306-
if ft, ok := fieldTypes[name]; ok {
329+
if ft, ok := fieldTypes[ritem.Name]; ok {
307330
fieldType = ft
308331
}
309-
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &name}, fieldType)
332+
w2 := w.prepareDescent(fieldpath.PathElement{FieldName: &ritem.Name}, fieldType)
310333
w2.rhs = &ritem.Value
311334
if newErrs := w2.merge(); len(newErrs) > 0 {
312335
errs = append(errs, newErrs...)
313336
} else if w2.out != nil {
314-
out.Set(name, *w2.out)
337+
out.Items = append(out.Items, value.Field{ritem.Name, *w2.out})
315338
}
339+
w.finishDescent(w2)
316340
}
317341
}
318342

typed/validate.go

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,36 @@ type validatingObjectWalker struct {
4949

5050
// internal housekeeping--don't set when constructing.
5151
inLeaf bool // Set to true if we're in a "big leaf"--atomic map/list
52+
53+
// Allocate only as many walkers as needed for the depth by storing them here.
54+
spareWalkers *[]*validatingObjectWalker
55+
}
56+
57+
func (v *validatingObjectWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeRef) *validatingObjectWalker {
58+
if v.spareWalkers == nil {
59+
// first descent.
60+
v.spareWalkers = &[]*validatingObjectWalker{}
61+
}
62+
var v2 *validatingObjectWalker
63+
if n := len(*v.spareWalkers); n > 0 {
64+
v2, *v.spareWalkers = (*v.spareWalkers)[n-1], (*v.spareWalkers)[:n-1]
65+
} else {
66+
v2 = &validatingObjectWalker{}
67+
}
68+
*v2 = *v
69+
v2.typeRef = tr
70+
v2.errorFormatter.descend(pe)
71+
return v2
72+
}
73+
74+
func (v *validatingObjectWalker) finishDescent(v2 *validatingObjectWalker) {
75+
// if the descent caused a realloc, ensure that we reuse the buffer
76+
// for the next sibling.
77+
v.errorFormatter = v2.errorFormatter.parent()
78+
*v.spareWalkers = append(*v.spareWalkers, v2)
5279
}
5380

54-
func (v validatingObjectWalker) validate() ValidationErrors {
81+
func (v *validatingObjectWalker) validate() ValidationErrors {
5582
return resolveSchema(v.schema, v.typeRef, &v.value, v)
5683
}
5784

@@ -87,7 +114,7 @@ func (v *validatingObjectWalker) doNode() {
87114
}
88115
}
89116

90-
func (v validatingObjectWalker) doScalar(t schema.Scalar) ValidationErrors {
117+
func (v *validatingObjectWalker) doScalar(t schema.Scalar) ValidationErrors {
91118
if errs := v.validateScalar(t, &v.value, ""); len(errs) > 0 {
92119
return errs
93120
}
@@ -98,7 +125,7 @@ func (v validatingObjectWalker) doScalar(t schema.Scalar) ValidationErrors {
98125
return nil
99126
}
100127

101-
func (v validatingObjectWalker) visitListItems(t schema.List, list *value.List) (errs ValidationErrors) {
128+
func (v *validatingObjectWalker) visitListItems(t schema.List, list *value.List) (errs ValidationErrors) {
102129
observedKeys := map[string]struct{}{}
103130
for i, child := range list.Items {
104131
pe, err := listItemToPathElement(t, i, child)
@@ -114,18 +141,17 @@ func (v validatingObjectWalker) visitListItems(t schema.List, list *value.List)
114141
errs = append(errs, v.errorf("duplicate entries for key %v", keyStr)...)
115142
}
116143
observedKeys[keyStr] = struct{}{}
117-
v2 := v
118-
v2.errorFormatter.descend(pe)
144+
v2 := v.prepareDescent(pe, t.ElementType)
119145
v2.value = child
120-
v2.typeRef = t.ElementType
121146
errs = append(errs, v2.validate()...)
122147

123148
v2.doNode()
149+
v.finishDescent(v2)
124150
}
125151
return errs
126152
}
127153

128-
func (v validatingObjectWalker) doList(t schema.List) (errs ValidationErrors) {
154+
func (v *validatingObjectWalker) doList(t schema.List) (errs ValidationErrors) {
129155
list, err := listValue(v.value)
130156
if err != nil {
131157
return v.error(err)
@@ -144,7 +170,7 @@ func (v validatingObjectWalker) doList(t schema.List) (errs ValidationErrors) {
144170
return errs
145171
}
146172

147-
func (v validatingObjectWalker) visitMapItems(t schema.Map, m *value.Map) (errs ValidationErrors) {
173+
func (v *validatingObjectWalker) visitMapItems(t schema.Map, m *value.Map) (errs ValidationErrors) {
148174
fieldTypes := map[string]schema.TypeRef{}
149175
for i := range t.Fields {
150176
// I don't want to use the loop variable since a reference
@@ -153,25 +179,27 @@ func (v validatingObjectWalker) visitMapItems(t schema.Map, m *value.Map) (errs
153179
fieldTypes[f.Name] = f.Type
154180
}
155181

156-
for _, item := range m.Items {
157-
v2 := v
158-
name := item.Name
159-
v2.errorFormatter.descend(fieldpath.PathElement{FieldName: &name})
160-
v2.value = item.Value
182+
for i := range m.Items {
183+
item := &m.Items[i]
184+
pe := fieldpath.PathElement{FieldName: &item.Name}
161185

162-
var ok bool
163-
if v2.typeRef, ok = fieldTypes[name]; ok {
186+
if tr, ok := fieldTypes[item.Name]; ok {
187+
v2 := v.prepareDescent(pe, tr)
188+
v2.value = item.Value
164189
errs = append(errs, v2.validate()...)
190+
v.finishDescent(v2)
165191
} else {
166-
v2.typeRef = t.ElementType
192+
v2 := v.prepareDescent(pe, t.ElementType)
193+
v2.value = item.Value
167194
errs = append(errs, v2.validate()...)
168195
v2.doNode()
196+
v.finishDescent(v2)
169197
}
170198
}
171199
return errs
172200
}
173201

174-
func (v validatingObjectWalker) doMap(t schema.Map) (errs ValidationErrors) {
202+
func (v *validatingObjectWalker) doMap(t schema.Map) (errs ValidationErrors) {
175203
m, err := mapValue(v.value)
176204
if err != nil {
177205
return v.error(err)

value/value.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,12 +276,29 @@ func (m *Map) Get(key string) (*Field, bool) {
276276

277277
// Set inserts or updates the given item.
278278
func (m *Map) Set(key string, value Value) {
279+
if len(m.Items) < 1 {
280+
m.Items = append(m.Items, Field{Name: key, Value: value})
281+
return
282+
}
279283
if f, ok := m.Get(key); ok {
280284
f.Value = value
281285
return
282286
}
287+
f0 := &m.Items[0]
283288
m.Items = append(m.Items, Field{Name: key, Value: value})
284-
m.index = nil // Since the append might have reallocated
289+
if f0 == &m.Items[0] {
290+
// No reallocation, it's safe to just update the map
291+
i := len(m.Items) - 1
292+
f := &m.Items[i]
293+
m.index[f.Name] = f
294+
} else {
295+
// The slice was reallocated, so we need to update all the
296+
// pointers in the map.
297+
for i := range m.Items {
298+
f := &m.Items[i]
299+
m.index[f.Name] = f
300+
}
301+
}
285302
m.order = nil
286303
}
287304

0 commit comments

Comments
 (0)