|
5 | 5 | "fmt"
|
6 | 6 | "hash"
|
7 | 7 | "hash/fnv"
|
| 8 | + "io" |
8 | 9 | "reflect"
|
9 | 10 | "time"
|
10 | 11 | )
|
@@ -122,6 +123,13 @@ type visitOpts struct {
|
122 | 123 |
|
123 | 124 | var timeType = reflect.TypeOf(time.Time{})
|
124 | 125 |
|
| 126 | +// A direct hash calculation used for numeric and bool values. |
| 127 | +func (w *walker) hashDirect(v any) (uint64, error) { |
| 128 | + w.h.Reset() |
| 129 | + err := binary.Write(w.h, binary.LittleEndian, v) |
| 130 | + return w.h.Sum64(), err |
| 131 | +} |
| 132 | + |
125 | 133 | func (w *walker) visit(v reflect.Value, opts *visitOpts) (uint64, error) {
|
126 | 134 | t := reflect.TypeOf(0)
|
127 | 135 |
|
@@ -152,29 +160,34 @@ func (w *walker) visit(v reflect.Value, opts *visitOpts) (uint64, error) {
|
152 | 160 | v = reflect.Zero(t)
|
153 | 161 | }
|
154 | 162 |
|
155 |
| - // Binary writing can use raw ints, we have to convert to |
156 |
| - // a sized-int, we'll choose the largest... |
157 |
| - switch v.Kind() { |
158 |
| - case reflect.Int: |
159 |
| - v = reflect.ValueOf(int64(v.Int())) |
160 |
| - case reflect.Uint: |
161 |
| - v = reflect.ValueOf(uint64(v.Uint())) |
162 |
| - case reflect.Bool: |
163 |
| - var tmp int8 |
164 |
| - if v.Bool() { |
165 |
| - tmp = 1 |
| 163 | + if v.CanInt() { |
| 164 | + if v.Kind() == reflect.Int { |
| 165 | + // binary.Write requires a fixed-size value. |
| 166 | + return w.hashDirect(v.Int()) |
166 | 167 | }
|
167 |
| - v = reflect.ValueOf(tmp) |
| 168 | + return w.hashDirect(v.Interface()) |
| 169 | + } |
| 170 | + |
| 171 | + if v.CanUint() { |
| 172 | + if v.Kind() == reflect.Uint { |
| 173 | + // binary.Write requires a fixed-size value. |
| 174 | + return w.hashDirect(v.Uint()) |
| 175 | + } |
| 176 | + return w.hashDirect(v.Interface()) |
| 177 | + } |
| 178 | + |
| 179 | + if v.CanFloat() { |
| 180 | + return w.hashDirect(v.Interface()) |
168 | 181 | }
|
169 | 182 |
|
170 | 183 | k := v.Kind()
|
171 | 184 |
|
172 |
| - // We can shortcut numeric values by directly binary writing them |
173 |
| - if k >= reflect.Int && k <= reflect.Complex64 { |
174 |
| - // A direct hash calculation |
175 |
| - w.h.Reset() |
176 |
| - err := binary.Write(w.h, binary.LittleEndian, v.Interface()) |
177 |
| - return w.h.Sum64(), err |
| 185 | + if k == reflect.Bool { |
| 186 | + var tmp int8 |
| 187 | + if v.Bool() { |
| 188 | + tmp = 1 |
| 189 | + } |
| 190 | + return w.hashDirect(tmp) |
178 | 191 | }
|
179 | 192 |
|
180 | 193 | switch v.Type() {
|
@@ -394,7 +407,10 @@ func (w *walker) visit(v reflect.Value, opts *visitOpts) (uint64, error) {
|
394 | 407 | case reflect.String:
|
395 | 408 | // Directly hash
|
396 | 409 | w.h.Reset()
|
397 |
| - _, err := w.h.Write([]byte(v.String())) |
| 410 | + |
| 411 | + // io.WriteString uses io.StringWriter if it exists, which is |
| 412 | + // implemented by e.g. github.com/cespare/xxhash. |
| 413 | + _, err := io.WriteString(w.h, v.String()) |
398 | 414 | return w.h.Sum64(), err
|
399 | 415 |
|
400 | 416 | default:
|
|
0 commit comments