diff options
Diffstat (limited to 'vendor/github.com/mitchellh/hashstructure')
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 @@ | |||
1 | The MIT License (MIT) | ||
2 | |||
3 | Copyright (c) 2016 Mitchell Hashimoto | ||
4 | |||
5 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
6 | of this software and associated documentation files (the "Software"), to deal | ||
7 | in the Software without restriction, including without limitation the rights | ||
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
9 | copies of the Software, and to permit persons to whom the Software is | ||
10 | furnished to do so, subject to the following conditions: | ||
11 | |||
12 | The above copyright notice and this permission notice shall be included in | ||
13 | all copies or substantial portions of the Software. | ||
14 | |||
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
21 | THE 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 | |||
3 | hashstructure is a Go library for creating a unique hash value | ||
4 | for arbitrary values in Go. | ||
5 | |||
6 | This can be used to key values in a hash (for use in a map, set, etc.) | ||
7 | that are complex. The most common use case is comparing two values without | ||
8 | sending 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 | |||
25 | Standard `go get`: | ||
26 | |||
27 | ``` | ||
28 | $ go get github.com/mitchellh/hashstructure | ||
29 | ``` | ||
30 | |||
31 | ## Usage & Example | ||
32 | |||
33 | For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/hashstructure). | ||
34 | |||
35 | A 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 @@ | |||
1 | package hashstructure | ||
2 | |||
3 | import ( | ||
4 | "encoding/binary" | ||
5 | "fmt" | ||
6 | "hash" | ||
7 | "hash/fnv" | ||
8 | "reflect" | ||
9 | ) | ||
10 | |||
11 | // HashOptions are options that are available for hashing. | ||
12 | type 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 | // | ||
49 | func 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 | |||
72 | type walker struct { | ||
73 | h hash.Hash64 | ||
74 | tag string | ||
75 | } | ||
76 | |||
77 | type 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 | |||
86 | func (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 | |||
295 | func 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 | |||
313 | func hashUpdateUnordered(a, b uint64) uint64 { | ||
314 | return a ^ b | ||
315 | } | ||
316 | |||
317 | // visitFlag is used as a bitmask for affecting visit behavior | ||
318 | type visitFlag uint | ||
319 | |||
320 | const ( | ||
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 @@ | |||
1 | package 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. | ||
6 | type 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. | ||
13 | type IncludableMap interface { | ||
14 | HashIncludeMap(field string, k, v interface{}) (bool, error) | ||
15 | } | ||