mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-27 19:51:41 +00:00
Jae/fixprefixdb (#199)
* Fix PrefixDB Iterator * PrefixDB Iterator/ReverseIterator fixes * Bump version 0.8.2 * Update CHANGELOG.md about DebugDB * Keep invalid source to be closed * Use prefixBatch instead of memBatch
This commit is contained in:
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,5 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## 0.8.2 (April 12th, 2018)
|
||||
|
||||
FEATURES:
|
||||
|
||||
- [db] DebugDB shows better colorized output
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
- [db] PrefixDB Iterator/ReverseIterator fixes
|
||||
- [db] DebugDB fixes
|
||||
|
||||
## 0.8.1 (April 5th, 2018)
|
||||
|
||||
FEATURES:
|
||||
|
@ -178,7 +178,7 @@ func (err *cmnError) Format(s fmt.State, verb rune) {
|
||||
if s.Flag('#') {
|
||||
s.Write([]byte("--= Error =--\n"))
|
||||
// Write msg.
|
||||
s.Write([]byte(fmt.Sprintf("Message: %#s\n", err.msg)))
|
||||
s.Write([]byte(fmt.Sprintf("Message: %s\n", err.msg)))
|
||||
// Write cause.
|
||||
s.Write([]byte(fmt.Sprintf("Cause: %#v\n", err.cause)))
|
||||
// Write type.
|
||||
|
@ -23,11 +23,11 @@ func TestWriteFileAtomic(t *testing.T) {
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
if err := ioutil.WriteFile(f.Name(), old, 0664); err != nil {
|
||||
if err = ioutil.WriteFile(f.Name(), old, 0664); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := WriteFileAtomic(f.Name(), data, perm); err != nil {
|
||||
if err = WriteFileAtomic(f.Name(), data, perm); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,12 @@ func checkNextPanics(t *testing.T, itr Iterator) {
|
||||
assert.Panics(t, func() { itr.Next() }, "checkNextPanics expected panic but didn't")
|
||||
}
|
||||
|
||||
func checkDomain(t *testing.T, itr Iterator, start, end []byte) {
|
||||
ds, de := itr.Domain()
|
||||
assert.Equal(t, start, ds, "checkDomain domain start incorrect")
|
||||
assert.Equal(t, end, de, "checkDomain domain end incorrect")
|
||||
}
|
||||
|
||||
func checkItem(t *testing.T, itr Iterator, key []byte, value []byte) {
|
||||
k, v := itr.Key(), itr.Value()
|
||||
assert.Exactly(t, key, k)
|
||||
|
@ -3,8 +3,14 @@ package db
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
||||
func _fmt(f string, az ...interface{}) string {
|
||||
return fmt.Sprintf(f, az...)
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// debugDB
|
||||
|
||||
@ -26,78 +32,84 @@ func (ddb debugDB) Mutex() *sync.Mutex { return nil }
|
||||
|
||||
// Implements DB.
|
||||
func (ddb debugDB) Get(key []byte) (value []byte) {
|
||||
defer fmt.Printf("%v.Get(%X) %X\n", ddb.label, key, value)
|
||||
defer func() {
|
||||
fmt.Printf("%v.Get(%v) %v\n", ddb.label, cmn.Cyan(_fmt("%X", key)), cmn.Blue(_fmt("%X", value)))
|
||||
}()
|
||||
value = ddb.db.Get(key)
|
||||
return
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
func (ddb debugDB) Has(key []byte) (has bool) {
|
||||
defer fmt.Printf("%v.Has(%X) %v\n", ddb.label, key, has)
|
||||
defer func() {
|
||||
fmt.Printf("%v.Has(%v) %v\n", ddb.label, cmn.Cyan(_fmt("%X", key)), has)
|
||||
}()
|
||||
return ddb.db.Has(key)
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
func (ddb debugDB) Set(key []byte, value []byte) {
|
||||
fmt.Printf("%v.Set(%X, %X)\n", ddb.label, key, value)
|
||||
fmt.Printf("%v.Set(%v, %v)\n", ddb.label, cmn.Cyan(_fmt("%X", key)), cmn.Yellow(_fmt("%X", value)))
|
||||
ddb.db.Set(key, value)
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
func (ddb debugDB) SetSync(key []byte, value []byte) {
|
||||
fmt.Printf("%v.SetSync(%X, %X)\n", ddb.label, key, value)
|
||||
fmt.Printf("%v.SetSync(%v, %v)\n", ddb.label, cmn.Cyan(_fmt("%X", key)), cmn.Yellow(_fmt("%X", value)))
|
||||
ddb.db.SetSync(key, value)
|
||||
}
|
||||
|
||||
// Implements atomicSetDeleter.
|
||||
func (ddb debugDB) SetNoLock(key []byte, value []byte) {
|
||||
fmt.Printf("%v.SetNoLock(%X, %X)\n", ddb.label, key, value)
|
||||
ddb.db.Set(key, value)
|
||||
fmt.Printf("%v.SetNoLock(%v, %v)\n", ddb.label, cmn.Cyan(_fmt("%X", key)), cmn.Yellow(_fmt("%X", value)))
|
||||
ddb.db.(atomicSetDeleter).SetNoLock(key, value)
|
||||
}
|
||||
|
||||
// Implements atomicSetDeleter.
|
||||
func (ddb debugDB) SetNoLockSync(key []byte, value []byte) {
|
||||
fmt.Printf("%v.SetNoLockSync(%X, %X)\n", ddb.label, key, value)
|
||||
ddb.db.SetSync(key, value)
|
||||
fmt.Printf("%v.SetNoLockSync(%v, %v)\n", ddb.label, cmn.Cyan(_fmt("%X", key)), cmn.Yellow(_fmt("%X", value)))
|
||||
ddb.db.(atomicSetDeleter).SetNoLockSync(key, value)
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
func (ddb debugDB) Delete(key []byte) {
|
||||
fmt.Printf("%v.Delete(%X)\n", ddb.label, key)
|
||||
fmt.Printf("%v.Delete(%v)\n", ddb.label, cmn.Red(_fmt("%X", key)))
|
||||
ddb.db.Delete(key)
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
func (ddb debugDB) DeleteSync(key []byte) {
|
||||
fmt.Printf("%v.DeleteSync(%X)\n", ddb.label, key)
|
||||
fmt.Printf("%v.DeleteSync(%v)\n", ddb.label, cmn.Red(_fmt("%X", key)))
|
||||
ddb.db.DeleteSync(key)
|
||||
}
|
||||
|
||||
// Implements atomicSetDeleter.
|
||||
func (ddb debugDB) DeleteNoLock(key []byte) {
|
||||
fmt.Printf("%v.DeleteNoLock(%X)\n", ddb.label, key)
|
||||
ddb.db.Delete(key)
|
||||
fmt.Printf("%v.DeleteNoLock(%v)\n", ddb.label, cmn.Red(_fmt("%X", key)))
|
||||
ddb.db.(atomicSetDeleter).DeleteNoLock(key)
|
||||
}
|
||||
|
||||
// Implements atomicSetDeleter.
|
||||
func (ddb debugDB) DeleteNoLockSync(key []byte) {
|
||||
fmt.Printf("%v.DeleteNoLockSync(%X)\n", ddb.label, key)
|
||||
ddb.db.DeleteSync(key)
|
||||
fmt.Printf("%v.DeleteNoLockSync(%v)\n", ddb.label, cmn.Red(_fmt("%X", key)))
|
||||
ddb.db.(atomicSetDeleter).DeleteNoLockSync(key)
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
func (ddb debugDB) Iterator(start, end []byte) Iterator {
|
||||
fmt.Printf("%v.Iterator(%X, %X)\n", ddb.label, start, end)
|
||||
fmt.Printf("%v.Iterator(%v, %v)\n", ddb.label, cmn.Cyan(_fmt("%X", start)), cmn.Blue(_fmt("%X", end)))
|
||||
return NewDebugIterator(ddb.label, ddb.db.Iterator(start, end))
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
func (ddb debugDB) ReverseIterator(start, end []byte) Iterator {
|
||||
fmt.Printf("%v.ReverseIterator(%X, %X)\n", ddb.label, start, end)
|
||||
fmt.Printf("%v.ReverseIterator(%v, %v)\n", ddb.label, cmn.Cyan(_fmt("%X", start)), cmn.Blue(_fmt("%X", end)))
|
||||
return NewDebugIterator(ddb.label, ddb.db.ReverseIterator(start, end))
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
// Panics if the underlying db is not an
|
||||
// atomicSetDeleter.
|
||||
func (ddb debugDB) NewBatch() Batch {
|
||||
fmt.Printf("%v.NewBatch()\n", ddb.label)
|
||||
return NewDebugBatch(ddb.label, ddb.db.NewBatch())
|
||||
@ -137,14 +149,18 @@ func NewDebugIterator(label string, itr Iterator) debugIterator {
|
||||
|
||||
// Implements Iterator.
|
||||
func (ditr debugIterator) Domain() (start []byte, end []byte) {
|
||||
defer fmt.Printf("%v.itr.Domain() (%X,%X)\n", ditr.label, start, end)
|
||||
defer func() {
|
||||
fmt.Printf("%v.itr.Domain() (%X,%X)\n", ditr.label, start, end)
|
||||
}()
|
||||
start, end = ditr.itr.Domain()
|
||||
return
|
||||
}
|
||||
|
||||
// Implements Iterator.
|
||||
func (ditr debugIterator) Valid() (ok bool) {
|
||||
defer fmt.Printf("%v.itr.Valid() %v\n", ditr.label, ok)
|
||||
defer func() {
|
||||
fmt.Printf("%v.itr.Valid() %v\n", ditr.label, ok)
|
||||
}()
|
||||
ok = ditr.itr.Valid()
|
||||
return
|
||||
}
|
||||
@ -157,14 +173,14 @@ func (ditr debugIterator) Next() {
|
||||
|
||||
// Implements Iterator.
|
||||
func (ditr debugIterator) Key() (key []byte) {
|
||||
fmt.Printf("%v.itr.Key() %X\n", ditr.label, key)
|
||||
fmt.Printf("%v.itr.Key() %v\n", ditr.label, cmn.Cyan(_fmt("%X", key)))
|
||||
key = ditr.itr.Key()
|
||||
return
|
||||
}
|
||||
|
||||
// Implements Iterator.
|
||||
func (ditr debugIterator) Value() (value []byte) {
|
||||
fmt.Printf("%v.itr.Value() %X\n", ditr.label, value)
|
||||
fmt.Printf("%v.itr.Value() %v\n", ditr.label, cmn.Blue(_fmt("%X", value)))
|
||||
value = ditr.itr.Value()
|
||||
return
|
||||
}
|
||||
@ -193,13 +209,13 @@ func NewDebugBatch(label string, bch Batch) debugBatch {
|
||||
|
||||
// Implements Batch.
|
||||
func (dbch debugBatch) Set(key, value []byte) {
|
||||
fmt.Printf("%v.batch.Set(%X, %X)\n", dbch.label, key, value)
|
||||
fmt.Printf("%v.batch.Set(%v, %v)\n", dbch.label, cmn.Cyan(_fmt("%X", key)), cmn.Yellow(_fmt("%X", value)))
|
||||
dbch.bch.Set(key, value)
|
||||
}
|
||||
|
||||
// Implements Batch.
|
||||
func (dbch debugBatch) Delete(key []byte) {
|
||||
fmt.Printf("%v.batch.Delete(%X)\n", dbch.label, key)
|
||||
fmt.Printf("%v.batch.Delete(%v)\n", dbch.label, cmn.Red(_fmt("%X", key)))
|
||||
dbch.bch.Delete(key)
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
package db
|
||||
|
||||
import "sync"
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type atomicSetDeleter interface {
|
||||
Mutex() *sync.Mutex
|
||||
@ -66,6 +68,5 @@ func (mBatch *memBatch) write(doSync bool) {
|
||||
case opTypeDelete:
|
||||
mBatch.db.DeleteNoLock(op.key)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
10
db/mem_db.go
10
db/mem_db.go
@ -37,7 +37,8 @@ func (db *MemDB) Get(key []byte) []byte {
|
||||
defer db.mtx.Unlock()
|
||||
key = nonNilBytes(key)
|
||||
|
||||
return db.db[string(key)]
|
||||
value := db.db[string(key)]
|
||||
return value
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
@ -162,7 +163,7 @@ func (db *MemDB) ReverseIterator(start, end []byte) Iterator {
|
||||
db.mtx.Lock()
|
||||
defer db.mtx.Unlock()
|
||||
|
||||
keys := db.getSortedKeys(end, start, true)
|
||||
keys := db.getSortedKeys(start, end, true)
|
||||
return newMemDBIterator(db, keys, start, end)
|
||||
}
|
||||
|
||||
@ -236,7 +237,8 @@ func (itr *memDBIterator) assertIsValid() {
|
||||
func (db *MemDB) getSortedKeys(start, end []byte, reverse bool) []string {
|
||||
keys := []string{}
|
||||
for key := range db.db {
|
||||
if IsKeyInDomain([]byte(key), start, end, false) {
|
||||
inDomain := IsKeyInDomain([]byte(key), start, end, reverse)
|
||||
if inDomain {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
@ -244,7 +246,9 @@ func (db *MemDB) getSortedKeys(start, end []byte, reverse bool) []string {
|
||||
if reverse {
|
||||
nkeys := len(keys)
|
||||
for i := 0; i < nkeys/2; i++ {
|
||||
temp := keys[i]
|
||||
keys[i] = keys[nkeys-i-1]
|
||||
keys[nkeys-i-1] = temp
|
||||
}
|
||||
}
|
||||
return keys
|
||||
|
214
db/prefix_db.go
214
db/prefix_db.go
@ -24,7 +24,8 @@ func IteratePrefix(db DB, prefix []byte) Iterator {
|
||||
TODO: Make test, maybe rename.
|
||||
// Like IteratePrefix but the iterator strips the prefix from the keys.
|
||||
func IteratePrefixStripped(db DB, prefix []byte) Iterator {
|
||||
return newUnprefixIterator(prefix, IteratePrefix(db, prefix))
|
||||
start, end := ...
|
||||
return newPrefixIterator(prefix, start, end, IteratePrefix(db, prefix))
|
||||
}
|
||||
*/
|
||||
|
||||
@ -55,7 +56,9 @@ func (pdb *prefixDB) Get(key []byte) []byte {
|
||||
pdb.mtx.Lock()
|
||||
defer pdb.mtx.Unlock()
|
||||
|
||||
return pdb.db.Get(pdb.prefixed(key))
|
||||
pkey := pdb.prefixed(key)
|
||||
value := pdb.db.Get(pkey)
|
||||
return value
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
@ -71,7 +74,8 @@ func (pdb *prefixDB) Set(key []byte, value []byte) {
|
||||
pdb.mtx.Lock()
|
||||
defer pdb.mtx.Unlock()
|
||||
|
||||
pdb.db.Set(pdb.prefixed(key), value)
|
||||
pkey := pdb.prefixed(key)
|
||||
pdb.db.Set(pkey, value)
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
@ -82,16 +86,6 @@ func (pdb *prefixDB) SetSync(key []byte, value []byte) {
|
||||
pdb.db.SetSync(pdb.prefixed(key), value)
|
||||
}
|
||||
|
||||
// Implements atomicSetDeleter.
|
||||
func (pdb *prefixDB) SetNoLock(key []byte, value []byte) {
|
||||
pdb.db.Set(pdb.prefixed(key), value)
|
||||
}
|
||||
|
||||
// Implements atomicSetDeleter.
|
||||
func (pdb *prefixDB) SetNoLockSync(key []byte, value []byte) {
|
||||
pdb.db.SetSync(pdb.prefixed(key), value)
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
func (pdb *prefixDB) Delete(key []byte) {
|
||||
pdb.mtx.Lock()
|
||||
@ -108,28 +102,22 @@ func (pdb *prefixDB) DeleteSync(key []byte) {
|
||||
pdb.db.DeleteSync(pdb.prefixed(key))
|
||||
}
|
||||
|
||||
// Implements atomicSetDeleter.
|
||||
func (pdb *prefixDB) DeleteNoLock(key []byte) {
|
||||
pdb.db.Delete(pdb.prefixed(key))
|
||||
}
|
||||
|
||||
// Implements atomicSetDeleter.
|
||||
func (pdb *prefixDB) DeleteNoLockSync(key []byte) {
|
||||
pdb.db.DeleteSync(pdb.prefixed(key))
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
func (pdb *prefixDB) Iterator(start, end []byte) Iterator {
|
||||
pdb.mtx.Lock()
|
||||
defer pdb.mtx.Unlock()
|
||||
|
||||
pstart := append(pdb.prefix, start...)
|
||||
pend := []byte(nil)
|
||||
if end != nil {
|
||||
pend = append(pdb.prefix, end...)
|
||||
var pstart, pend []byte
|
||||
pstart = append(cp(pdb.prefix), start...)
|
||||
if end == nil {
|
||||
pend = cpIncr(pdb.prefix)
|
||||
} else {
|
||||
pend = append(cp(pdb.prefix), end...)
|
||||
}
|
||||
return newUnprefixIterator(
|
||||
return newPrefixIterator(
|
||||
pdb.prefix,
|
||||
start,
|
||||
end,
|
||||
pdb.db.Iterator(
|
||||
pstart,
|
||||
pend,
|
||||
@ -142,31 +130,68 @@ func (pdb *prefixDB) ReverseIterator(start, end []byte) Iterator {
|
||||
pdb.mtx.Lock()
|
||||
defer pdb.mtx.Unlock()
|
||||
|
||||
pstart := []byte(nil)
|
||||
if start != nil {
|
||||
pstart = append(pdb.prefix, start...)
|
||||
var pstart, pend []byte
|
||||
if start == nil {
|
||||
// This may cause the underlying iterator to start with
|
||||
// an item which doesn't start with prefix. We will skip
|
||||
// that item later in this function. See 'skipOne'.
|
||||
pstart = cpIncr(pdb.prefix)
|
||||
} else {
|
||||
pstart = append(cp(pdb.prefix), start...)
|
||||
}
|
||||
pend := []byte(nil)
|
||||
if end != nil {
|
||||
pend = append(pdb.prefix, end...)
|
||||
if end == nil {
|
||||
// This may cause the underlying iterator to end with an
|
||||
// item which doesn't start with prefix. The
|
||||
// prefixIterator will terminate iteration
|
||||
// automatically upon detecting this.
|
||||
pend = cpDecr(pdb.prefix)
|
||||
} else {
|
||||
pend = append(cp(pdb.prefix), end...)
|
||||
}
|
||||
return newUnprefixIterator(
|
||||
ritr := pdb.db.ReverseIterator(pstart, pend)
|
||||
if start == nil {
|
||||
skipOne(ritr, cpIncr(pdb.prefix))
|
||||
}
|
||||
return newPrefixIterator(
|
||||
pdb.prefix,
|
||||
pdb.db.ReverseIterator(
|
||||
pstart,
|
||||
pend,
|
||||
),
|
||||
start,
|
||||
end,
|
||||
ritr,
|
||||
)
|
||||
}
|
||||
|
||||
// Implements DB.
|
||||
// Panics if the underlying DB is not an
|
||||
// atomicSetDeleter.
|
||||
func (pdb *prefixDB) NewBatch() Batch {
|
||||
pdb.mtx.Lock()
|
||||
defer pdb.mtx.Unlock()
|
||||
|
||||
return &memBatch{pdb, nil}
|
||||
return newPrefixBatch(pdb.prefix, pdb.db.NewBatch())
|
||||
}
|
||||
|
||||
/* NOTE: Uncomment to use memBatch instead of prefixBatch
|
||||
// Implements atomicSetDeleter.
|
||||
func (pdb *prefixDB) SetNoLock(key []byte, value []byte) {
|
||||
pdb.db.(atomicSetDeleter).SetNoLock(pdb.prefixed(key), value)
|
||||
}
|
||||
|
||||
// Implements atomicSetDeleter.
|
||||
func (pdb *prefixDB) SetNoLockSync(key []byte, value []byte) {
|
||||
pdb.db.(atomicSetDeleter).SetNoLockSync(pdb.prefixed(key), value)
|
||||
}
|
||||
|
||||
// Implements atomicSetDeleter.
|
||||
func (pdb *prefixDB) DeleteNoLock(key []byte) {
|
||||
pdb.db.(atomicSetDeleter).DeleteNoLock(pdb.prefixed(key))
|
||||
}
|
||||
|
||||
// Implements atomicSetDeleter.
|
||||
func (pdb *prefixDB) DeleteNoLockSync(key []byte) {
|
||||
pdb.db.(atomicSetDeleter).DeleteNoLockSync(pdb.prefixed(key))
|
||||
}
|
||||
*/
|
||||
|
||||
// Implements DB.
|
||||
func (pdb *prefixDB) Close() {
|
||||
pdb.mtx.Lock()
|
||||
@ -201,52 +226,109 @@ func (pdb *prefixDB) Stats() map[string]string {
|
||||
}
|
||||
|
||||
func (pdb *prefixDB) prefixed(key []byte) []byte {
|
||||
return append(pdb.prefix, key...)
|
||||
return append(cp(pdb.prefix), key...)
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// prefixBatch
|
||||
|
||||
// Strips prefix while iterating from Iterator.
|
||||
type unprefixIterator struct {
|
||||
type prefixBatch struct {
|
||||
prefix []byte
|
||||
source Iterator
|
||||
source Batch
|
||||
}
|
||||
|
||||
func newUnprefixIterator(prefix []byte, source Iterator) unprefixIterator {
|
||||
return unprefixIterator{
|
||||
func newPrefixBatch(prefix []byte, source Batch) prefixBatch {
|
||||
return prefixBatch{
|
||||
prefix: prefix,
|
||||
source: source,
|
||||
}
|
||||
}
|
||||
|
||||
func (itr unprefixIterator) Domain() (start []byte, end []byte) {
|
||||
start, end = itr.source.Domain()
|
||||
if len(start) > 0 {
|
||||
start = stripPrefix(start, itr.prefix)
|
||||
func (pb prefixBatch) Set(key, value []byte) {
|
||||
pkey := append(cp(pb.prefix), key...)
|
||||
pb.source.Set(pkey, value)
|
||||
}
|
||||
if len(end) > 0 {
|
||||
end = stripPrefix(end, itr.prefix)
|
||||
|
||||
func (pb prefixBatch) Delete(key []byte) {
|
||||
pkey := append(cp(pb.prefix), key...)
|
||||
pb.source.Delete(pkey)
|
||||
}
|
||||
|
||||
func (pb prefixBatch) Write() {
|
||||
pb.source.Write()
|
||||
}
|
||||
|
||||
func (pb prefixBatch) WriteSync() {
|
||||
pb.source.WriteSync()
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// prefixIterator
|
||||
|
||||
// Strips prefix while iterating from Iterator.
|
||||
type prefixIterator struct {
|
||||
prefix []byte
|
||||
start []byte
|
||||
end []byte
|
||||
source Iterator
|
||||
valid bool
|
||||
}
|
||||
|
||||
func newPrefixIterator(prefix, start, end []byte, source Iterator) prefixIterator {
|
||||
if !source.Valid() || !bytes.HasPrefix(source.Key(), prefix) {
|
||||
return prefixIterator{
|
||||
prefix: prefix,
|
||||
start: start,
|
||||
end: end,
|
||||
source: source,
|
||||
valid: false,
|
||||
}
|
||||
} else {
|
||||
return prefixIterator{
|
||||
prefix: prefix,
|
||||
start: start,
|
||||
end: end,
|
||||
source: source,
|
||||
valid: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (itr prefixIterator) Domain() (start []byte, end []byte) {
|
||||
return itr.start, itr.end
|
||||
}
|
||||
|
||||
func (itr prefixIterator) Valid() bool {
|
||||
return itr.valid && itr.source.Valid()
|
||||
}
|
||||
|
||||
func (itr prefixIterator) Next() {
|
||||
if !itr.valid {
|
||||
panic("prefixIterator invalid, cannot call Next()")
|
||||
}
|
||||
itr.source.Next()
|
||||
if !itr.source.Valid() || !bytes.HasPrefix(itr.source.Key(), itr.prefix) {
|
||||
itr.source.Close()
|
||||
itr.valid = false
|
||||
return
|
||||
}
|
||||
|
||||
func (itr unprefixIterator) Valid() bool {
|
||||
return itr.source.Valid()
|
||||
}
|
||||
|
||||
func (itr unprefixIterator) Next() {
|
||||
itr.source.Next()
|
||||
func (itr prefixIterator) Key() (key []byte) {
|
||||
if !itr.valid {
|
||||
panic("prefixIterator invalid, cannot call Key()")
|
||||
}
|
||||
|
||||
func (itr unprefixIterator) Key() (key []byte) {
|
||||
return stripPrefix(itr.source.Key(), itr.prefix)
|
||||
}
|
||||
|
||||
func (itr unprefixIterator) Value() (value []byte) {
|
||||
func (itr prefixIterator) Value() (value []byte) {
|
||||
if !itr.valid {
|
||||
panic("prefixIterator invalid, cannot call Value()")
|
||||
}
|
||||
return itr.source.Value()
|
||||
}
|
||||
|
||||
func (itr unprefixIterator) Close() {
|
||||
func (itr prefixIterator) Close() {
|
||||
itr.source.Close()
|
||||
}
|
||||
|
||||
@ -261,3 +343,13 @@ func stripPrefix(key []byte, prefix []byte) (stripped []byte) {
|
||||
}
|
||||
return key[len(prefix):]
|
||||
}
|
||||
|
||||
// If the first iterator item is skipKey, then
|
||||
// skip it.
|
||||
func skipOne(itr Iterator, skipKey []byte) {
|
||||
if itr.Valid() {
|
||||
if bytes.Equal(itr.Key(), skipKey) {
|
||||
itr.Next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package db
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestIteratePrefix(t *testing.T) {
|
||||
func mockDBWithStuff() DB {
|
||||
db := NewMemDB()
|
||||
// Under "key" prefix
|
||||
db.Set(bz("key"), bz("value"))
|
||||
@ -14,10 +14,13 @@ func TestIteratePrefix(t *testing.T) {
|
||||
db.Set(bz("k"), bz("val"))
|
||||
db.Set(bz("ke"), bz("valu"))
|
||||
db.Set(bz("kee"), bz("valuu"))
|
||||
xitr := db.Iterator(nil, nil)
|
||||
xitr.Key()
|
||||
return db
|
||||
}
|
||||
|
||||
func TestPrefixDBSimple(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
checkValue(t, pdb, bz("key"), nil)
|
||||
checkValue(t, pdb, bz(""), bz("value"))
|
||||
checkValue(t, pdb, bz("key1"), nil)
|
||||
@ -30,9 +33,14 @@ func TestIteratePrefix(t *testing.T) {
|
||||
checkValue(t, pdb, bz("k"), nil)
|
||||
checkValue(t, pdb, bz("ke"), nil)
|
||||
checkValue(t, pdb, bz("kee"), nil)
|
||||
}
|
||||
|
||||
func TestPrefixDBIterator1(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.Iterator(nil, nil)
|
||||
itr.Key()
|
||||
checkDomain(t, itr, nil, nil)
|
||||
checkItem(t, itr, bz(""), bz("value"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz("1"), bz("value1"))
|
||||
@ -40,5 +48,100 @@ func TestIteratePrefix(t *testing.T) {
|
||||
checkItem(t, itr, bz("2"), bz("value2"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz("3"), bz("value3"))
|
||||
checkNext(t, itr, false)
|
||||
checkInvalid(t, itr)
|
||||
itr.Close()
|
||||
}
|
||||
|
||||
func TestPrefixDBIterator2(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.Iterator(nil, bz(""))
|
||||
checkDomain(t, itr, nil, bz(""))
|
||||
checkInvalid(t, itr)
|
||||
itr.Close()
|
||||
}
|
||||
|
||||
func TestPrefixDBIterator3(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.Iterator(bz(""), nil)
|
||||
checkDomain(t, itr, bz(""), nil)
|
||||
checkItem(t, itr, bz(""), bz("value"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz("1"), bz("value1"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz("2"), bz("value2"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz("3"), bz("value3"))
|
||||
checkNext(t, itr, false)
|
||||
checkInvalid(t, itr)
|
||||
itr.Close()
|
||||
}
|
||||
|
||||
func TestPrefixDBIterator4(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.Iterator(bz(""), bz(""))
|
||||
checkDomain(t, itr, bz(""), bz(""))
|
||||
checkInvalid(t, itr)
|
||||
itr.Close()
|
||||
}
|
||||
|
||||
func TestPrefixDBReverseIterator1(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.ReverseIterator(nil, nil)
|
||||
checkDomain(t, itr, nil, nil)
|
||||
checkItem(t, itr, bz("3"), bz("value3"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz("2"), bz("value2"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz("1"), bz("value1"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz(""), bz("value"))
|
||||
checkNext(t, itr, false)
|
||||
checkInvalid(t, itr)
|
||||
itr.Close()
|
||||
}
|
||||
|
||||
func TestPrefixDBReverseIterator2(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.ReverseIterator(nil, bz(""))
|
||||
checkDomain(t, itr, nil, bz(""))
|
||||
checkItem(t, itr, bz("3"), bz("value3"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz("2"), bz("value2"))
|
||||
checkNext(t, itr, true)
|
||||
checkItem(t, itr, bz("1"), bz("value1"))
|
||||
checkNext(t, itr, false)
|
||||
checkInvalid(t, itr)
|
||||
itr.Close()
|
||||
}
|
||||
|
||||
func TestPrefixDBReverseIterator3(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.ReverseIterator(bz(""), nil)
|
||||
checkDomain(t, itr, bz(""), nil)
|
||||
checkItem(t, itr, bz(""), bz("value"))
|
||||
checkNext(t, itr, false)
|
||||
checkInvalid(t, itr)
|
||||
itr.Close()
|
||||
}
|
||||
|
||||
func TestPrefixDBReverseIterator4(t *testing.T) {
|
||||
db := mockDBWithStuff()
|
||||
pdb := NewPrefixDB(db, bz("key"))
|
||||
|
||||
itr := pdb.ReverseIterator(bz(""), bz(""))
|
||||
checkInvalid(t, itr)
|
||||
itr.Close()
|
||||
}
|
||||
|
26
db/util.go
26
db/util.go
@ -33,6 +33,29 @@ func cpIncr(bz []byte) (ret []byte) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns a slice of the same length (big endian)
|
||||
// except decremented by one.
|
||||
// Returns nil on underflow (e.g. if bz bytes are all 0x00)
|
||||
// CONTRACT: len(bz) > 0
|
||||
func cpDecr(bz []byte) (ret []byte) {
|
||||
if len(bz) == 0 {
|
||||
panic("cpDecr expects non-zero bz length")
|
||||
}
|
||||
ret = cp(bz)
|
||||
for i := len(bz) - 1; i >= 0; i-- {
|
||||
if ret[i] > byte(0x00) {
|
||||
ret[i]--
|
||||
return
|
||||
}
|
||||
ret[i] = byte(0xFF)
|
||||
if i == 0 {
|
||||
// Underflow
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// See DB interface documentation for more information.
|
||||
func IsKeyInDomain(key, start, end []byte, isReverse bool) bool {
|
||||
if !isReverse {
|
||||
@ -43,7 +66,7 @@ func IsKeyInDomain(key, start, end []byte, isReverse bool) bool {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if start != nil && bytes.Compare(start, key) < 0 {
|
||||
return false
|
||||
}
|
||||
@ -52,3 +75,4 @@ func IsKeyInDomain(key, start, end []byte, isReverse bool) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
package version
|
||||
|
||||
const Version = "0.8.1"
|
||||
const Version = "0.8.2"
|
||||
|
Reference in New Issue
Block a user