234 lines
5.3 KiB
Go
234 lines
5.3 KiB
Go
package store
|
|
|
|
import (
|
|
"bytes"
|
|
)
|
|
|
|
// cacheMergeIterator merges a parent Iterator and a cache Iterator.
|
|
// The cache iterator may return nil keys to signal that an item
|
|
// had been deleted (but not deleted in the parent).
|
|
// If the cache iterator has the same key as the parent, the
|
|
// cache shadows (overrides) the parent.
|
|
//
|
|
// TODO: Optimize by memoizing.
|
|
type cacheMergeIterator struct {
|
|
parent Iterator
|
|
cache Iterator
|
|
ascending bool
|
|
}
|
|
|
|
var _ Iterator = (*cacheMergeIterator)(nil)
|
|
|
|
func newCacheMergeIterator(parent, cache Iterator, ascending bool) *cacheMergeIterator {
|
|
iter := &cacheMergeIterator{
|
|
parent: parent,
|
|
cache: cache,
|
|
ascending: ascending,
|
|
}
|
|
return iter
|
|
}
|
|
|
|
// Domain implements Iterator.
|
|
// If the domains are different, returns the union.
|
|
func (iter *cacheMergeIterator) Domain() (start, end []byte) {
|
|
startP, endP := iter.parent.Domain()
|
|
startC, endC := iter.cache.Domain()
|
|
if iter.compare(startP, startC) < 0 {
|
|
start = startP
|
|
} else {
|
|
start = startC
|
|
}
|
|
if iter.compare(endP, endC) < 0 {
|
|
end = endC
|
|
} else {
|
|
end = endP
|
|
}
|
|
return start, end
|
|
}
|
|
|
|
// Valid implements Iterator.
|
|
func (iter *cacheMergeIterator) Valid() bool {
|
|
return iter.skipUntilExistsOrInvalid()
|
|
}
|
|
|
|
// Next implements Iterator
|
|
func (iter *cacheMergeIterator) Next() {
|
|
iter.skipUntilExistsOrInvalid()
|
|
iter.assertValid()
|
|
|
|
// If parent is invalid, get the next cache item.
|
|
if !iter.parent.Valid() {
|
|
iter.cache.Next()
|
|
return
|
|
}
|
|
|
|
// If cache is invalid, get the next parent item.
|
|
if !iter.cache.Valid() {
|
|
iter.parent.Next()
|
|
return
|
|
}
|
|
|
|
// Both are valid. Compare keys.
|
|
keyP, keyC := iter.parent.Key(), iter.cache.Key()
|
|
switch iter.compare(keyP, keyC) {
|
|
case -1: // parent < cache
|
|
iter.parent.Next()
|
|
case 0: // parent == cache
|
|
iter.parent.Next()
|
|
iter.cache.Next()
|
|
case 1: // parent > cache
|
|
iter.cache.Next()
|
|
}
|
|
}
|
|
|
|
// Key implements Iterator
|
|
func (iter *cacheMergeIterator) Key() []byte {
|
|
iter.skipUntilExistsOrInvalid()
|
|
iter.assertValid()
|
|
|
|
// If parent is invalid, get the cache key.
|
|
if !iter.parent.Valid() {
|
|
return iter.cache.Key()
|
|
}
|
|
|
|
// If cache is invalid, get the parent key.
|
|
if !iter.cache.Valid() {
|
|
return iter.parent.Key()
|
|
}
|
|
|
|
// Both are valid. Compare keys.
|
|
keyP, keyC := iter.parent.Key(), iter.cache.Key()
|
|
cmp := iter.compare(keyP, keyC)
|
|
switch cmp {
|
|
case -1: // parent < cache
|
|
return keyP
|
|
case 0: // parent == cache
|
|
return keyP
|
|
case 1: // parent > cache
|
|
return keyC
|
|
default:
|
|
panic("invalid compare result")
|
|
}
|
|
}
|
|
|
|
// Value implements Iterator
|
|
func (iter *cacheMergeIterator) Value() []byte {
|
|
iter.skipUntilExistsOrInvalid()
|
|
iter.assertValid()
|
|
|
|
// If parent is invalid, get the cache value.
|
|
if !iter.parent.Valid() {
|
|
return iter.cache.Value()
|
|
}
|
|
|
|
// If cache is invalid, get the parent value.
|
|
if !iter.cache.Valid() {
|
|
return iter.parent.Value()
|
|
}
|
|
|
|
// Both are valid. Compare keys.
|
|
keyP, keyC := iter.parent.Key(), iter.cache.Key()
|
|
cmp := iter.compare(keyP, keyC)
|
|
switch cmp {
|
|
case -1: // parent < cache
|
|
return iter.parent.Value()
|
|
case 0: // parent == cache
|
|
return iter.cache.Value()
|
|
case 1: // parent > cache
|
|
return iter.cache.Value()
|
|
default:
|
|
panic("invalid comparison result")
|
|
}
|
|
}
|
|
|
|
// Close implements Iterator
|
|
func (iter *cacheMergeIterator) Close() {
|
|
iter.parent.Close()
|
|
iter.cache.Close()
|
|
}
|
|
|
|
// Like bytes.Compare but opposite if not ascending.
|
|
func (iter *cacheMergeIterator) compare(a, b []byte) int {
|
|
if iter.ascending {
|
|
return bytes.Compare(a, b)
|
|
} else {
|
|
return bytes.Compare(a, b) * -1
|
|
}
|
|
}
|
|
|
|
// Skip all delete-items from the cache w/ `key < until`. After this function,
|
|
// current cache item is a non-delete-item, or `until <= key`.
|
|
// If the current cache item is not a delete item, does nothing.
|
|
// If `until` is nil, there is no limit, and cache may end up invalid.
|
|
// CONTRACT: cache is valid.
|
|
func (iter *cacheMergeIterator) skipCacheDeletes(until []byte) {
|
|
for iter.cache.Valid() &&
|
|
iter.cache.Value() == nil &&
|
|
(until == nil || iter.compare(iter.cache.Key(), until) < 0) {
|
|
|
|
iter.cache.Next()
|
|
}
|
|
}
|
|
|
|
// Fast forwards cache (or parent+cache in case of deleted items) until current
|
|
// item exists, or until iterator becomes invalid.
|
|
// Returns whether the iterator is valid.
|
|
func (iter *cacheMergeIterator) skipUntilExistsOrInvalid() bool {
|
|
for {
|
|
|
|
// If parent is invalid, fast-forward cache.
|
|
if !iter.parent.Valid() {
|
|
iter.skipCacheDeletes(nil)
|
|
return iter.cache.Valid()
|
|
}
|
|
// Parent is valid.
|
|
|
|
if !iter.cache.Valid() {
|
|
return true
|
|
}
|
|
// Parent is valid, cache is valid.
|
|
|
|
// Compare parent and cache.
|
|
keyP := iter.parent.Key()
|
|
keyC := iter.cache.Key()
|
|
switch iter.compare(keyP, keyC) {
|
|
|
|
case -1: // parent < cache.
|
|
return true
|
|
|
|
case 0: // parent == cache.
|
|
|
|
// Skip over if cache item is a delete.
|
|
valueC := iter.cache.Value()
|
|
if valueC == nil {
|
|
iter.parent.Next()
|
|
iter.cache.Next()
|
|
continue
|
|
}
|
|
// Cache is not a delete.
|
|
|
|
return true // cache exists.
|
|
|
|
case 1: // cache < parent
|
|
|
|
// Skip over if cache item is a delete.
|
|
valueC := iter.cache.Value()
|
|
if valueC == nil {
|
|
iter.skipCacheDeletes(keyP)
|
|
continue
|
|
}
|
|
// Cache is not a delete.
|
|
|
|
return true // cache exists.
|
|
}
|
|
}
|
|
}
|
|
|
|
// If not valid, panics.
|
|
// NOTE: May have side-effect of iterating over cache.
|
|
func (iter *cacheMergeIterator) assertValid() {
|
|
if !iter.Valid() {
|
|
panic("iterator is invalid")
|
|
}
|
|
}
|