Skip to content

Commit 49a5de7

Browse files
author
Antoine Pelisse
committed
Separate implementation of fieldset from validation
1 parent 990c697 commit 49a5de7

File tree

2 files changed

+167
-6
lines changed

2 files changed

+167
-6
lines changed

typed/tofieldset.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package typed
18+
19+
import (
20+
"sync"
21+
22+
"sigs.k8s.io/structured-merge-diff/fieldpath"
23+
"sigs.k8s.io/structured-merge-diff/schema"
24+
"sigs.k8s.io/structured-merge-diff/value"
25+
)
26+
27+
var tPool = sync.Pool{
28+
New: func() interface{} { return &toFieldSetWalker{} },
29+
}
30+
31+
func (tv TypedValue) toFieldSetWalker() *toFieldSetWalker {
32+
v := tPool.Get().(*toFieldSetWalker)
33+
v.value = tv.value
34+
v.schema = tv.schema
35+
v.typeRef = tv.typeRef
36+
v.set = &fieldpath.Set{}
37+
return v
38+
}
39+
40+
func (v *toFieldSetWalker) finished() {
41+
v.schema = nil
42+
v.typeRef = schema.TypeRef{}
43+
v.path = nil
44+
v.set = nil
45+
tPool.Put(v)
46+
}
47+
48+
type toFieldSetWalker struct {
49+
value value.Value
50+
schema *schema.Schema
51+
typeRef schema.TypeRef
52+
53+
set *fieldpath.Set
54+
path fieldpath.Path
55+
56+
// Allocate only as many walkers as needed for the depth by storing them here.
57+
spareWalkers *[]*toFieldSetWalker
58+
}
59+
60+
func (v *toFieldSetWalker) prepareDescent(pe fieldpath.PathElement, tr schema.TypeRef) *toFieldSetWalker {
61+
if v.spareWalkers == nil {
62+
// first descent.
63+
v.spareWalkers = &[]*toFieldSetWalker{}
64+
}
65+
var v2 *toFieldSetWalker
66+
if n := len(*v.spareWalkers); n > 0 {
67+
v2, *v.spareWalkers = (*v.spareWalkers)[n-1], (*v.spareWalkers)[:n-1]
68+
} else {
69+
v2 = &toFieldSetWalker{}
70+
}
71+
*v2 = *v
72+
v2.typeRef = tr
73+
v2.path = append(v2.path, pe)
74+
return v2
75+
}
76+
77+
func (v *toFieldSetWalker) finishDescent(v2 *toFieldSetWalker) {
78+
// if the descent caused a realloc, ensure that we reuse the buffer
79+
// for the next sibling.
80+
v.path = v2.path[:len(v2.path)-1]
81+
*v.spareWalkers = append(*v.spareWalkers, v2)
82+
}
83+
84+
func (v *toFieldSetWalker) toFieldSet() ValidationErrors {
85+
return resolveSchema(v.schema, v.typeRef, &v.value, v)
86+
}
87+
88+
func (v *toFieldSetWalker) doScalar(t *schema.Scalar) ValidationErrors {
89+
v.set.Insert(v.path)
90+
91+
return nil
92+
}
93+
94+
func (v *toFieldSetWalker) visitListItems(t *schema.List, list []interface{}) (errs ValidationErrors) {
95+
for i, child := range list {
96+
pe, _ := listItemToPathElement(t, i, child)
97+
v2 := v.prepareDescent(pe, t.ElementType)
98+
v2.value = child
99+
errs = append(errs, v2.toFieldSet()...)
100+
101+
v2.set.Insert(v2.path)
102+
v.finishDescent(v2)
103+
}
104+
return errs
105+
}
106+
107+
func (v *toFieldSetWalker) doList(t *schema.List) (errs ValidationErrors) {
108+
list, _ := listValue(v.value)
109+
110+
if t.ElementRelationship == schema.Atomic {
111+
v.set.Insert(v.path)
112+
return nil
113+
}
114+
115+
if list == nil {
116+
return nil
117+
}
118+
119+
errs = v.visitListItems(t, list)
120+
121+
return errs
122+
}
123+
124+
func (v *toFieldSetWalker) visitMapItems(t *schema.Map, m map[string]interface{}) (errs ValidationErrors) {
125+
for key, val := range m {
126+
k := key
127+
pe := fieldpath.PathElement{FieldName: &k}
128+
129+
tr := t.ElementType
130+
if sf, ok := t.FindField(key); ok {
131+
tr = sf.Type
132+
}
133+
v2 := v.prepareDescent(pe, tr)
134+
v2.value = val
135+
errs = append(errs, v2.toFieldSet()...)
136+
if _, ok := t.FindField(key); !ok {
137+
v2.set.Insert(v2.path)
138+
}
139+
v.finishDescent(v2)
140+
141+
}
142+
return errs
143+
}
144+
145+
func (v *toFieldSetWalker) doMap(t *schema.Map) (errs ValidationErrors) {
146+
m, _ := mapValue(v.value)
147+
148+
if t.ElementRelationship == schema.Atomic {
149+
v.set.Insert(v.path)
150+
return nil
151+
}
152+
153+
if m == nil {
154+
return nil
155+
}
156+
157+
errs = v.visitMapItems(t, m)
158+
159+
return errs
160+
}
161+
162+
func (v *toFieldSetWalker) errorf(string, ...interface{}) ValidationErrors {
163+
return nil
164+
}

typed/typed.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,12 @@ func (tv TypedValue) Validate() error {
7979
// ToFieldSet creates a set containing every leaf field and item mentioned, or
8080
// validation errors, if any were encountered.
8181
func (tv TypedValue) ToFieldSet() (*fieldpath.Set, error) {
82-
s := fieldpath.NewSet()
83-
w := tv.walker()
82+
w := tv.toFieldSetWalker()
8483
defer w.finished()
85-
w.leafFieldCallback = func(p fieldpath.Path) { s.Insert(p) }
86-
w.nodeFieldCallback = func(p fieldpath.Path) { s.Insert(p) }
87-
if errs := w.validate(); len(errs) != 0 {
84+
if errs := w.toFieldSet(); len(errs) != 0 {
8885
return nil, errs
8986
}
90-
return s, nil
87+
return w.set, nil
9188
}
9289

9390
// Merge returns the result of merging tv and pso ("partially specified

0 commit comments

Comments
 (0)