diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a55fb554..a9509d5d3 100644 --- a/CHANGELOG.md +++ b/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: diff --git a/common/errors.go b/common/errors.go index 1ee1fb349..5992b2346 100644 --- a/common/errors.go +++ b/common/errors.go @@ -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. diff --git a/common/os_test.go b/common/os_test.go index 97ad672b5..973d68901 100644 --- a/common/os_test.go +++ b/common/os_test.go @@ -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) } diff --git a/db/common_test.go b/db/common_test.go index 1d8d52c5f..6af6e15e6 100644 --- a/db/common_test.go +++ b/db/common_test.go @@ -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) diff --git a/db/debug_db.go b/db/debug_db.go index 7a15bc294..7666ed9fd 100644 --- a/db/debug_db.go +++ b/db/debug_db.go @@ -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) } diff --git a/db/mem_batch.go b/db/mem_batch.go index 81a63d62b..5c5d0c13a 100644 --- a/db/mem_batch.go +++ b/db/mem_batch.go @@ -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) } - } } diff --git a/db/mem_db.go b/db/mem_db.go index 2d802947c..1521f87ac 100644 --- a/db/mem_db.go +++ b/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 diff --git a/db/prefix_db.go b/db/prefix_db.go index 4381ce070..5bb53ebd9 100644 --- a/db/prefix_db.go +++ b/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...) + } + 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...) } - pend := []byte(nil) - if end != nil { - pend = append(pdb.prefix, end...) + ritr := pdb.db.ReverseIterator(pstart, pend) + if start == nil { + skipOne(ritr, cpIncr(pdb.prefix)) } - return newUnprefixIterator( + 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) - } - if len(end) > 0 { - end = stripPrefix(end, itr.prefix) +func (pb prefixBatch) Set(key, value []byte) { + pkey := append(cp(pb.prefix), key...) + pb.source.Set(pkey, value) +} + +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, + } } - return } -func (itr unprefixIterator) Valid() bool { - return itr.source.Valid() +func (itr prefixIterator) Domain() (start []byte, end []byte) { + return itr.start, itr.end } -func (itr unprefixIterator) Next() { +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) Key() (key []byte) { +func (itr prefixIterator) Key() (key []byte) { + if !itr.valid { + panic("prefixIterator invalid, cannot call Key()") + } 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() + } + } +} diff --git a/db/prefix_db_test.go b/db/prefix_db_test.go index fd44a7ec8..60809f157 100644 --- a/db/prefix_db_test.go +++ b/db/prefix_db_test.go @@ -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,42 @@ 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")) + 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 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")) @@ -40,5 +76,72 @@ 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 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() } diff --git a/db/util.go b/db/util.go index 1ad5002d6..51277ac42 100644 --- a/db/util.go +++ b/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,12 +66,13 @@ func IsKeyInDomain(key, start, end []byte, isReverse bool) bool { return false } return true + } else { + if start != nil && bytes.Compare(start, key) < 0 { + return false + } + if end != nil && bytes.Compare(key, end) <= 0 { + return false + } + return true } - if start != nil && bytes.Compare(start, key) < 0 { - return false - } - if end != nil && bytes.Compare(key, end) <= 0 { - return false - } - return true } diff --git a/version/version.go b/version/version.go index b389a63a0..107f5cf3a 100644 --- a/version/version.go +++ b/version/version.go @@ -1,3 +1,3 @@ package version -const Version = "0.8.1" +const Version = "0.8.2"