|
|
@@ -37,8 +37,7 @@ func max(x, y int) int {
|
|
|
}
|
|
|
|
|
|
type block struct {
|
|
|
- bpool *util.BufferPool
|
|
|
- cmp comparer.BasicComparer
|
|
|
+ tr *Reader
|
|
|
data []byte
|
|
|
restartsLen int
|
|
|
restartsOffset int
|
|
|
@@ -47,31 +46,25 @@ type block struct {
|
|
|
}
|
|
|
|
|
|
func (b *block) seek(rstart, rlimit int, key []byte) (index, offset int, err error) {
|
|
|
- n := b.restartsOffset
|
|
|
- data := b.data
|
|
|
- cmp := b.cmp
|
|
|
-
|
|
|
index = sort.Search(b.restartsLen-rstart-(b.restartsLen-rlimit), func(i int) bool {
|
|
|
- offset := int(binary.LittleEndian.Uint32(data[n+4*(rstart+i):]))
|
|
|
- offset += 1 // shared always zero, since this is a restart point
|
|
|
- v1, n1 := binary.Uvarint(data[offset:]) // key length
|
|
|
- _, n2 := binary.Uvarint(data[offset+n1:]) // value length
|
|
|
+ offset := int(binary.LittleEndian.Uint32(b.data[b.restartsOffset+4*(rstart+i):]))
|
|
|
+ offset += 1 // shared always zero, since this is a restart point
|
|
|
+ v1, n1 := binary.Uvarint(b.data[offset:]) // key length
|
|
|
+ _, n2 := binary.Uvarint(b.data[offset+n1:]) // value length
|
|
|
m := offset + n1 + n2
|
|
|
- return cmp.Compare(data[m:m+int(v1)], key) > 0
|
|
|
+ return b.tr.cmp.Compare(b.data[m:m+int(v1)], key) > 0
|
|
|
}) + rstart - 1
|
|
|
if index < rstart {
|
|
|
// The smallest key is greater-than key sought.
|
|
|
index = rstart
|
|
|
}
|
|
|
- offset = int(binary.LittleEndian.Uint32(data[n+4*index:]))
|
|
|
+ offset = int(binary.LittleEndian.Uint32(b.data[b.restartsOffset+4*index:]))
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (b *block) restartIndex(rstart, rlimit, offset int) int {
|
|
|
- n := b.restartsOffset
|
|
|
- data := b.data
|
|
|
return sort.Search(b.restartsLen-rstart-(b.restartsLen-rlimit), func(i int) bool {
|
|
|
- return int(binary.LittleEndian.Uint32(data[n+4*(rstart+i):])) > offset
|
|
|
+ return int(binary.LittleEndian.Uint32(b.data[b.restartsOffset+4*(rstart+i):])) > offset
|
|
|
}) + rstart - 1
|
|
|
}
|
|
|
|
|
|
@@ -141,10 +134,10 @@ func (b *block) newIterator(slice *util.Range, inclLimit bool, cache util.Releas
|
|
|
}
|
|
|
|
|
|
func (b *block) Release() {
|
|
|
- if b.bpool != nil {
|
|
|
- b.bpool.Put(b.data)
|
|
|
- b.bpool = nil
|
|
|
+ if b.tr.bpool != nil {
|
|
|
+ b.tr.bpool.Put(b.data)
|
|
|
}
|
|
|
+ b.tr = nil
|
|
|
b.data = nil
|
|
|
}
|
|
|
|
|
|
@@ -270,7 +263,7 @@ func (i *blockIter) Seek(key []byte) bool {
|
|
|
i.dir = dirForward
|
|
|
}
|
|
|
for i.Next() {
|
|
|
- if i.block.cmp.Compare(i.key, key) >= 0 {
|
|
|
+ if i.block.tr.cmp.Compare(i.key, key) >= 0 {
|
|
|
return true
|
|
|
}
|
|
|
}
|
|
|
@@ -479,7 +472,7 @@ func (i *blockIter) Error() error {
|
|
|
}
|
|
|
|
|
|
type filterBlock struct {
|
|
|
- filter filter.Filter
|
|
|
+ tr *Reader
|
|
|
data []byte
|
|
|
oOffset int
|
|
|
baseLg uint
|
|
|
@@ -493,7 +486,7 @@ func (b *filterBlock) contains(offset uint64, key []byte) bool {
|
|
|
n := int(binary.LittleEndian.Uint32(o))
|
|
|
m := int(binary.LittleEndian.Uint32(o[4:]))
|
|
|
if n < m && m <= b.oOffset {
|
|
|
- return b.filter.Contains(b.data[n:m], key)
|
|
|
+ return b.tr.filter.Contains(b.data[n:m], key)
|
|
|
} else if n == m {
|
|
|
return false
|
|
|
}
|
|
|
@@ -501,10 +494,17 @@ func (b *filterBlock) contains(offset uint64, key []byte) bool {
|
|
|
return true
|
|
|
}
|
|
|
|
|
|
+func (b *filterBlock) Release() {
|
|
|
+ if b.tr.bpool != nil {
|
|
|
+ b.tr.bpool.Put(b.data)
|
|
|
+ }
|
|
|
+ b.tr = nil
|
|
|
+ b.data = nil
|
|
|
+}
|
|
|
+
|
|
|
type indexIter struct {
|
|
|
- blockIter
|
|
|
- tableReader *Reader
|
|
|
- slice *util.Range
|
|
|
+ *blockIter
|
|
|
+ slice *util.Range
|
|
|
// Options
|
|
|
checksum bool
|
|
|
fillCache bool
|
|
|
@@ -523,7 +523,7 @@ func (i *indexIter) Get() iterator.Iterator {
|
|
|
if i.slice != nil && (i.blockIter.isFirst() || i.blockIter.isLast()) {
|
|
|
slice = i.slice
|
|
|
}
|
|
|
- return i.tableReader.getDataIter(dataBH, slice, i.checksum, i.fillCache)
|
|
|
+ return i.blockIter.block.tr.getDataIter(dataBH, slice, i.checksum, i.fillCache)
|
|
|
}
|
|
|
|
|
|
// Reader is a table reader.
|
|
|
@@ -538,9 +538,8 @@ type Reader struct {
|
|
|
checksum bool
|
|
|
strictIter bool
|
|
|
|
|
|
- dataEnd int64
|
|
|
- indexBlock *block
|
|
|
- filterBlock *filterBlock
|
|
|
+ dataEnd int64
|
|
|
+ indexBH, filterBH blockHandle
|
|
|
}
|
|
|
|
|
|
func verifyChecksum(data []byte) bool {
|
|
|
@@ -557,6 +556,7 @@ func (r *Reader) readRawBlock(bh blockHandle, checksum bool) ([]byte, error) {
|
|
|
}
|
|
|
if checksum || r.checksum {
|
|
|
if !verifyChecksum(data) {
|
|
|
+ r.bpool.Put(data)
|
|
|
return nil, errors.New("leveldb/table: Reader: invalid block (checksum mismatch)")
|
|
|
}
|
|
|
}
|
|
|
@@ -575,6 +575,7 @@ func (r *Reader) readRawBlock(bh blockHandle, checksum bool) ([]byte, error) {
|
|
|
return nil, err
|
|
|
}
|
|
|
default:
|
|
|
+ r.bpool.Put(data)
|
|
|
return nil, fmt.Errorf("leveldb/table: Reader: unknown block compression type: %d", data[bh.length])
|
|
|
}
|
|
|
return data, nil
|
|
|
@@ -587,7 +588,7 @@ func (r *Reader) readBlock(bh blockHandle, checksum bool) (*block, error) {
|
|
|
}
|
|
|
restartsLen := int(binary.LittleEndian.Uint32(data[len(data)-4:]))
|
|
|
b := &block{
|
|
|
- cmp: r.cmp,
|
|
|
+ tr: r,
|
|
|
data: data,
|
|
|
restartsLen: restartsLen,
|
|
|
restartsOffset: len(data) - (restartsLen+1)*4,
|
|
|
@@ -596,7 +597,44 @@ func (r *Reader) readBlock(bh blockHandle, checksum bool) (*block, error) {
|
|
|
return b, nil
|
|
|
}
|
|
|
|
|
|
-func (r *Reader) readFilterBlock(bh blockHandle, filter filter.Filter) (*filterBlock, error) {
|
|
|
+func (r *Reader) readBlockCached(bh blockHandle, checksum, fillCache bool) (*block, util.Releaser, error) {
|
|
|
+ if r.cache != nil {
|
|
|
+ var err error
|
|
|
+ ch := r.cache.Get(bh.offset, func() (charge int, value interface{}) {
|
|
|
+ if !fillCache {
|
|
|
+ return 0, nil
|
|
|
+ }
|
|
|
+ var b *block
|
|
|
+ b, err = r.readBlock(bh, checksum)
|
|
|
+ if err != nil {
|
|
|
+ return 0, nil
|
|
|
+ }
|
|
|
+ return cap(b.data), b
|
|
|
+ })
|
|
|
+ if ch != nil {
|
|
|
+ b, ok := ch.Value().(*block)
|
|
|
+ if !ok {
|
|
|
+ ch.Release()
|
|
|
+ return nil, nil, errors.New("leveldb/table: Reader: inconsistent block type")
|
|
|
+ }
|
|
|
+ if !b.checksum && (r.checksum || checksum) {
|
|
|
+ if !verifyChecksum(b.data) {
|
|
|
+ ch.Release()
|
|
|
+ return nil, nil, errors.New("leveldb/table: Reader: invalid block (checksum mismatch)")
|
|
|
+ }
|
|
|
+ b.checksum = true
|
|
|
+ }
|
|
|
+ return b, ch, err
|
|
|
+ } else if err != nil {
|
|
|
+ return nil, nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ b, err := r.readBlock(bh, checksum)
|
|
|
+ return b, b, err
|
|
|
+}
|
|
|
+
|
|
|
+func (r *Reader) readFilterBlock(bh blockHandle) (*filterBlock, error) {
|
|
|
data, err := r.readRawBlock(bh, true)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
@@ -611,7 +649,7 @@ func (r *Reader) readFilterBlock(bh blockHandle, filter filter.Filter) (*filterB
|
|
|
return nil, errors.New("leveldb/table: Reader: invalid filter block (invalid offset)")
|
|
|
}
|
|
|
b := &filterBlock{
|
|
|
- filter: filter,
|
|
|
+ tr: r,
|
|
|
data: data,
|
|
|
oOffset: oOffset,
|
|
|
baseLg: uint(data[n-1]),
|
|
|
@@ -620,42 +658,42 @@ func (r *Reader) readFilterBlock(bh blockHandle, filter filter.Filter) (*filterB
|
|
|
return b, nil
|
|
|
}
|
|
|
|
|
|
-func (r *Reader) getDataIter(dataBH blockHandle, slice *util.Range, checksum, fillCache bool) iterator.Iterator {
|
|
|
+func (r *Reader) readFilterBlockCached(bh blockHandle, fillCache bool) (*filterBlock, util.Releaser, error) {
|
|
|
if r.cache != nil {
|
|
|
- // Get/set block cache.
|
|
|
var err error
|
|
|
- cache := r.cache.Get(dataBH.offset, func() (charge int, value interface{}) {
|
|
|
+ ch := r.cache.Get(bh.offset, func() (charge int, value interface{}) {
|
|
|
if !fillCache {
|
|
|
return 0, nil
|
|
|
}
|
|
|
- var dataBlock *block
|
|
|
- dataBlock, err = r.readBlock(dataBH, checksum)
|
|
|
+ var b *filterBlock
|
|
|
+ b, err = r.readFilterBlock(bh)
|
|
|
if err != nil {
|
|
|
return 0, nil
|
|
|
}
|
|
|
- return int(dataBH.length), dataBlock
|
|
|
+ return cap(b.data), b
|
|
|
})
|
|
|
- if err != nil {
|
|
|
- return iterator.NewEmptyIterator(err)
|
|
|
- }
|
|
|
- if cache != nil {
|
|
|
- dataBlock := cache.Value().(*block)
|
|
|
- if !dataBlock.checksum && (r.checksum || checksum) {
|
|
|
- if !verifyChecksum(dataBlock.data) {
|
|
|
- return iterator.NewEmptyIterator(errors.New("leveldb/table: Reader: invalid block (checksum mismatch)"))
|
|
|
- }
|
|
|
- dataBlock.checksum = true
|
|
|
+ if ch != nil {
|
|
|
+ b, ok := ch.Value().(*filterBlock)
|
|
|
+ if !ok {
|
|
|
+ ch.Release()
|
|
|
+ return nil, nil, errors.New("leveldb/table: Reader: inconsistent block type")
|
|
|
}
|
|
|
- iter := dataBlock.newIterator(slice, false, cache)
|
|
|
- return iter
|
|
|
+ return b, ch, err
|
|
|
+ } else if err != nil {
|
|
|
+ return nil, nil, err
|
|
|
}
|
|
|
}
|
|
|
- dataBlock, err := r.readBlock(dataBH, checksum)
|
|
|
+
|
|
|
+ b, err := r.readFilterBlock(bh)
|
|
|
+ return b, b, err
|
|
|
+}
|
|
|
+
|
|
|
+func (r *Reader) getDataIter(dataBH blockHandle, slice *util.Range, checksum, fillCache bool) iterator.Iterator {
|
|
|
+ b, rel, err := r.readBlockCached(dataBH, checksum, fillCache)
|
|
|
if err != nil {
|
|
|
return iterator.NewEmptyIterator(err)
|
|
|
}
|
|
|
- iter := dataBlock.newIterator(slice, false, dataBlock)
|
|
|
- return iter
|
|
|
+ return b.newIterator(slice, false, rel)
|
|
|
}
|
|
|
|
|
|
// NewIterator creates an iterator from the table.
|
|
|
@@ -669,18 +707,21 @@ func (r *Reader) getDataIter(dataBH blockHandle, slice *util.Range, checksum, fi
|
|
|
// when not used.
|
|
|
//
|
|
|
// Also read Iterator documentation of the leveldb/iterator package.
|
|
|
-
|
|
|
func (r *Reader) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
|
|
|
if r.err != nil {
|
|
|
return iterator.NewEmptyIterator(r.err)
|
|
|
}
|
|
|
|
|
|
+ fillCache := !ro.GetDontFillCache()
|
|
|
+ b, rel, err := r.readBlockCached(r.indexBH, true, fillCache)
|
|
|
+ if err != nil {
|
|
|
+ return iterator.NewEmptyIterator(err)
|
|
|
+ }
|
|
|
index := &indexIter{
|
|
|
- blockIter: *r.indexBlock.newIterator(slice, true, nil),
|
|
|
- tableReader: r,
|
|
|
- slice: slice,
|
|
|
- checksum: ro.GetStrict(opt.StrictBlockChecksum),
|
|
|
- fillCache: !ro.GetDontFillCache(),
|
|
|
+ blockIter: b.newIterator(slice, true, rel),
|
|
|
+ slice: slice,
|
|
|
+ checksum: ro.GetStrict(opt.StrictBlockChecksum),
|
|
|
+ fillCache: !ro.GetDontFillCache(),
|
|
|
}
|
|
|
return iterator.NewIndexedIterator(index, r.strictIter || ro.GetStrict(opt.StrictIterator), false)
|
|
|
}
|
|
|
@@ -697,7 +738,13 @@ func (r *Reader) Find(key []byte, ro *opt.ReadOptions) (rkey, value []byte, err
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- index := r.indexBlock.newIterator(nil, true, nil)
|
|
|
+ indexBlock, rel, err := r.readBlockCached(r.indexBH, true, true)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer rel.Release()
|
|
|
+
|
|
|
+ index := indexBlock.newIterator(nil, true, nil)
|
|
|
defer index.Release()
|
|
|
if !index.Seek(key) {
|
|
|
err = index.Error()
|
|
|
@@ -711,9 +758,15 @@ func (r *Reader) Find(key []byte, ro *opt.ReadOptions) (rkey, value []byte, err
|
|
|
err = errors.New("leveldb/table: Reader: invalid table (bad data block handle)")
|
|
|
return
|
|
|
}
|
|
|
- if r.filterBlock != nil && !r.filterBlock.contains(dataBH.offset, key) {
|
|
|
- err = ErrNotFound
|
|
|
- return
|
|
|
+ if r.filter != nil {
|
|
|
+ filterBlock, rel, ferr := r.readFilterBlockCached(r.filterBH, true)
|
|
|
+ if ferr == nil {
|
|
|
+ if !filterBlock.contains(dataBH.offset, key) {
|
|
|
+ rel.Release()
|
|
|
+ return nil, nil, ErrNotFound
|
|
|
+ }
|
|
|
+ rel.Release()
|
|
|
+ }
|
|
|
}
|
|
|
data := r.getDataIter(dataBH, nil, ro.GetStrict(opt.StrictBlockChecksum), !ro.GetDontFillCache())
|
|
|
defer data.Release()
|
|
|
@@ -760,7 +813,13 @@ func (r *Reader) OffsetOf(key []byte) (offset int64, err error) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- index := r.indexBlock.newIterator(nil, true, nil)
|
|
|
+ indexBlock, rel, err := r.readBlockCached(r.indexBH, true, true)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer rel.Release()
|
|
|
+
|
|
|
+ index := indexBlock.newIterator(nil, true, nil)
|
|
|
defer index.Release()
|
|
|
if index.Seek(key) {
|
|
|
dataBH, n := decodeBlockHandle(index.Value())
|
|
|
@@ -778,6 +837,17 @@ func (r *Reader) OffsetOf(key []byte) (offset int64, err error) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
+// Release implements util.Releaser.
|
|
|
+// It also close the file if it is an io.Closer.
|
|
|
+func (r *Reader) Release() {
|
|
|
+ if closer, ok := r.reader.(io.Closer); ok {
|
|
|
+ closer.Close()
|
|
|
+ }
|
|
|
+ r.reader = nil
|
|
|
+ r.cache = nil
|
|
|
+ r.bpool = nil
|
|
|
+}
|
|
|
+
|
|
|
// NewReader creates a new initialized table reader for the file.
|
|
|
// The cache and bpool is optional and can be nil.
|
|
|
//
|
|
|
@@ -817,16 +887,11 @@ func NewReader(f io.ReaderAt, size int64, cache cache.Namespace, bpool *util.Buf
|
|
|
return r
|
|
|
}
|
|
|
// Decode the index block handle.
|
|
|
- indexBH, n := decodeBlockHandle(footer[n:])
|
|
|
+ r.indexBH, n = decodeBlockHandle(footer[n:])
|
|
|
if n == 0 {
|
|
|
r.err = errors.New("leveldb/table: Reader: invalid table (bad index block handle)")
|
|
|
return r
|
|
|
}
|
|
|
- // Read index block.
|
|
|
- r.indexBlock, r.err = r.readBlock(indexBH, true)
|
|
|
- if r.err != nil {
|
|
|
- return r
|
|
|
- }
|
|
|
// Read metaindex block.
|
|
|
metaBlock, err := r.readBlock(metaBH, true)
|
|
|
if err != nil {
|
|
|
@@ -842,32 +907,28 @@ func NewReader(f io.ReaderAt, size int64, cache cache.Namespace, bpool *util.Buf
|
|
|
continue
|
|
|
}
|
|
|
fn := key[7:]
|
|
|
- var filter filter.Filter
|
|
|
if f0 := o.GetFilter(); f0 != nil && f0.Name() == fn {
|
|
|
- filter = f0
|
|
|
+ r.filter = f0
|
|
|
} else {
|
|
|
for _, f0 := range o.GetAltFilters() {
|
|
|
if f0.Name() == fn {
|
|
|
- filter = f0
|
|
|
+ r.filter = f0
|
|
|
break
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- if filter != nil {
|
|
|
+ if r.filter != nil {
|
|
|
filterBH, n := decodeBlockHandle(metaIter.Value())
|
|
|
if n == 0 {
|
|
|
continue
|
|
|
}
|
|
|
+ r.filterBH = filterBH
|
|
|
// Update data end.
|
|
|
r.dataEnd = int64(filterBH.offset)
|
|
|
- filterBlock, err := r.readFilterBlock(filterBH, filter)
|
|
|
- if err != nil {
|
|
|
- continue
|
|
|
- }
|
|
|
- r.filterBlock = filterBlock
|
|
|
break
|
|
|
}
|
|
|
}
|
|
|
metaIter.Release()
|
|
|
+ metaBlock.Release()
|
|
|
return r
|
|
|
}
|