aboutsummaryrefslogblamecommitdiffstats
path: root/swarm/shed/index.go
blob: df88b1b62dc36f3503ce5d6397253ad11b665885 (plain) (tree)


















                                                                                  

               


                                             
                                                               





                                                            
                                                                   



                                                                         
                  









                                                        


                                                     




















                                                                       

                              




                                                  



                                                                              




                                                                   



                                                                          

















                                                                               
                                                                     








                                                                                                  
                                                                     






                                                       
                                                           
                                                                

                                                          







                                              
                                                      





                                        
                                                 
                               
                                        













                                                    
                                                                     











                                          
                                                 
                                         
                                                   








                                                         
                                                                                







                                              
                                                            



                                                        











                                                                   
 
















                                                                               


                                











                                                                         
                               
                                                  

                             

                                                                                               


                                  

                                                                                                 


                                  
                                                         









                                  

















                                                             

                                               
                             








                                                         
                       
         
                                
 
// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package shed

import (
    "bytes"

    "github.com/syndtr/goleveldb/leveldb"
)

// Item holds fields relevant to Swarm Chunk data and metadata.
// All information required for swarm storage and operations
// on that storage must be defined here.
// This structure is logically connected to swarm storage,
// the only part of this package that is not generalized,
// mostly for performance reasons.
//
// Item is a type that is used for retrieving, storing and encoding
// chunk data and metadata. It is passed as an argument to Index encoding
// functions, get function and put function.
// But it is also returned with additional data from get function call
// and as the argument in iterator function definition.
type Item struct {
    Address         []byte
    Data            []byte
    AccessTimestamp int64
    StoreTimestamp  int64
    // UseMockStore is a pointer to identify
    // an unset state of the field in Join function.
    UseMockStore *bool
}

// Merge is a helper method to construct a new
// Item by filling up fields with default values
// of a particular Item with values from another one.
func (i Item) Merge(i2 Item) (new Item) {
    if i.Address == nil {
        i.Address = i2.Address
    }
    if i.Data == nil {
        i.Data = i2.Data
    }
    if i.AccessTimestamp == 0 {
        i.AccessTimestamp = i2.AccessTimestamp
    }
    if i.StoreTimestamp == 0 {
        i.StoreTimestamp = i2.StoreTimestamp
    }
    if i.UseMockStore == nil {
        i.UseMockStore = i2.UseMockStore
    }
    return i
}

// Index represents a set of LevelDB key value pairs that have common
// prefix. It holds functions for encoding and decoding keys and values
// to provide transparent actions on saved data which inclide:
// - getting a particular Item
// - saving a particular Item
// - iterating over a sorted LevelDB keys
// It implements IndexIteratorInterface interface.
type Index struct {
    db              *DB
    prefix          []byte
    encodeKeyFunc   func(fields Item) (key []byte, err error)
    decodeKeyFunc   func(key []byte) (e Item, err error)
    encodeValueFunc func(fields Item) (value []byte, err error)
    decodeValueFunc func(keyFields Item, value []byte) (e Item, err error)
}

// IndexFuncs structure defines functions for encoding and decoding
// LevelDB keys and values for a specific index.
type IndexFuncs struct {
    EncodeKey   func(fields Item) (key []byte, err error)
    DecodeKey   func(key []byte) (e Item, err error)
    EncodeValue func(fields Item) (value []byte, err error)
    DecodeValue func(keyFields Item, value []byte) (e Item, err error)
}

// NewIndex returns a new Index instance with defined name and
// encoding functions. The name must be unique and will be validated
// on database schema for a key prefix byte.
func (db *DB) NewIndex(name string, funcs IndexFuncs) (f Index, err error) {
    id, err := db.schemaIndexPrefix(name)
    if err != nil {
        return f, err
    }
    prefix := []byte{id}
    return Index{
        db:     db,
        prefix: prefix,
        // This function adjusts Index LevelDB key
        // by appending the provided index id byte.
        // This is needed to avoid collisions between keys of different
        // indexes as all index ids are unique.
        encodeKeyFunc: func(e Item) (key []byte, err error) {
            key, err = funcs.EncodeKey(e)
            if err != nil {
                return nil, err
            }
            return append(append(make([]byte, 0, len(key)+1), prefix...), key...), nil
        },
        // This function reverses the encodeKeyFunc constructed key
        // to transparently work with index keys without their index ids.
        // It assumes that index keys are prefixed with only one byte.
        decodeKeyFunc: func(key []byte) (e Item, err error) {
            return funcs.DecodeKey(key[1:])
        },
        encodeValueFunc: funcs.EncodeValue,
        decodeValueFunc: funcs.DecodeValue,
    }, nil
}

