@@ -17,72 +17,101 @@ limitations under the License.
17
17
package fieldpath
18
18
19
19
import (
20
+ "bytes"
20
21
"errors"
21
22
"fmt"
22
23
"io"
23
24
"strconv"
24
25
"strings"
25
26
26
- jsoniter "github.com/json-iterator/go"
27
+ "github.com/go-json-experiment/json"
28
+ "github.com/go-json-experiment/json/jsontext"
27
29
"sigs.k8s.io/structured-merge-diff/v6/value"
28
30
)
29
31
30
32
var ErrUnknownPathElementType = errors .New ("unknown path element type" )
31
33
32
34
const (
33
35
// Field indicates that the content of this path element is a field's name
34
- peField = "f"
36
+ peField byte = 'f'
35
37
36
38
// Value indicates that the content of this path element is a field's value
37
- peValue = "v"
39
+ peValue byte = 'v'
38
40
39
41
// Index indicates that the content of this path element is an index in an array
40
- peIndex = "i"
42
+ peIndex byte = 'i'
41
43
42
44
// Key indicates that the content of this path element is a key value map
43
- peKey = "k"
45
+ peKey byte = 'k'
44
46
45
47
// Separator separates the type of a path element from the contents
46
- peSeparator = ":"
48
+ peSeparator byte = ':'
47
49
)
48
50
49
51
var (
50
- peFieldSepBytes = []byte (peField + peSeparator )
51
- peValueSepBytes = []byte (peValue + peSeparator )
52
- peIndexSepBytes = []byte (peIndex + peSeparator )
53
- peKeySepBytes = []byte (peKey + peSeparator )
54
- peSepBytes = []byte (peSeparator )
52
+ peFieldSepBytes = []byte {peField , peSeparator }
53
+ peValueSepBytes = []byte {peValue , peSeparator }
54
+ peIndexSepBytes = []byte {peIndex , peSeparator }
55
+ peKeySepBytes = []byte {peKey , peSeparator }
55
56
)
56
57
57
- // readJSONIter reads a Value from a JSON iterator.
58
- // DO NOT EXPORT
59
- // TODO: eliminate this https://github.com/kubernetes-sigs/structured-merge-diff/issues/202
60
- func readJSONIter (iter * jsoniter.Iterator ) (value.Value , error ) {
61
- v := iter .Read ()
62
- if iter .Error != nil && iter .Error != io .EOF {
63
- return nil , iter .Error
64
- }
65
- return value .NewValueInterface (v ), nil
58
+ // writeValueToEncoder writes a value to an Encoder.
59
+ func writeValueToEncoder (v value.Value , enc * jsontext.Encoder ) error {
60
+ return json .MarshalEncode (enc , v .Unstructured (), json .Deterministic (true ))
66
61
}
67
62
68
- // writeJSONStream writes a value into a JSON stream.
69
- // DO NOT EXPORT
70
- // TODO: eliminate this https://github.com/kubernetes-sigs/structured-merge-diff/issues/202
71
- func writeJSONStream (v value.Value , stream * jsoniter.Stream ) {
72
- stream .WriteVal (v .Unstructured ())
63
+ // FieldListFromJSON is a helper function for reading a JSON document.
64
+ func fieldListFromJSON (input []byte ) (value.FieldList , error ) {
65
+ parser := jsontext .NewDecoder (bytes .NewBuffer (input ))
66
+
67
+ if objStart , err := parser .ReadToken (); err != nil {
68
+ return nil , fmt .Errorf ("parsing JSON: %v" , err )
69
+ } else if objStart .Kind () != jsontext .BeginObject .Kind () {
70
+ return nil , fmt .Errorf ("expected object" )
71
+ }
72
+
73
+ var fields value.FieldList
74
+ for {
75
+ if parser .PeekKind () == jsontext .EndObject .Kind () {
76
+ if _ , err := parser .ReadToken (); err != nil {
77
+ return nil , fmt .Errorf ("parsing JSON: %v" , err )
78
+ }
79
+ break
80
+ }
81
+
82
+ rawKey , err := parser .ReadToken ()
83
+ if err == io .EOF {
84
+ return nil , fmt .Errorf ("unexpected EOF" )
85
+ } else if err != nil {
86
+ return nil , fmt .Errorf ("parsing JSON: %v" , err )
87
+ }
88
+
89
+ k := rawKey .String ()
90
+
91
+ var v any
92
+ if err := json .UnmarshalDecode (parser , & v ); err == io .EOF {
93
+ return nil , fmt .Errorf ("unexpected EOF" )
94
+ } else if err != nil {
95
+ return nil , fmt .Errorf ("parsing JSON: %v" , err )
96
+ }
97
+
98
+ fields = append (fields , value.Field {Name : k , Value : value .NewValueInterface (v )})
99
+ }
100
+
101
+ return fields , nil
73
102
}
74
103
75
104
// DeserializePathElement parses a serialized path element
76
105
func DeserializePathElement (s string ) (PathElement , error ) {
77
106
b := []byte (s )
78
107
if len (b ) < 2 {
79
- return PathElement {}, errors .New ("key must be 2 characters long: " )
108
+ return PathElement {}, errors .New ("key must be 2 characters long" )
80
109
}
81
- typeSep , b := b [: 2 ], b [2 :]
82
- if typeSep [ 1 ] != peSepBytes [ 0 ] {
110
+ typeSep0 , typeSep1 , b := b [0 ], b [ 1 ], b [2 :]
111
+ if typeSep1 != peSeparator {
83
112
return PathElement {}, fmt .Errorf ("missing colon: %v" , s )
84
113
}
85
- switch typeSep [ 0 ] {
114
+ switch typeSep0 {
86
115
case peFieldSepBytes [0 ]:
87
116
// Slice s rather than convert b, to save on
88
117
// allocations.
@@ -91,29 +120,18 @@ func DeserializePathElement(s string) (PathElement, error) {
91
120
FieldName : & str ,
92
121
}, nil
93
122
case peValueSepBytes [0 ]:
94
- iter := readPool .BorrowIterator (b )
95
- defer readPool .ReturnIterator (iter )
96
- v , err := readJSONIter (iter )
123
+ v , err := value .FromJSON (b )
97
124
if err != nil {
98
125
return PathElement {}, err
99
126
}
100
127
return PathElement {Value : & v }, nil
101
128
case peKeySepBytes [0 ]:
102
- iter := readPool .BorrowIterator (b )
103
- defer readPool .ReturnIterator (iter )
104
- fields := value.FieldList {}
105
-
106
- iter .ReadObjectCB (func (iter * jsoniter.Iterator , key string ) bool {
107
- v , err := readJSONIter (iter )
108
- if err != nil {
109
- iter .Error = err
110
- return false
111
- }
112
- fields = append (fields , value.Field {Name : key , Value : v })
113
- return true
114
- })
129
+ fields , err := fieldListFromJSON (b )
130
+ if err != nil {
131
+ return PathElement {}, err
132
+ }
115
133
fields .Sort ()
116
- return PathElement {Key : & fields }, iter . Error
134
+ return PathElement {Key : & fields }, nil
117
135
case peIndexSepBytes [0 ]:
118
136
i , err := strconv .Atoi (s [2 :])
119
137
if err != nil {
@@ -127,60 +145,58 @@ func DeserializePathElement(s string) (PathElement, error) {
127
145
}
128
146
}
129
147
130
- var (
131
- readPool = jsoniter . NewIterator ( jsoniter . ConfigCompatibleWithStandardLibrary ). Pool ()
132
- writePool = jsoniter . NewStream ( jsoniter . ConfigCompatibleWithStandardLibrary , nil , 1024 ). Pool ()
133
- )
148
+ type PathElementSerializer struct {
149
+ buffer bytes. Buffer
150
+ encoder jsontext. Encoder
151
+ }
134
152
135
153
// SerializePathElement serializes a path element
136
154
func SerializePathElement (pe PathElement ) (string , error ) {
137
- buf := strings.Builder {}
138
- err := serializePathElementToWriter (& buf , pe )
139
- return buf .String (), err
155
+ byteVal , err := (& PathElementSerializer {}).serialize (pe )
156
+ return string (byteVal ), err
140
157
}
141
158
142
- func serializePathElementToWriter ( w io. Writer , pe PathElement ) error {
143
- stream := writePool . BorrowStream ( w )
144
- defer writePool . ReturnStream ( stream )
159
+ func ( pes * PathElementSerializer ) serialize ( pe PathElement ) ( string , error ) {
160
+ pes . buffer . Reset ( )
161
+
145
162
switch {
146
163
case pe .FieldName != nil :
147
- if _ , err := stream .Write (peFieldSepBytes ); err != nil {
148
- return err
164
+ if _ , err := pes . buffer .Write (peFieldSepBytes ); err != nil {
165
+ return "" , err
149
166
}
150
- stream . WriteRaw (* pe .FieldName )
167
+ pes . buffer . WriteString (* pe .FieldName )
151
168
case pe .Key != nil :
152
- if _ , err := stream .Write (peKeySepBytes ); err != nil {
153
- return err
169
+ if _ , err := pes . buffer .Write (peKeySepBytes ); err != nil {
170
+ return "" , err
154
171
}
155
- stream .WriteObjectStart ()
156
-
157
- for i , field := range * pe .Key {
158
- if i > 0 {
159
- stream .WriteMore ()
172
+ pes .encoder .Reset (& pes .buffer )
173
+ pes .encoder .WriteToken (jsontext .BeginObject )
174
+ for _ , f := range * pe .Key {
175
+ if err := pes .encoder .WriteToken (jsontext .String (f .Name )); err != nil {
176
+ return "" , err
177
+ }
178
+ if err := writeValueToEncoder (f .Value , & pes .encoder ); err != nil {
179
+ return "" , err
160
180
}
161
- stream .WriteObjectField (field .Name )
162
- writeJSONStream (field .Value , stream )
163
181
}
164
- stream . WriteObjectEnd ( )
182
+ pes . encoder . WriteToken ( jsontext . EndObject )
165
183
case pe .Value != nil :
166
- if _ , err := stream .Write (peValueSepBytes ); err != nil {
167
- return err
184
+ if _ , err := pes .buffer .Write (peValueSepBytes ); err != nil {
185
+ return "" , err
186
+ }
187
+ pes .encoder .Reset (& pes .buffer )
188
+ if err := writeValueToEncoder (* pe .Value , & pes .encoder ); err != nil {
189
+ return "" , err
168
190
}
169
- writeJSONStream (* pe .Value , stream )
170
191
case pe .Index != nil :
171
- if _ , err := stream .Write (peIndexSepBytes ); err != nil {
172
- return err
192
+ if _ , err := pes . buffer .Write (peIndexSepBytes ); err != nil {
193
+ return "" , err
173
194
}
174
- stream . WriteInt ( * pe .Index )
195
+ pes . buffer . WriteString ( strconv . Itoa ( * pe .Index ) )
175
196
default :
176
- return errors .New ("invalid PathElement" )
197
+ return "" , errors .New ("invalid PathElement" )
177
198
}
178
- b := stream .Buffer ()
179
- err := stream .Flush ()
180
- // Help jsoniter manage its buffers--without this, the next
181
- // use of the stream is likely to require an allocation. Look
182
- // at the jsoniter stream code to understand why. They were probably
183
- // optimizing for folks using the buffer directly.
184
- stream .SetBuffer (b [:0 ])
185
- return err
199
+
200
+ // TODO: is there a way to not emit newlines
201
+ return strings .TrimSpace (pes .buffer .String ()), nil
186
202
}
0 commit comments