| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package vsync |
| |
| // Helpful wrappers to a persistent key/value (K/V) DB used by Veyron Sync. |
| // The current underlying DB is an in-memory map. |
| |
| import ( |
| "bytes" |
| "fmt" |
| "sort" |
| |
| "v.io/v23/vom" |
| ) |
| |
| var memStore map[string]*kvdb |
| |
| type kvdb struct { |
| tables map[string]*kvtable |
| } |
| |
| type kvtable struct { |
| data map[string][]byte |
| } |
| |
| // kvdbOpen opens or creates a K/V DB for the given filename and table names |
| // within the DB. It returns the DB handler and handlers for each table. |
| func kvdbOpen(filename string, tables []string) (*kvdb, []*kvtable, error) { |
| if memStore == nil { |
| memStore = make(map[string]*kvdb) |
| } |
| |
| db := memStore[filename] |
| if db == nil { |
| db = &kvdb{tables: make(map[string]*kvtable)} |
| memStore[filename] = db |
| } |
| |
| tbls := make([]*kvtable, len(tables)) |
| |
| for i, table := range tables { |
| t := db.tables[table] |
| if t == nil { |
| t = &kvtable{data: make(map[string][]byte)} |
| db.tables[table] = t |
| } |
| tbls[i] = t |
| } |
| |
| return db, tbls, nil |
| } |
| |
| // close closes the given K/V DB. |
| func (db *kvdb) close() { |
| } |
| |
| // flush flushes the given K/V DB to disk. |
| func (db *kvdb) flush() { |
| } |
| |
| // set stores (or overwrites) the given key/value pair in the DB table. |
| func (t *kvtable) set(key string, value interface{}) error { |
| var val bytes.Buffer |
| if err := vom.NewEncoder(&val).Encode(value); err != nil { |
| return err |
| } |
| t.data[key] = val.Bytes() |
| return nil |
| } |
| |
| // create stores the given key/value pair in the DB table only if |
| // the key does not already exist. Otherwise it returns an error. |
| func (t *kvtable) create(key string, value interface{}) error { |
| if t.hasKey(key) { |
| return fmt.Errorf("key %s exists", key) |
| } |
| return t.set(key, value) |
| } |
| |
| // update stores the given key/value pair in the DB table only if |
| // the key already exists. Otherwise it returns an error. |
| func (t *kvtable) update(key string, value interface{}) error { |
| if !t.hasKey(key) { |
| return fmt.Errorf("key %s does not exist", key) |
| } |
| return t.set(key, value) |
| } |
| |
| // get retrieves the value of a key from the DB table. |
| func (t *kvtable) get(key string, value interface{}) error { |
| val := t.data[key] |
| if val == nil { |
| return fmt.Errorf("entry %s not found in the K/V DB table", key) |
| } |
| return vom.NewDecoder(bytes.NewBuffer(val)).Decode(value) |
| } |
| |
| // del deletes the entry in the DB table given its key. |
| func (t *kvtable) del(key string) error { |
| delete(t.data, key) |
| return nil |
| } |
| |
| // hasKey returns true if the given key exists in the DB table. |
| func (t *kvtable) hasKey(key string) bool { |
| val, ok := t.data[key] |
| return ok && val != nil |
| } |
| |
| // keyIter iterates over all keys in a DB table invoking the given callback |
| // function for each one. The key iterator callback is passed the item key. |
| func (t *kvtable) keyIter(keyIterCB func(key string)) error { |
| keys := make([]string, 0, len(t.data)) |
| for k := range t.data { |
| keys = append(keys, k) |
| } |
| sort.Strings(keys) |
| for _, k := range keys { |
| keyIterCB(k) |
| } |
| return nil |
| } |
| |
| // getNumKeys returns the number of keys (entries) in the DB table. |
| func (t *kvtable) getNumKeys() uint64 { |
| return uint64(len(t.data)) |
| } |