123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- /*
- * EliasDB
- *
- * Copyright 2016 Matthias Ladkau. All rights reserved.
- *
- * 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, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
- package hash
- import (
- "errors"
- "fmt"
- "devt.de/krotik/eliasdb/storage"
- )
- /*
- ErrNoMoreItems is assigned to LastError when Next() is called and there are no
- more items to iterate.
- */
- var ErrNoMoreItems = errors.New("No more items to iterate")
- /*
- HTreeIterator data structure
- */
- type HTreeIterator struct {
- tree *HTree // Tree to iterate
- nodePath []uint64 // Path in the tree we currently traversing
- indices []int // List of the current indices in the current path
- nextKey []byte // Next iterator key (overwritten by nextItem)
- nextValue interface{} // Next iterator value
- LastError error // Last encountered error
- }
- /*
- NewHTreeIterator creates a new HTreeIterator.
- */
- func NewHTreeIterator(tree *HTree) *HTreeIterator {
- it := &HTreeIterator{tree, make([]uint64, 0), make([]int, 0), nil, nil, nil}
- it.nodePath = append(it.nodePath, tree.Root.Location())
- it.indices = append(it.indices, -1)
- // Set the nextKey and nextValue properties
- it.Next()
- return it
- }
- /*
- HasNext returns if there is a next key / value pair.
- */
- func (it *HTreeIterator) HasNext() bool {
- return it.nextKey != nil
- }
- /*
- Next returns the next key / value pair.
- */
- func (it *HTreeIterator) Next() ([]byte, interface{}) {
- key := it.nextKey
- value := it.nextValue
- if err := it.nextItem(); err != ErrNoMoreItems && err != nil {
- it.LastError = err
- // There was a serious error terminate the iterator
- it.nodePath = make([]uint64, 0)
- it.indices = make([]int, 0)
- it.nextKey = nil
- it.nextValue = nil
- }
- return key, value
- }
- /*
- Retrieve the next key / value pair for the iterator. The tree might
- have changed significantly after the last call. We need to cope
- with errors as best as we can.
- */
- func (it *HTreeIterator) nextItem() error {
- // Check if there are more items available to iterate
- if len(it.nodePath) == 0 {
- it.nextKey = nil
- it.nextValue = nil
- return ErrNoMoreItems
- }
- // Get the current path element
- loc := it.nodePath[len(it.nodePath)-1]
- index := it.indices[len(it.indices)-1]
- node, err := it.tree.Root.fetchNode(loc)
- if err != nil {
- if err == storage.ErrSlotNotFound {
- // Something is wrong - the tree must have changed since the last
- // nextItem call. Remove the path element and try again.
- it.nodePath = it.nodePath[:len(it.nodePath)-1]
- it.indices = it.indices[:len(it.indices)-1]
- return it.nextItem()
- }
- // If it is another error there is something more serious - report it
- return err
- }
- if node.Children != nil {
- // If the current path element is a page get the next child and delegate
- page := &htreePage{node}
- page.loc = loc
- page.sm = it.tree.Root.sm
- nextChild := it.searchNextChild(page, index)
- if nextChild != -1 {
- // If we found another element then update the current index and delegate to it
- it.indices[len(it.indices)-1] = nextChild
- it.nodePath = append(it.nodePath, page.Children[nextChild])
- it.indices = append(it.indices, -1)
- return it.nextItem()
- }
- // If we finished this page remove it from the stack and continue
- // with the parent
- it.nodePath = it.nodePath[:len(it.nodePath)-1]
- it.indices = it.indices[:len(it.indices)-1]
- return it.nextItem()
- }
- // If the current path element is a bucket just iterate the elements
- // delegate once it has finished
- bucket := &htreeBucket{node}
- bucket.loc = loc
- bucket.sm = it.tree.Root.sm
- nextElement := it.searchNextElement(bucket, index)
- if nextElement != -1 {
- // If we found another element then update the current index and return it
- it.indices[len(it.indices)-1] = nextElement
- it.nextKey = bucket.Keys[nextElement]
- it.nextValue = bucket.Values[nextElement]
- return nil
- }
- // If we finished this bucket remove it from the stack and continue
- // with the parent
- it.nodePath = it.nodePath[:len(it.nodePath)-1]
- it.indices = it.indices[:len(it.indices)-1]
- return it.nextItem()
- }
- /*
- searchNextChild searches for the index of the next available page child from a given index.
- */
- func (it *HTreeIterator) searchNextChild(page *htreePage, current int) int {
- for i := current + 1; i < MaxPageChildren; i++ {
- child := page.Children[i]
- if child != 0 {
- return i
- }
- }
- return -1
- }
- /*
- searchNextElement searches for the index of the next available bucket element from a given index.
- */
- func (it *HTreeIterator) searchNextElement(bucket *htreeBucket, current int) int {
- next := current + 1
- if next < int(bucket.BucketSize) {
- return next
- }
- return -1
- }
- /*
- Return a string representation of the iterator.
- */
- func (it *HTreeIterator) String() string {
- return fmt.Sprintf("HTree Iterator (tree: %v)\n path: %v\n indices: %v\n next: %v / %v\n",
- it.tree.Root.Location(), it.nodePath, it.indices, it.nextKey, it.nextValue)
- }
|