feat(qs2) add qs2 framework
This commit is contained in:
parent
6ae65fe09b
commit
83100007e3
33 changed files with 2934 additions and 60 deletions
225
src/kvstore/boltdbpvd/provider.go
Normal file
225
src/kvstore/boltdbpvd/provider.go
Normal file
|
@ -0,0 +1,225 @@
|
|||
package boltdbpvd
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
|
||||
"github.com/ihexxa/quickshare/src/kvstore"
|
||||
)
|
||||
|
||||
type BoltPvd struct {
|
||||
dbPath string
|
||||
db *bolt.DB
|
||||
maxStrLen int
|
||||
}
|
||||
|
||||
func New(dbPath string, maxStrLen int) *BoltPvd {
|
||||
boltPath := path.Join(path.Clean(dbPath), "quickshare.db")
|
||||
db, err := bolt.Open(boltPath, 0600, &bolt.Options{Timeout: 1 * time.Second})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
buckets := []string{"bools", "ints", "int64s", "floats", "strings", "locks"}
|
||||
for _, bucketName := range buckets {
|
||||
db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(bucketName))
|
||||
if b != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := tx.CreateBucket([]byte(bucketName))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return &BoltPvd{
|
||||
dbPath: dbPath,
|
||||
db: db,
|
||||
maxStrLen: maxStrLen,
|
||||
}
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) Close() error {
|
||||
return bp.db.Close()
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) GetBool(key string) (bool, bool) {
|
||||
buf, ok := make([]byte, 1), false
|
||||
|
||||
bp.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("bools"))
|
||||
v := b.Get([]byte(key))
|
||||
copy(buf, v)
|
||||
ok = v != nil
|
||||
return nil
|
||||
})
|
||||
|
||||
// 1 means true, 0 means false
|
||||
return buf[0] == 1, ok
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) SetBool(key string, val bool) error {
|
||||
var bVal byte = 0
|
||||
if val {
|
||||
bVal = 1
|
||||
}
|
||||
return bp.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("bools"))
|
||||
return b.Put([]byte(key), []byte{bVal})
|
||||
})
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) DelBool(key string) error {
|
||||
return bp.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("bools"))
|
||||
return b.Delete([]byte(key))
|
||||
})
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) GetInt(key string) (int, bool) {
|
||||
x, ok := bp.GetInt64(key)
|
||||
return int(x), ok
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) SetInt(key string, val int) error {
|
||||
return bp.SetInt64(key, int64(val))
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) DelInt(key string) error {
|
||||
return bp.DelInt64(key)
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) GetInt64(key string) (int64, bool) {
|
||||
buf, ok := make([]byte, binary.MaxVarintLen64), false
|
||||
|
||||
bp.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("int64s"))
|
||||
v := b.Get([]byte(key))
|
||||
copy(buf, v)
|
||||
ok = v != nil
|
||||
return nil
|
||||
})
|
||||
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
x, n := binary.Varint(buf)
|
||||
if n < 0 {
|
||||
return 0, false
|
||||
}
|
||||
return x, true
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) SetInt64(key string, val int64) error {
|
||||
buf := make([]byte, binary.MaxVarintLen64)
|
||||
n := binary.PutVarint(buf, val)
|
||||
|
||||
return bp.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("int64s"))
|
||||
return b.Put([]byte(key), buf[:n])
|
||||
})
|
||||
}
|
||||
func (bp *BoltPvd) DelInt64(key string) error {
|
||||
return bp.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("int64s"))
|
||||
return b.Delete([]byte(key))
|
||||
})
|
||||
}
|
||||
|
||||
func float64ToBytes(num float64) []byte {
|
||||
buf := make([]byte, 64)
|
||||
binary.PutUvarint(buf, math.Float64bits(num))
|
||||
return buf
|
||||
}
|
||||
|
||||
func bytesToFloat64(buf []byte) float64 {
|
||||
uintVal, _ := binary.Uvarint(buf[:64])
|
||||
return math.Float64frombits(uintVal)
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) GetFloat(key string) (float64, bool) {
|
||||
buf, ok := make([]byte, 64), false
|
||||
bp.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("floats"))
|
||||
v := b.Get([]byte(key))
|
||||
copy(buf, v)
|
||||
ok = v != nil
|
||||
return nil
|
||||
})
|
||||
if !ok {
|
||||
return 0.0, false
|
||||
}
|
||||
return bytesToFloat64(buf), true
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) SetFloat(key string, val float64) error {
|
||||
return bp.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("floats"))
|
||||
return b.Put([]byte(key), float64ToBytes(val))
|
||||
})
|
||||
}
|
||||
func (bp *BoltPvd) DelFloat(key string) error {
|
||||
return bp.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("floats"))
|
||||
return b.Delete([]byte(key))
|
||||
})
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) GetString(key string) (string, bool) {
|
||||
buf, ok, length := make([]byte, bp.maxStrLen), false, bp.maxStrLen
|
||||
bp.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("strings"))
|
||||
v := b.Get([]byte(key))
|
||||
length = copy(buf, v)
|
||||
ok = v != nil
|
||||
return nil
|
||||
})
|
||||
return string(buf[:length]), ok
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) SetString(key string, val string) error {
|
||||
if len(val) > bp.maxStrLen {
|
||||
return fmt.Errorf("can not set string value longer than %d", bp.maxStrLen)
|
||||
}
|
||||
|
||||
return bp.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("strings"))
|
||||
return b.Put([]byte(key), []byte(val))
|
||||
})
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) DelString(key string) error {
|
||||
return bp.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("strings"))
|
||||
return b.Delete([]byte(key))
|
||||
})
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) TryLock(key string) error {
|
||||
return bp.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("locks"))
|
||||
if b.Get([]byte(key)) != nil {
|
||||
return kvstore.ErrLocked
|
||||
}
|
||||
return b.Put([]byte(key), []byte{})
|
||||
})
|
||||
}
|
||||
|
||||
func (bp *BoltPvd) Unlock(key string) error {
|
||||
return bp.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte("locks"))
|
||||
if b.Get([]byte(key)) != nil {
|
||||
return b.Delete([]byte(key))
|
||||
}
|
||||
return kvstore.ErrNoLock
|
||||
})
|
||||
}
|
26
src/kvstore/kvstore_interface.go
Normal file
26
src/kvstore/kvstore_interface.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package kvstore
|
||||
|
||||
import "errors"
|
||||
|
||||
var ErrLocked = errors.New("already locked")
|
||||
var ErrNoLock = errors.New("no lock to unlock")
|
||||
|
||||
type IKVStore interface {
|
||||
GetBool(key string) (bool, bool)
|
||||
SetBool(key string, val bool) error
|
||||
DelBool(key string) error
|
||||
GetInt(key string) (int, bool)
|
||||
SetInt(key string, val int) error
|
||||
DelInt(key string) error
|
||||
GetInt64(key string) (int64, bool)
|
||||
SetInt64(key string, val int64) error
|
||||
DelInt64(key string) error
|
||||
GetFloat(key string) (float64, bool)
|
||||
SetFloat(key string, val float64) error
|
||||
DelFloat(key string) error
|
||||
GetString(key string) (string, bool)
|
||||
SetString(key string, val string) error
|
||||
DelString(key string) error
|
||||
TryLock(key string) error
|
||||
Unlock(key string) error
|
||||
}
|
166
src/kvstore/memstore/provider.go
Normal file
166
src/kvstore/memstore/provider.go
Normal file
|
@ -0,0 +1,166 @@
|
|||
package memstore
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/ihexxa/quickshare/src/kvstore"
|
||||
)
|
||||
|
||||
type MemStore struct {
|
||||
bools map[string]bool
|
||||
boolsMtx *sync.Mutex
|
||||
ints map[string]int
|
||||
intsMtx *sync.Mutex
|
||||
int64s map[string]int64
|
||||
int64sMtx *sync.Mutex
|
||||
floats map[string]float64
|
||||
floatsMtx *sync.Mutex
|
||||
strings map[string]string
|
||||
stringsMtx *sync.Mutex
|
||||
locks map[string]bool
|
||||
locksMtx *sync.Mutex
|
||||
}
|
||||
|
||||
func New() *MemStore {
|
||||
return &MemStore{
|
||||
bools: map[string]bool{},
|
||||
boolsMtx: &sync.Mutex{},
|
||||
ints: map[string]int{},
|
||||
intsMtx: &sync.Mutex{},
|
||||
int64s: map[string]int64{},
|
||||
int64sMtx: &sync.Mutex{},
|
||||
floats: map[string]float64{},
|
||||
floatsMtx: &sync.Mutex{},
|
||||
strings: map[string]string{},
|
||||
stringsMtx: &sync.Mutex{},
|
||||
locks: map[string]bool{},
|
||||
locksMtx: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (st *MemStore) GetBool(key string) (bool, bool) {
|
||||
st.boolsMtx.Lock()
|
||||
defer st.boolsMtx.Unlock()
|
||||
val, ok := st.bools[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func (st *MemStore) SetBool(key string, val bool) error {
|
||||
st.boolsMtx.Lock()
|
||||
defer st.boolsMtx.Unlock()
|
||||
st.bools[key] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *MemStore) GetInt(key string) (int, bool) {
|
||||
st.intsMtx.Lock()
|
||||
defer st.intsMtx.Unlock()
|
||||
val, ok := st.ints[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func (st *MemStore) SetInt(key string, val int) error {
|
||||
st.intsMtx.Lock()
|
||||
defer st.intsMtx.Unlock()
|
||||
st.ints[key] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *MemStore) GetInt64(key string) (int64, bool) {
|
||||
st.int64sMtx.Lock()
|
||||
defer st.int64sMtx.Unlock()
|
||||
val, ok := st.int64s[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func (st *MemStore) SetInt64(key string, val int64) error {
|
||||
st.int64sMtx.Lock()
|
||||
defer st.int64sMtx.Unlock()
|
||||
st.int64s[key] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *MemStore) GetFloat(key string) (float64, bool) {
|
||||
st.floatsMtx.Lock()
|
||||
defer st.floatsMtx.Unlock()
|
||||
val, ok := st.floats[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func (st *MemStore) SetFloat(key string, val float64) error {
|
||||
st.floatsMtx.Lock()
|
||||
defer st.floatsMtx.Unlock()
|
||||
st.floats[key] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *MemStore) GetString(key string) (string, bool) {
|
||||
st.stringsMtx.Lock()
|
||||
defer st.stringsMtx.Unlock()
|
||||
val, ok := st.strings[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func (st *MemStore) SetString(key string, val string) error {
|
||||
st.stringsMtx.Lock()
|
||||
defer st.stringsMtx.Unlock()
|
||||
st.strings[key] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *MemStore) DelBool(key string) error {
|
||||
st.boolsMtx.Lock()
|
||||
defer st.boolsMtx.Unlock()
|
||||
delete(st.bools, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *MemStore) DelInt(key string) error {
|
||||
st.intsMtx.Lock()
|
||||
defer st.intsMtx.Unlock()
|
||||
delete(st.ints, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *MemStore) DelInt64(key string) error {
|
||||
st.int64sMtx.Lock()
|
||||
defer st.int64sMtx.Unlock()
|
||||
delete(st.int64s, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *MemStore) DelFloat(key string) error {
|
||||
st.floatsMtx.Lock()
|
||||
defer st.floatsMtx.Unlock()
|
||||
delete(st.floats, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *MemStore) DelString(key string) error {
|
||||
st.stringsMtx.Lock()
|
||||
defer st.stringsMtx.Unlock()
|
||||
delete(st.strings, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *MemStore) TryLock(key string) error {
|
||||
st.stringsMtx.Lock()
|
||||
defer st.stringsMtx.Unlock()
|
||||
_, ok := st.locks[key]
|
||||
if ok {
|
||||
return kvstore.ErrLocked
|
||||
}
|
||||
st.locks[key] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *MemStore) Unlock(key string) error {
|
||||
st.stringsMtx.Lock()
|
||||
defer st.stringsMtx.Unlock()
|
||||
_, ok := st.locks[key]
|
||||
if !ok {
|
||||
return kvstore.ErrNoLock
|
||||
}
|
||||
delete(st.locks, key)
|
||||
return nil
|
||||
}
|
185
src/kvstore/test/provider_test.go
Normal file
185
src/kvstore/test/provider_test.go
Normal file
|
@ -0,0 +1,185 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ihexxa/quickshare/src/kvstore"
|
||||
"github.com/ihexxa/quickshare/src/kvstore/boltdbpvd"
|
||||
"github.com/ihexxa/quickshare/src/kvstore/memstore"
|
||||
)
|
||||
|
||||
func TestKVStoreProviders(t *testing.T) {
|
||||
var err error
|
||||
var ok bool
|
||||
key, boolV, intV, int64V, floatV, stringV := "key", true, 2027, int64(2027), 3.1415, "foobar"
|
||||
|
||||
kvstoreTest := func(store kvstore.IKVStore, t *testing.T) {
|
||||
// test bools
|
||||
_, ok = store.GetBool(key)
|
||||
if ok {
|
||||
t.Error("value should not exist")
|
||||
}
|
||||
err = store.SetBool(key, boolV)
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
boolVGot, ok := store.GetBool(key)
|
||||
if !ok {
|
||||
t.Error("value should exit")
|
||||
} else if boolVGot != boolV {
|
||||
t.Error(fmt.Sprintln("value not equal", boolVGot, boolV))
|
||||
}
|
||||
err = store.DelBool(key)
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
_, ok = store.GetBool(key)
|
||||
if ok {
|
||||
t.Error("value should not exist")
|
||||
}
|
||||
|
||||
// test ints
|
||||
_, ok = store.GetInt(key)
|
||||
if ok {
|
||||
t.Error("value should not exist")
|
||||
}
|
||||
err = store.SetInt(key, intV)
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
intVGot, ok := store.GetInt(key)
|
||||
if !ok {
|
||||
t.Error("value should exit")
|
||||
} else if intVGot != intV {
|
||||
t.Error(fmt.Sprintln("value not equal", intVGot, intV))
|
||||
}
|
||||
err = store.DelInt(key)
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
_, ok = store.GetInt(key)
|
||||
if ok {
|
||||
t.Error("value should not exist")
|
||||
}
|
||||
|
||||
// test int64s
|
||||
_, ok = store.GetInt64(key)
|
||||
if ok {
|
||||
t.Error("value should not exist")
|
||||
}
|
||||
err = store.SetInt64(key, int64V)
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
int64VGot, ok := store.GetInt64(key)
|
||||
if !ok {
|
||||
t.Error("value should exit")
|
||||
} else if int64VGot != int64V {
|
||||
t.Error(fmt.Sprintln("value not equal", int64VGot, int64V))
|
||||
}
|
||||
err = store.DelInt64(key)
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
_, ok = store.GetInt64(key)
|
||||
if ok {
|
||||
t.Error("value should not exist")
|
||||
}
|
||||
|
||||
// test floats
|
||||
_, ok = store.GetFloat(key)
|
||||
if ok {
|
||||
t.Error("value should not exist")
|
||||
}
|
||||
err = store.SetFloat(key, floatV)
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
floatVGot, ok := store.GetFloat(key)
|
||||
if !ok {
|
||||
t.Error("value should exit")
|
||||
} else if floatVGot != floatV {
|
||||
t.Error(fmt.Sprintln("value not equal", floatVGot, floatV))
|
||||
}
|
||||
err = store.DelFloat(key)
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
_, ok = store.GetFloat(key)
|
||||
if ok {
|
||||
t.Error("value should not exist")
|
||||
}
|
||||
|
||||
// test strings
|
||||
_, ok = store.GetString(key)
|
||||
if ok {
|
||||
t.Error("value should not exist")
|
||||
}
|
||||
err = store.SetString(key, stringV)
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
stringVGot, ok := store.GetString(key)
|
||||
if !ok {
|
||||
t.Error("value should exit")
|
||||
} else if stringVGot != stringV {
|
||||
t.Error(fmt.Sprintln("value not equal", stringVGot, stringV))
|
||||
}
|
||||
err = store.DelString(key)
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
_, ok = store.GetString(key)
|
||||
if ok {
|
||||
t.Error("value should not exist")
|
||||
}
|
||||
|
||||
// test locks
|
||||
err = store.TryLock(key)
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
err = store.TryLock(key)
|
||||
if err == nil || err != kvstore.ErrLocked {
|
||||
t.Error("there should be locked")
|
||||
}
|
||||
err = store.TryLock("key2")
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
err = store.Unlock(key)
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
err = store.Unlock("key2")
|
||||
if err != nil {
|
||||
t.Errorf("there should be no error %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("test bolt provider", func(t *testing.T) {
|
||||
rootPath, err := ioutil.TempDir("./", "quickshare_kvstore_test_")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(rootPath)
|
||||
|
||||
store := boltdbpvd.New(rootPath, 1024)
|
||||
defer store.Close()
|
||||
kvstoreTest(store, t)
|
||||
})
|
||||
|
||||
t.Run("test in-memory provider", func(t *testing.T) {
|
||||
rootPath, err := ioutil.TempDir("./", "quickshare_kvstore_test_")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(rootPath)
|
||||
|
||||
store := memstore.New()
|
||||
kvstoreTest(store, t)
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue