@@ -6,11 +6,15 @@ package jsonschema
66
77import (
88 "bytes"
9+ "cmp"
10+ "encoding/binary"
911 "encoding/json"
1012 "fmt"
13+ "hash/maphash"
1114 "math"
1215 "math/big"
1316 "reflect"
17+ "slices"
1418)
1519
1620// Equal reports whether two Go values representing JSON values are equal according
@@ -126,14 +130,93 @@ func equalValue(x, y reflect.Value) bool {
126130 return x .String () == y .String ()
127131 case reflect .Bool :
128132 return x .Bool () == y .Bool ()
129- case reflect .Complex64 , reflect .Complex128 :
130- return x .Complex () == y .Complex ()
131133 // Ints, uints and floats handled in jsonNumber, at top of function.
132134 default :
133135 panic (fmt .Sprintf ("unsupported kind: %s" , x .Kind ()))
134136 }
135137}
136138
139+ // hashValue adds v to the data hashed by h. v must not have cycles.
140+ // hashValue panics if the value contains functions or channels, or maps whose
141+ // key type is not string.
142+ // It ignores unexported fields of structs.
143+ // Calls to hashValue with the equal values (in the sense
144+ // of [Equal]) result in the same sequence of values written to the hash.
145+ func hashValue (h * maphash.Hash , v reflect.Value ) {
146+ // TODO: replace writes of basic types with WriteComparable in 1.24.
147+
148+ writeUint := func (u uint64 ) {
149+ var buf [8 ]byte
150+ binary .BigEndian .PutUint64 (buf [:], u )
151+ h .Write (buf [:])
152+ }
153+
154+ var write func (reflect.Value )
155+ write = func (v reflect.Value ) {
156+ if r , ok := jsonNumber (v ); ok {
157+ // We want 1.0 and 1 to hash the same.
158+ // big.Rats are always normalized, so they will be.
159+ // We could do this more efficiently by handling the int and float cases
160+ // separately, but that's premature.
161+ writeUint (uint64 (r .Sign () + 1 ))
162+ h .Write (r .Num ().Bytes ())
163+ h .Write (r .Denom ().Bytes ())
164+ return
165+ }
166+ switch v .Kind () {
167+ case reflect .Invalid :
168+ h .WriteByte (0 )
169+ case reflect .String :
170+ h .WriteString (v .String ())
171+ case reflect .Bool :
172+ if v .Bool () {
173+ h .WriteByte (1 )
174+ } else {
175+ h .WriteByte (0 )
176+ }
177+ case reflect .Complex64 , reflect .Complex128 :
178+ c := v .Complex ()
179+ writeUint (math .Float64bits (real (c )))
180+ writeUint (math .Float64bits (imag (c )))
181+ case reflect .Array , reflect .Slice :
182+ // Although we could treat []byte more efficiently,
183+ // JSON values are unlikely to contain them.
184+ writeUint (uint64 (v .Len ()))
185+ for i := range v .Len () {
186+ write (v .Index (i ))
187+ }
188+ case reflect .Interface , reflect .Pointer :
189+ write (v .Elem ())
190+ case reflect .Struct :
191+ t := v .Type ()
192+ for i := range t .NumField () {
193+ if sf := t .Field (i ); sf .IsExported () {
194+ write (v .FieldByIndex (sf .Index ))
195+ }
196+ }
197+ case reflect .Map :
198+ if v .Type ().Key ().Kind () != reflect .String {
199+ panic ("map with non-string key" )
200+ }
201+ // Sort the keys so the hash is deterministic.
202+ keys := v .MapKeys ()
203+ // Write the length. That distinguishes between, say, two consecutive
204+ // maps with disjoint keys from one map that has the items of both.
205+ writeUint (uint64 (len (keys )))
206+ slices .SortFunc (keys , func (x , y reflect.Value ) int { return cmp .Compare (x .String (), y .String ()) })
207+ for _ , k := range keys {
208+ write (k )
209+ write (v .MapIndex (k ))
210+ }
211+ // Ints, uints and floats handled in jsonNumber, at top of function.
212+ default :
213+ panic (fmt .Sprintf ("unsupported kind: %s" , v .Kind ()))
214+ }
215+ }
216+
217+ write (v )
218+ }
219+
137220// jsonNumber converts a numeric value or a json.Number to a [big.Rat].
138221// If v is not a number, it returns nil, false.
139222func jsonNumber (v reflect.Value ) (* big.Rat , bool ) {
0 commit comments