@@ -17,72 +17,101 @@ limitations under the License.
1717package fieldpath
1818
1919import (
20+ "bytes"
2021 "errors"
2122 "fmt"
2223 "io"
2324 "strconv"
2425 "strings"
2526
26- jsoniter "github.com/json-iterator/go"
27+ "github.com/go-json-experiment/json"
28+ "github.com/go-json-experiment/json/jsontext"
2729 "sigs.k8s.io/structured-merge-diff/v6/value"
2830)
2931
3032var ErrUnknownPathElementType = errors .New ("unknown path element type" )
3133
3234const (
3335 // Field indicates that the content of this path element is a field's name
34- peField = "f"
36+ peField byte = 'f'
3537
3638 // Value indicates that the content of this path element is a field's value
37- peValue = "v"
39+ peValue byte = 'v'
3840
3941 // Index indicates that the content of this path element is an index in an array
40- peIndex = "i"
42+ peIndex byte = 'i'
4143
4244 // Key indicates that the content of this path element is a key value map
43- peKey = "k"
45+ peKey byte = 'k'
4446
4547 // Separator separates the type of a path element from the contents
46- peSeparator = ":"
48+ peSeparator byte = ':'
4749)
4850
4951var (
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 }
5556)
5657
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 ))
6661}
6762
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
73102}
74103
75104// DeserializePathElement parses a serialized path element
76105func DeserializePathElement (s string ) (PathElement , error ) {
77106 b := []byte (s )
78107 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" )
80109 }
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 {
83112 return PathElement {}, fmt .Errorf ("missing colon: %v" , s )
84113 }
85- switch typeSep [ 0 ] {
114+ switch typeSep0 {
86115 case peFieldSepBytes [0 ]:
87116 // Slice s rather than convert b, to save on
88117 // allocations.
@@ -91,29 +120,18 @@ func DeserializePathElement(s string) (PathElement, error) {
91120 FieldName : & str ,
92121 }, nil
93122 case peValueSepBytes [0 ]:
94- iter := readPool .BorrowIterator (b )
95- defer readPool .ReturnIterator (iter )
96- v , err := readJSONIter (iter )
123+ v , err := value .FromJSON (b )
97124 if err != nil {
98125 return PathElement {}, err
99126 }
100127 return PathElement {Value : & v }, nil
101128 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+ }
115133 fields .Sort ()
116- return PathElement {Key : & fields }, iter . Error
134+ return PathElement {Key : & fields }, nil
117135 case peIndexSepBytes [0 ]:
118136 i , err := strconv .Atoi (s [2 :])
119137 if err != nil {
@@ -127,60 +145,58 @@ func DeserializePathElement(s string) (PathElement, error) {
127145 }
128146}
129147
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+ }
134152
135153// SerializePathElement serializes a path element
136154func 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
140157}
141158
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+
145162 switch {
146163 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
149166 }
150- stream . WriteRaw (* pe .FieldName )
167+ pes . buffer . WriteString (* pe .FieldName )
151168 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
154171 }
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
160180 }
161- stream .WriteObjectField (field .Name )
162- writeJSONStream (field .Value , stream )
163181 }
164- stream . WriteObjectEnd ( )
182+ pes . encoder . WriteToken ( jsontext . EndObject )
165183 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
168190 }
169- writeJSONStream (* pe .Value , stream )
170191 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
173194 }
174- stream . WriteInt ( * pe .Index )
195+ pes . buffer . WriteString ( strconv . Itoa ( * pe .Index ) )
175196 default :
176- return errors .New ("invalid PathElement" )
197+ return "" , errors .New ("invalid PathElement" )
177198 }
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
186202}
0 commit comments