--- /dev/null
+package store
+
+// persistent key-value store
+// TODO: thread safety, persistence, efficient update and query, incremental backup
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "io/ioutil"
+ "sync"
+)
+
+
+type Conn struct {
+ name string
+ path string
+ mutex sync.RWMutex
+ memstore map[string][]byte
+}
+
+func Open(name string) (c *Conn, err error) {
+ c = new(Conn)
+ c.memstore = make(map[string][]byte)
+ c.name = name
+ c.path, err = filepath.Abs(name)
+ if err != nil {
+ return
+ }
+ err = os.MkdirAll(c.path, 0755)
+ if err != nil {
+ return
+ }
+ err = filepath.Walk(c.path, func(path string, info *os.FileInfo, err error) error {
+ if info.IsDirectory() {
+ if info.Name == c.name {
+ return nil
+ }
+ return filepath.SkipDir
+ }
+ if err != nil {
+ return err
+ }
+ k := info.Name
+ v, err := ioutil.ReadFile(filepath.Join(c.path, k))
+ if err != nil {
+ return err
+ }
+ c.memstore[k] = v
+ return nil
+ })
+ return
+}
+
+func (c *Conn) Get(k string) (v []byte, err error) {
+ c.mutex.RLock()
+ defer c.mutex.RUnlock()
+ v, ok := c.memstore[k]
+ if !ok {
+ err = fmt.Errorf("key not found")
+ }
+ return
+}
+
+func (c *Conn) Set(k string, v []byte) (err error) {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ f, err := os.Create(filepath.Join(c.path, k))
+ if err != nil {
+ return
+ }
+ _, err = f.Write(v)
+ if err != nil {
+ return
+ }
+ c.memstore[k] = v
+ return
+}
+
+func (c *Conn) Close() (err error) {
+ return
+}