Merge branch 'master' of https://git.freesoftwareextremist.com/bloat
[bloat] / kv / kv.go
1 package kv
2
3 import (
4         "errors"
5         "io/ioutil"
6         "os"
7         "path/filepath"
8         "strings"
9         "sync"
10 )
11
12 var (
13         errInvalidKey = errors.New("invalid key")
14         errNoSuchKey  = errors.New("no such key")
15 )
16
17 type Database struct {
18         cache   map[string][]byte
19         basedir string
20         m       sync.RWMutex
21 }
22
23 func NewDatabse(basedir string) (db *Database, err error) {
24         err = os.Mkdir(basedir, 0755)
25         if err != nil && !os.IsExist(err) {
26                 return
27         }
28
29         return &Database{
30                 cache:   make(map[string][]byte),
31                 basedir: basedir,
32         }, nil
33 }
34
35 func (db *Database) Set(key string, val []byte) (err error) {
36         if len(key) < 1 || strings.ContainsRune(key, os.PathSeparator) {
37                 return errInvalidKey
38         }
39
40         err = ioutil.WriteFile(filepath.Join(db.basedir, key), val, 0644)
41         if err != nil {
42                 return
43         }
44
45         db.m.Lock()
46         db.cache[key] = val
47         db.m.Unlock()
48
49         return
50 }
51
52 func (db *Database) Get(key string) (val []byte, err error) {
53         if len(key) < 1 || strings.ContainsRune(key, os.PathSeparator) {
54                 return nil, errInvalidKey
55         }
56
57         db.m.RLock()
58         data, ok := db.cache[key]
59         db.m.RUnlock()
60
61         if !ok {
62                 data, err = ioutil.ReadFile(filepath.Join(db.basedir, key))
63                 if err != nil {
64                         err = errNoSuchKey
65                         return nil, err
66                 }
67
68                 db.m.Lock()
69                 db.cache[key] = data
70                 db.m.Unlock()
71         }
72
73         val = make([]byte, len(data))
74         copy(val, data)
75
76         return
77 }
78
79 func (db *Database) Remove(key string) {
80         if len(key) < 1 || strings.ContainsRune(key, os.PathSeparator) {
81                 return
82         }
83
84         os.Remove(filepath.Join(db.basedir, key))
85
86         db.m.Lock()
87         delete(db.cache, key)
88         db.m.Unlock()
89
90         return
91 }