aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/mitchellh/hashstructure
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mitchellh/hashstructure')
-rw-r--r--vendor/github.com/mitchellh/hashstructure/LICENSE21
-rw-r--r--vendor/github.com/mitchellh/hashstructure/README.md61
-rw-r--r--vendor/github.com/mitchellh/hashstructure/hashstructure.go323
-rw-r--r--vendor/github.com/mitchellh/hashstructure/include.go15
4 files changed, 420 insertions, 0 deletions
diff --git a/vendor/github.com/mitchellh/hashstructure/LICENSE b/vendor/github.com/mitchellh/hashstructure/LICENSE
new file mode 100644
index 0000000..a3866a2
--- /dev/null
+++ b/vendor/github.com/mitchellh/hashstructure/LICENSE
@@ -0,0 +1,21 @@
1The MIT License (MIT)
2
3Copyright (c) 2016 Mitchell Hashimoto
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21THE SOFTWARE.
diff --git a/vendor/github.com/mitchellh/hashstructure/README.md b/vendor/github.com/mitchellh/hashstructure/README.md
new file mode 100644
index 0000000..7d0de5b
--- /dev/null
+++ b/vendor/github.com/mitchellh/hashstructure/README.md
@@ -0,0 +1,61 @@
1# hashstructure
2
3hashstructure is a Go library for creating a unique hash value
4for arbitrary values in Go.
5
6This can be used to key values in a hash (for use in a map, set, etc.)
7that are complex. The most common use case is comparing two values without
8sending data across the network, caching values locally (de-dup), and so on.
9
10## Features
11
12 * Hash any arbitrary Go value, including complex types.
13
14 * Tag a struct field to ignore it and not affect the hash value.
15
16 * Tag a slice type struct field to treat it as a set where ordering
17 doesn't affect the hash code but the field itself is still taken into
18 account to create the hash value.
19
20 * Optionally specify a custom hash function to optimize for speed, collision
21 avoidance for your data set, etc.
22
23## Installation
24
25Standard `go get`:
26
27```
28$ go get github.com/mitchellh/hashstructure
29```
30
31## Usage & Example
32
33For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/hashstructure).
34
35A quick code example is shown below:
36
37
38 type ComplexStruct struct {
39 Name string
40 Age uint
41 Metadata map[string]interface{}
42 }
43
44 v := ComplexStruct{
45 Name: "mitchellh",
46 Age: 64,
47 Metadata: map[string]interface{}{
48 "car": true,
49 "location": "California",
50 "siblings": []string{"Bob", "John"},
51 },
52 }
53
54 hash, err := hashstructure.Hash(v, nil)
55 if err != nil {
56 panic(err)
57 }
58
59 fmt.Printf("%d", hash)
60 // Output:
61 // 2307517237273902113
diff --git a/vendor/github.com/mitchellh/hashstructure/hashstructure.go b/vendor/github.com/mitchellh/hashstructure/hashstructure.go
new file mode 100644
index 0000000..6f586fa
--- /dev/null
+++ b/vendor/github.com/mitchellh/hashstructure/hashstructure.go
@@ -0,0 +1,323 @@
1package hashstructure
2
3import (
4 "encoding/binary"
5 "fmt"
6 "hash"
7 "hash/fnv"
8 "reflect"
9)
10
11// HashOptions are options that are available for hashing.
12type HashOptions struct {
13 // Hasher is the hash function to use. If this isn't set, it will
14 // default to FNV.
15 Hasher hash.Hash64
16
17 // TagName is the struct tag to look at when hashing the structure.
18 // By default this is "hash".
19 TagName string
20}
21
22// Hash returns the hash value of an arbitrary value.
23//
24// If opts is nil, then default options will be used. See HashOptions
25// for the default values.
26//
27// Notes on the value:
28//
29// * Unexported fields on structs are ignored and do not affect the
30// hash value.
31//
32// * Adding an exported field to a struct with the zero value will change
33// the hash value.
34//
35// For structs, the hashing can be controlled using tags. For example:
36//
37// struct {
38// Name string
39// UUID string `hash:"ignore"`
40// }
41//
42// The available tag values are:
43//
44// * "ignore" - The field will be ignored and not affect the hash code.
45//
46// * "set" - The field will be treated as a set, where ordering doesn't
47// affect the hash code. This only works for slices.
48//
49func Hash(v interface{}, opts *HashOptions) (uint64, error) {
50 // Create default options
51 if opts == nil {
52 opts = &HashOptions{}
53 }
54 if opts.Hasher == nil {
55 opts.Hasher = fnv.New64()
56 }
57 if opts.TagName == "" {
58 opts.TagName = "hash"
59 }
60
61 // Reset the hash
62 opts.Hasher.Reset()
63
64 // Create our walker and walk the structure
65 w := &walker{
66 h: opts.Hasher,
67 tag: opts.TagName,
68 }
69 return w.visit(reflect.ValueOf(v), nil)
70}
71
72type walker struct {
73 h hash.Hash64
74 tag string
75}
76
77type visitOpts struct {
78 // Flags are a bitmask of flags to affect behavior of this visit
79 Flags visitFlag
80
81 // Information about the struct containing this field
82 Struct interface{}
83 StructField string
84}
85
86func (w *walker) visit(v reflect.Value, opts *visitOpts) (uint64, error) {
87 // Loop since these can be wrapped in multiple layers of pointers
88 // and interfaces.
89 for {
90 // If we have an interface, dereference it. We have to do this up
91 // here because it might be a nil in there and the check below must
92 // catch that.
93 if v.Kind() == reflect.Interface {
94 v = v.Elem()
95 continue
96 }
97
98 if v.Kind() == reflect.Ptr {
99 v = reflect.Indirect(v)
100 continue
101 }
102
103 break
104 }
105
106 // If it is nil, treat it like a zero.
107 if !v.IsValid() {
108 var tmp int8
109 v = reflect.ValueOf(tmp)
110 }
111
112 // Binary writing can use raw ints, we have to convert to
113 // a sized-int, we'll choose the largest...
114 switch v.Kind() {
115 case reflect.Int:
116 v = reflect.ValueOf(int64(v.Int()))
117 case reflect.Uint:
118 v = reflect.ValueOf(uint64(v.Uint()))
119 case reflect.Bool:
120 var tmp int8
121 if v.Bool() {
122 tmp = 1
123 }
124 v = reflect.ValueOf(tmp)
125 }
126
127 k := v.Kind()
128
129 // We can shortcut numeric values by directly binary writing them
130 if k >= reflect.Int && k <= reflect.Complex64 {
131 // A direct hash calculation
132 w.h.Reset()
133 err := binary.Write(w.h, binary.LittleEndian, v.Interface())
134 return w.h.Sum64(), err
135 }
136
137 switch k {
138 case reflect.Array:
139 var h uint64
140 l := v.Len()
141 for i := 0; i < l; i++ {
142 current, err := w.visit(v.Index(i), nil)
143 if err != nil {
144 return 0, err
145 }
146
147 h = hashUpdateOrdered(w.h, h, current)
148 }
149
150 return h, nil
151
152 case reflect.Map:
153 var includeMap IncludableMap
154 if opts != nil && opts.Struct != nil {
155 if v, ok := opts.Struct.(IncludableMap); ok {
156 includeMap = v
157 }
158 }
159
160 // Build the hash for the map. We do this by XOR-ing all the key
161 // and value hashes. This makes it deterministic despite ordering.
162 var h uint64
163 for _, k := range v.MapKeys() {
164 v := v.MapIndex(k)
165 if includeMap != nil {
166 incl, err := includeMap.HashIncludeMap(
167 opts.StructField, k.Interface(), v.Interface())
168 if err != nil {
169 return 0, err
170 }
171 if !incl {
172 continue
173 }
174 }
175
176 kh, err := w.visit(k, nil)
177 if err != nil {
178 return 0, err
179 }
180 vh, err := w.visit(v, nil)
181 if err != nil {
182 return 0, err
183 }
184
185 fieldHash := hashUpdateOrdered(w.h, kh, vh)
186 h = hashUpdateUnordered(h, fieldHash)
187 }
188
189 return h, nil
190
191 case reflect.Struct:
192 var include Includable
193 parent := v.Interface()
194 if impl, ok := parent.(Includable); ok {
195 include = impl
196 }
197
198 t := v.Type()
199 h, err := w.visit(reflect.ValueOf(t.Name()), nil)
200 if err != nil {
201 return 0, err
202 }
203
204 l := v.NumField()
205 for i := 0; i < l; i++ {
206 if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" {
207 var f visitFlag
208 fieldType := t.Field(i)
209 if fieldType.PkgPath != "" {
210 // Unexported
211 continue
212 }
213
214 tag := fieldType.Tag.Get(w.tag)
215 if tag == "ignore" {
216 // Ignore this field
217 continue
218 }
219
220 // Check if we implement includable and check it
221 if include != nil {
222 incl, err := include.HashInclude(fieldType.Name, v)
223 if err != nil {
224 return 0, err
225 }
226 if !incl {
227 continue
228 }
229 }
230
231 switch tag {
232 case "set":
233 f |= visitFlagSet
234 }
235
236 kh, err := w.visit(reflect.ValueOf(fieldType.Name), nil)
237 if err != nil {
238 return 0, err
239 }
240
241 vh, err := w.visit(v, &visitOpts{
242 Flags: f,
243 Struct: parent,
244 StructField: fieldType.Name,
245 })
246 if err != nil {
247 return 0, err
248 }
249
250 fieldHash := hashUpdateOrdered(w.h, kh, vh)
251 h = hashUpdateUnordered(h, fieldHash)
252 }
253 }
254
255 return h, nil
256
257 case reflect.Slice:
258 // We have two behaviors here. If it isn't a set, then we just
259 // visit all the elements. If it is a set, then we do a deterministic
260 // hash code.
261 var h uint64
262 var set bool
263 if opts != nil {
264 set = (opts.Flags & visitFlagSet) != 0
265 }
266 l := v.Len()
267 for i := 0; i < l; i++ {
268 current, err := w.visit(v.Index(i), nil)
269 if err != nil {
270 return 0, err
271 }
272
273 if set {
274 h = hashUpdateUnordered(h, current)
275 } else {
276 h = hashUpdateOrdered(w.h, h, current)
277 }
278 }
279
280 return h, nil
281
282 case reflect.String:
283 // Directly hash
284 w.h.Reset()
285 _, err := w.h.Write([]byte(v.String()))
286 return w.h.Sum64(), err
287
288 default:
289 return 0, fmt.Errorf("unknown kind to hash: %s", k)
290 }
291
292 return 0, nil
293}
294
295func hashUpdateOrdered(h hash.Hash64, a, b uint64) uint64 {
296 // For ordered updates, use a real hash function
297 h.Reset()
298
299 // We just panic if the binary writes fail because we are writing
300 // an int64 which should never be fail-able.
301 e1 := binary.Write(h, binary.LittleEndian, a)
302 e2 := binary.Write(h, binary.LittleEndian, b)
303 if e1 != nil {
304 panic(e1)
305 }
306 if e2 != nil {
307 panic(e2)
308 }
309
310 return h.Sum64()
311}
312
313func hashUpdateUnordered(a, b uint64) uint64 {
314 return a ^ b
315}
316
317// visitFlag is used as a bitmask for affecting visit behavior
318type visitFlag uint
319
320const (
321 visitFlagInvalid visitFlag = iota
322 visitFlagSet = iota << 1
323)
diff --git a/vendor/github.com/mitchellh/hashstructure/include.go b/vendor/github.com/mitchellh/hashstructure/include.go
new file mode 100644
index 0000000..b6289c0
--- /dev/null
+++ b/vendor/github.com/mitchellh/hashstructure/include.go
@@ -0,0 +1,15 @@
1package hashstructure
2
3// Includable is an interface that can optionally be implemented by
4// a struct. It will be called for each field in the struct to check whether
5// it should be included in the hash.
6type Includable interface {
7 HashInclude(field string, v interface{}) (bool, error)
8}
9
10// IncludableMap is an interface that can optionally be implemented by
11// a struct. It will be called when a map-type field is found to ask the
12// struct if the map item should be included in the hash.
13type IncludableMap interface {
14 HashIncludeMap(field string, k, v interface{}) (bool, error)
15}