package kv import ( "errors" "io/ioutil" "os" "path/filepath" "strings" "sync" ) var ( ErrInvalidKey = errors.New("kv: invalid key") ErrNoSuchKey = errors.New("kv: no such key") ) type DB struct { cache map[string][]byte basedir string m sync.RWMutex } func NewDB(basedir string) (db *DB, err error) { err = os.Mkdir(basedir, 0755) if err != nil && !os.IsExist(err) { return } return &DB{ cache: make(map[string][]byte), basedir: basedir, }, nil } func (db *DB) Set(key string, val []byte) (err error) { if len(key) < 1 || strings.ContainsRune(key, os.PathSeparator) { return ErrInvalidKey } db.m.Lock() defer db.m.Unlock() err = ioutil.WriteFile(filepath.Join(db.basedir, key), val, 0644) if err != nil { return } db.cache[key] = val return } func (db *DB) Get(key string) (val []byte, err error) { if len(key) < 1 || strings.ContainsRune(key, os.PathSeparator) { return nil, ErrInvalidKey } db.m.Lock() defer db.m.Unlock() data, ok := db.cache[key] if !ok { data, err = ioutil.ReadFile(filepath.Join(db.basedir, key)) if err != nil { if os.IsNotExist(err) { err = ErrNoSuchKey } return nil, err } db.cache[key] = data } val = make([]byte, len(data)) copy(val, data) return } func (db *DB) Remove(key string) (err error) { if len(key) < 1 || strings.ContainsRune(key, os.PathSeparator) { return ErrInvalidKey } db.m.Lock() defer db.m.Unlock() err = os.Remove(filepath.Join(db.basedir, key)) if err != nil { if os.IsNotExist(err) { err = ErrNoSuchKey } return } delete(db.cache, key) return }