1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
package skate
import (
"crypto/sha1"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"gitlab.com/internetarchive/refcat/skate/atomic"
)
var ErrCacheMiss = errors.New("cache miss")
type Cache struct {
Dir string
initialized bool
}
// Init creates all directories.
func (c *Cache) init() error {
for i := 0; i < 256; i++ {
s := fmt.Sprintf("%02x", i)
d := path.Join(c.Dir, s)
if _, err := os.Stat(d); os.IsNotExist(err) {
if err := os.MkdirAll(d, 0755); err != nil {
return err
}
}
}
c.initialized = true
return nil
}
func (c *Cache) Has(k string) bool {
var (
shard, name = shardedHash(k)
spath = path.Join(c.Dir, shard, name)
)
if _, err := os.Stat(spath); os.IsNotExist(err) {
return false
}
return true
}
func (c *Cache) Get(k string) ([]byte, error) {
var (
shard, name = shardedHash(k)
spath = path.Join(c.Dir, shard, name)
)
if !c.initialized {
if err := c.init(); err != nil {
return nil, err
}
}
if _, err := os.Stat(spath); os.IsNotExist(err) {
return nil, ErrCacheMiss
}
f, err := os.Open(spath)
if err != nil {
return nil, err
}
defer f.Close()
return ioutil.ReadAll(f)
}
func (c *Cache) Set(k string, v []byte) error {
var (
shard, name = shardedHash(k)
spath = path.Join(c.Dir, shard, name)
)
if !c.initialized {
if err := c.init(); err != nil {
return err
}
}
if err := atomic.WriteFile(spath, v, 0755); err != nil {
return err
}
return nil
}
// shardedHash returns a sha1 shard (2) and name (38) for a given string value
// (e.g. a URL).
func shardedHash(v string) (shard, name string) {
h := sha1.New()
_, _ = io.WriteString(h, v)
name = fmt.Sprintf("%x", h.Sum(nil))
return name[:2], name[2:]
}
|