diff options
Diffstat (limited to 'vendor/honnef.co/go/tools/internal/cache/hash.go')
-rw-r--r-- | vendor/honnef.co/go/tools/internal/cache/hash.go | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/vendor/honnef.co/go/tools/internal/cache/hash.go b/vendor/honnef.co/go/tools/internal/cache/hash.go new file mode 100644 index 0000000..a53543e --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/cache/hash.go | |||
@@ -0,0 +1,176 @@ | |||
1 | // Copyright 2017 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package cache | ||
6 | |||
7 | import ( | ||
8 | "bytes" | ||
9 | "crypto/sha256" | ||
10 | "fmt" | ||
11 | "hash" | ||
12 | "io" | ||
13 | "os" | ||
14 | "sync" | ||
15 | ) | ||
16 | |||
17 | var debugHash = false // set when GODEBUG=gocachehash=1 | ||
18 | |||
19 | // HashSize is the number of bytes in a hash. | ||
20 | const HashSize = 32 | ||
21 | |||
22 | // A Hash provides access to the canonical hash function used to index the cache. | ||
23 | // The current implementation uses salted SHA256, but clients must not assume this. | ||
24 | type Hash struct { | ||
25 | h hash.Hash | ||
26 | name string // for debugging | ||
27 | buf *bytes.Buffer // for verify | ||
28 | } | ||
29 | |||
30 | // hashSalt is a salt string added to the beginning of every hash | ||
31 | // created by NewHash. Using the Staticcheck version makes sure that different | ||
32 | // versions of the command do not address the same cache | ||
33 | // entries, so that a bug in one version does not affect the execution | ||
34 | // of other versions. This salt will result in additional ActionID files | ||
35 | // in the cache, but not additional copies of the large output files, | ||
36 | // which are still addressed by unsalted SHA256. | ||
37 | var hashSalt []byte | ||
38 | |||
39 | func SetSalt(b []byte) { | ||
40 | hashSalt = b | ||
41 | } | ||
42 | |||
43 | // Subkey returns an action ID corresponding to mixing a parent | ||
44 | // action ID with a string description of the subkey. | ||
45 | func Subkey(parent ActionID, desc string) ActionID { | ||
46 | h := sha256.New() | ||
47 | h.Write([]byte("subkey:")) | ||
48 | h.Write(parent[:]) | ||
49 | h.Write([]byte(desc)) | ||
50 | var out ActionID | ||
51 | h.Sum(out[:0]) | ||
52 | if debugHash { | ||
53 | fmt.Fprintf(os.Stderr, "HASH subkey %x %q = %x\n", parent, desc, out) | ||
54 | } | ||
55 | if verify { | ||
56 | hashDebug.Lock() | ||
57 | hashDebug.m[out] = fmt.Sprintf("subkey %x %q", parent, desc) | ||
58 | hashDebug.Unlock() | ||
59 | } | ||
60 | return out | ||
61 | } | ||
62 | |||
63 | // NewHash returns a new Hash. | ||
64 | // The caller is expected to Write data to it and then call Sum. | ||
65 | func NewHash(name string) *Hash { | ||
66 | h := &Hash{h: sha256.New(), name: name} | ||
67 | if debugHash { | ||
68 | fmt.Fprintf(os.Stderr, "HASH[%s]\n", h.name) | ||
69 | } | ||
70 | h.Write(hashSalt) | ||
71 | if verify { | ||
72 | h.buf = new(bytes.Buffer) | ||
73 | } | ||
74 | return h | ||
75 | } | ||
76 | |||
77 | // Write writes data to the running hash. | ||
78 | func (h *Hash) Write(b []byte) (int, error) { | ||
79 | if debugHash { | ||
80 | fmt.Fprintf(os.Stderr, "HASH[%s]: %q\n", h.name, b) | ||
81 | } | ||
82 | if h.buf != nil { | ||
83 | h.buf.Write(b) | ||
84 | } | ||
85 | return h.h.Write(b) | ||
86 | } | ||
87 | |||
88 | // Sum returns the hash of the data written previously. | ||
89 | func (h *Hash) Sum() [HashSize]byte { | ||
90 | var out [HashSize]byte | ||
91 | h.h.Sum(out[:0]) | ||
92 | if debugHash { | ||
93 | fmt.Fprintf(os.Stderr, "HASH[%s]: %x\n", h.name, out) | ||
94 | } | ||
95 | if h.buf != nil { | ||
96 | hashDebug.Lock() | ||
97 | if hashDebug.m == nil { | ||
98 | hashDebug.m = make(map[[HashSize]byte]string) | ||
99 | } | ||
100 | hashDebug.m[out] = h.buf.String() | ||
101 | hashDebug.Unlock() | ||
102 | } | ||
103 | return out | ||
104 | } | ||
105 | |||
106 | // In GODEBUG=gocacheverify=1 mode, | ||
107 | // hashDebug holds the input to every computed hash ID, | ||
108 | // so that we can work backward from the ID involved in a | ||
109 | // cache entry mismatch to a description of what should be there. | ||
110 | var hashDebug struct { | ||
111 | sync.Mutex | ||
112 | m map[[HashSize]byte]string | ||
113 | } | ||
114 | |||
115 | // reverseHash returns the input used to compute the hash id. | ||
116 | func reverseHash(id [HashSize]byte) string { | ||
117 | hashDebug.Lock() | ||
118 | s := hashDebug.m[id] | ||
119 | hashDebug.Unlock() | ||
120 | return s | ||
121 | } | ||
122 | |||
123 | var hashFileCache struct { | ||
124 | sync.Mutex | ||
125 | m map[string][HashSize]byte | ||
126 | } | ||
127 | |||
128 | // FileHash returns the hash of the named file. | ||
129 | // It caches repeated lookups for a given file, | ||
130 | // and the cache entry for a file can be initialized | ||
131 | // using SetFileHash. | ||
132 | // The hash used by FileHash is not the same as | ||
133 | // the hash used by NewHash. | ||
134 | func FileHash(file string) ([HashSize]byte, error) { | ||
135 | hashFileCache.Lock() | ||
136 | out, ok := hashFileCache.m[file] | ||
137 | hashFileCache.Unlock() | ||
138 | |||
139 | if ok { | ||
140 | return out, nil | ||
141 | } | ||
142 | |||
143 | h := sha256.New() | ||
144 | f, err := os.Open(file) | ||
145 | if err != nil { | ||
146 | if debugHash { | ||
147 | fmt.Fprintf(os.Stderr, "HASH %s: %v\n", file, err) | ||
148 | } | ||
149 | return [HashSize]byte{}, err | ||
150 | } | ||
151 | _, err = io.Copy(h, f) | ||
152 | f.Close() | ||
153 | if err != nil { | ||
154 | if debugHash { | ||
155 | fmt.Fprintf(os.Stderr, "HASH %s: %v\n", file, err) | ||
156 | } | ||
157 | return [HashSize]byte{}, err | ||
158 | } | ||
159 | h.Sum(out[:0]) | ||
160 | if debugHash { | ||
161 | fmt.Fprintf(os.Stderr, "HASH %s: %x\n", file, out) | ||
162 | } | ||
163 | |||
164 | SetFileHash(file, out) | ||
165 | return out, nil | ||
166 | } | ||
167 | |||
168 | // SetFileHash sets the hash returned by FileHash for file. | ||
169 | func SetFileHash(file string, sum [HashSize]byte) { | ||
170 | hashFileCache.Lock() | ||
171 | if hashFileCache.m == nil { | ||
172 | hashFileCache.m = make(map[string][HashSize]byte) | ||
173 | } | ||
174 | hashFileCache.m[file] = sum | ||
175 | hashFileCache.Unlock() | ||
176 | } | ||