diff options
Diffstat (limited to 'skate/cache.go')
-rw-r--r-- | skate/cache.go | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/skate/cache.go b/skate/cache.go new file mode 100644 index 0000000..e5b0171 --- /dev/null +++ b/skate/cache.go @@ -0,0 +1,92 @@ +package skate + +import ( + "crypto/sha1" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path" + + "git.archive.org/martin/cgraph/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:] +} |