2015-01-17 19:53:33 +00:00
|
|
|
// Copyright (C) 2014 The Syncthing Authors.
|
|
|
|
//
|
2015-03-07 20:36:35 +00:00
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
2017-02-09 06:52:18 +00:00
|
|
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
2015-01-17 19:53:33 +00:00
|
|
|
|
|
|
|
package db
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/syndtr/goleveldb/leveldb"
|
2015-05-12 15:37:47 +00:00
|
|
|
"github.com/syndtr/goleveldb/leveldb/util"
|
2015-01-17 19:53:33 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// NamespacedKV is a simple key-value store using a specific namespace within
|
|
|
|
// a leveldb.
|
|
|
|
type NamespacedKV struct {
|
2018-10-10 09:34:24 +00:00
|
|
|
db *Lowlevel
|
2015-01-17 19:53:33 +00:00
|
|
|
prefix []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewNamespacedKV returns a new NamespacedKV that lives in the namespace
|
|
|
|
// specified by the prefix.
|
2018-10-10 09:34:24 +00:00
|
|
|
func NewNamespacedKV(db *Lowlevel, prefix string) *NamespacedKV {
|
2015-01-17 19:53:33 +00:00
|
|
|
return &NamespacedKV{
|
|
|
|
db: db,
|
|
|
|
prefix: []byte(prefix),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-12 15:37:47 +00:00
|
|
|
// Reset removes all entries in this namespace.
|
|
|
|
func (n *NamespacedKV) Reset() {
|
|
|
|
it := n.db.NewIterator(util.BytesPrefix(n.prefix), nil)
|
|
|
|
defer it.Release()
|
|
|
|
batch := new(leveldb.Batch)
|
|
|
|
for it.Next() {
|
|
|
|
batch.Delete(it.Key())
|
|
|
|
if batch.Len() > batchFlushSize {
|
|
|
|
if err := n.db.Write(batch, nil); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
batch.Reset()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if batch.Len() > 0 {
|
|
|
|
if err := n.db.Write(batch, nil); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-17 19:53:33 +00:00
|
|
|
// PutInt64 stores a new int64. Any existing value (even if of another type)
|
|
|
|
// is overwritten.
|
|
|
|
func (n *NamespacedKV) PutInt64(key string, val int64) {
|
|
|
|
keyBs := append(n.prefix, []byte(key)...)
|
|
|
|
var valBs [8]byte
|
|
|
|
binary.BigEndian.PutUint64(valBs[:], uint64(val))
|
|
|
|
n.db.Put(keyBs, valBs[:], nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Int64 returns the stored value interpreted as an int64 and a boolean that
|
|
|
|
// is false if no value was stored at the key.
|
|
|
|
func (n *NamespacedKV) Int64(key string) (int64, bool) {
|
|
|
|
keyBs := append(n.prefix, []byte(key)...)
|
|
|
|
valBs, err := n.db.Get(keyBs, nil)
|
|
|
|
if err != nil {
|
|
|
|
return 0, false
|
|
|
|
}
|
|
|
|
val := binary.BigEndian.Uint64(valBs)
|
|
|
|
return int64(val), true
|
|
|
|
}
|
|
|
|
|
|
|
|
// PutTime stores a new time.Time. Any existing value (even if of another
|
|
|
|
// type) is overwritten.
|
|
|
|
func (n *NamespacedKV) PutTime(key string, val time.Time) {
|
|
|
|
keyBs := append(n.prefix, []byte(key)...)
|
|
|
|
valBs, _ := val.MarshalBinary() // never returns an error
|
|
|
|
n.db.Put(keyBs, valBs, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Time returns the stored value interpreted as a time.Time and a boolean
|
|
|
|
// that is false if no value was stored at the key.
|
|
|
|
func (n NamespacedKV) Time(key string) (time.Time, bool) {
|
|
|
|
var t time.Time
|
|
|
|
keyBs := append(n.prefix, []byte(key)...)
|
|
|
|
valBs, err := n.db.Get(keyBs, nil)
|
|
|
|
if err != nil {
|
|
|
|
return t, false
|
|
|
|
}
|
|
|
|
err = t.UnmarshalBinary(valBs)
|
|
|
|
return t, err == nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// PutString stores a new string. Any existing value (even if of another type)
|
|
|
|
// is overwritten.
|
|
|
|
func (n *NamespacedKV) PutString(key, val string) {
|
|
|
|
keyBs := append(n.prefix, []byte(key)...)
|
|
|
|
n.db.Put(keyBs, []byte(val), nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// String returns the stored value interpreted as a string and a boolean that
|
|
|
|
// is false if no value was stored at the key.
|
|
|
|
func (n NamespacedKV) String(key string) (string, bool) {
|
|
|
|
keyBs := append(n.prefix, []byte(key)...)
|
|
|
|
valBs, err := n.db.Get(keyBs, nil)
|
|
|
|
if err != nil {
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
return string(valBs), true
|
|
|
|
}
|
|
|
|
|
2015-05-13 14:57:29 +00:00
|
|
|
// PutBytes stores a new byte slice. Any existing value (even if of another type)
|
|
|
|
// is overwritten.
|
|
|
|
func (n *NamespacedKV) PutBytes(key string, val []byte) {
|
|
|
|
keyBs := append(n.prefix, []byte(key)...)
|
|
|
|
n.db.Put(keyBs, val, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bytes returns the stored value as a raw byte slice and a boolean that
|
|
|
|
// is false if no value was stored at the key.
|
|
|
|
func (n NamespacedKV) Bytes(key string) ([]byte, bool) {
|
|
|
|
keyBs := append(n.prefix, []byte(key)...)
|
|
|
|
valBs, err := n.db.Get(keyBs, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
return valBs, true
|
|
|
|
}
|
|
|
|
|
2015-06-16 11:12:34 +00:00
|
|
|
// PutBool stores a new boolean. Any existing value (even if of another type)
|
|
|
|
// is overwritten.
|
|
|
|
func (n *NamespacedKV) PutBool(key string, val bool) {
|
|
|
|
keyBs := append(n.prefix, []byte(key)...)
|
|
|
|
if val {
|
|
|
|
n.db.Put(keyBs, []byte{0x0}, nil)
|
|
|
|
} else {
|
|
|
|
n.db.Put(keyBs, []byte{0x1}, nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bool returns the stored value as a boolean and a boolean that
|
|
|
|
// is false if no value was stored at the key.
|
|
|
|
func (n NamespacedKV) Bool(key string) (bool, bool) {
|
|
|
|
keyBs := append(n.prefix, []byte(key)...)
|
|
|
|
valBs, err := n.db.Get(keyBs, nil)
|
|
|
|
if err != nil {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
return valBs[0] == 0x0, true
|
|
|
|
}
|
|
|
|
|
2015-01-17 19:53:33 +00:00
|
|
|
// Delete deletes the specified key. It is allowed to delete a nonexistent
|
|
|
|
// key.
|
|
|
|
func (n NamespacedKV) Delete(key string) {
|
|
|
|
keyBs := append(n.prefix, []byte(key)...)
|
|
|
|
n.db.Delete(keyBs, nil)
|
|
|
|
}
|
2018-10-10 09:34:24 +00:00
|
|
|
|
|
|
|
// Well known namespaces that can be instantiated without knowing the key
|
|
|
|
// details.
|
|
|
|
|
|
|
|
// NewDeviceStatisticsNamespace creates a KV namespace for device statistics
|
|
|
|
// for the given device.
|
|
|
|
func NewDeviceStatisticsNamespace(db *Lowlevel, device string) *NamespacedKV {
|
|
|
|
return NewNamespacedKV(db, string(KeyTypeDeviceStatistic)+device)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewFolderStatisticsNamespace creates a KV namespace for folder statistics
|
|
|
|
// for the given folder.
|
|
|
|
func NewFolderStatisticsNamespace(db *Lowlevel, folder string) *NamespacedKV {
|
|
|
|
return NewNamespacedKV(db, string(KeyTypeFolderStatistic)+folder)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewMiscDateNamespace creates a KV namespace for miscellaneous metadata.
|
|
|
|
func NewMiscDataNamespace(db *Lowlevel) *NamespacedKV {
|
|
|
|
return NewNamespacedKV(db, string(KeyTypeMiscData))
|
|
|
|
}
|