// Get accepts key fields represented as Item to retrieve a
// value from the index and return maximum available information
// from the index represented as another Item.
func (f Index) Get(keyFields Item) (out Item, err error) {
    key, err := f.encodeKeyFunc(keyFields)
    if err != nil {
        return out, err
    }
    value, err := f.db.Get(key)
    if err != nil {
        return out, err
    }
    out, err = f.decodeValueFunc(keyFields, value)
    if err != nil {
        return out, err
    }
    return out.Merge(keyFields), nil
}

// Put accepts Item to encode information from it
// and save it to the database.
func (f Index) Put(i Item) (err error) {
    key, err := f.encodeKeyFunc(i)
    if err != nil {
        return err
    }
    value, err := f.encodeValueFunc(i)
    if err != nil {
        return err
    }
    return f.db.Put(key, value)
}

// PutInBatch is the same as Put method, but it just
// saves the key/value pair to the batch instead
// directly to the database.
func (f Index) PutInBatch(batch *leveldb.Batch, i Item) (err error) {
    key, err := f.encodeKeyFunc(i)
    if err != nil {
        return err
    }
    value, err := f.encodeValueFunc(i)
    if err != nil {
        return err
    }
    batch.Put(key, value)
    return nil
}

// Delete accepts Item to remove a key/value pair
// from the database based on its fields.
func (f Index) Delete(keyFields Item) (err error) {
    key, err := f.encodeKeyFunc(keyFields)
    if err != nil {
        return err
    }
    return f.db.Delete(key)
}

// DeleteInBatch is the same as Delete just the operation
// is performed on the batch instead on the database.
func (f Index) DeleteInBatch(batch *leveldb.Batch, keyFields Item) (err error) {
    key, err := f.encodeKeyFunc(keyFields)
    if err != nil {
        return err
    }
    batch.Delete(key)
    return nil
}

// IndexIterFunc is a callback on every Item that is decoded
// by iterating on an Index keys.
// By returning a true for stop variable, iteration will
// stop, and by returning the error, that error will be
// propagated to the called iterator method on Index.
type IndexIterFunc func(item Item) (stop bool, err error)

// IterateOptions defines optional parameters for Iterate function.
type IterateOptions struct {
    // StartFrom is the Item to start the iteration from.
    StartFrom *Item
    // If SkipStartFromItem is true, StartFrom item will not
    // be iterated on.
    SkipStartFromItem bool
    // Iterate over items which keys have a common prefix.
    Prefix []byte
}

// Iterate function iterates over keys of the Index.
// If IterateOptions is nil, the iterations is over all keys.
func (f Index) Iterate(fn IndexIterFunc, options *IterateOptions) (err error) {
    if options == nil {
        options = new(IterateOptions)
    }
    // construct a prefix with Index prefix and optional common key prefix
    prefix := append(f.prefix, options.Prefix...)
    // start from the prefix
    startKey := prefix
    if options.StartFrom != nil {
        // start from the provided StartFrom Item key value
        startKey, err = f.encodeKeyFunc(*options.StartFrom)
        if err != nil {
            return err
        }
    }
    it := f.db.NewIterator()
    defer it.Release()

    // move the cursor to the start key
    ok := it.Seek(startKey)
    if !ok {
        // stop iterator if seek has failed
        return it.Error()
    }
    if options.SkipStartFromItem && bytes.Equal(startKey, it.Key()) {
        // skip the start from Item if it is the first key
        // and it is explicitly configured to skip it
        ok = it.Next()
    }
    for ; ok; ok = it.Next() {
        key := it.Key()
        if !bytes.HasPrefix(key, prefix) {
            break
        }
        // create a copy of key byte slice not to share leveldb underlaying slice array
        keyItem, err := f.decodeKeyFunc(append([]byte(nil), key...))
        if err != nil {
            return err
        }
        // create a copy of value byte slice not to share leveldb underlaying slice array
        valueItem, err := f.decodeValueFunc(keyItem, append([]byte(nil), it.Value()...))
        if err != nil {
            return err
        }
        stop, err := fn(keyItem.Merge(valueItem))
        if err != nil {
            return err
        }
        if stop {
            break
        }
    }
    return it.Error()
}

// Count returns the number of items in index.
func (f Index) Count() (count int, err error) {
    it := f.db.NewIterator()
    defer it.Release()

    for ok := it.Seek(f.prefix); ok; ok = it.Next() {
        key := it.Key()
        if key[0] != f.prefix[0] {
            break
        }
        count++
    }
    return count, it.Error()
}

// CountFrom returns the number of items in index keys
// starting from the key encoded from the provided Item.
func (f Index) CountFrom(start Item) (count int, err error) {
    startKey, err := f.encodeKeyFunc(start)
    if err != nil {
        return 0, err
    }
    it := f.db.NewIterator()
    defer it.Release()

    for ok := it.Seek(startKey); ok; ok = it.Next() {
        key := it.Key()
        if key[0] != f.prefix[0] {
            break
        }
        count++
    }
    return count, it.Error()
}