github.com/codingfuture/orig-energi3@v0.8.4/swarm/storage/feed/lookup/lookup.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  /*
    18  Package lookup defines feed lookup algorithms and provides tools to place updates
    19  so they can be found
    20  */
    21  package lookup
    22  
    23  const maxuint64 = ^uint64(0)
    24  
    25  // LowestLevel establishes the frequency resolution of the lookup algorithm as a power of 2.
    26  const LowestLevel uint8 = 0 // default is 0 (1 second)
    27  
    28  // HighestLevel sets the lowest frequency the algorithm will operate at, as a power of 2.
    29  // 25 -> 2^25 equals to roughly one year.
    30  const HighestLevel = 25 // default is 25 (~1 year)
    31  
    32  // DefaultLevel sets what level will be chosen to search when there is no hint
    33  const DefaultLevel = HighestLevel
    34  
    35  //Algorithm is the function signature of a lookup algorithm
    36  type Algorithm func(now uint64, hint Epoch, read ReadFunc) (value interface{}, err error)
    37  
    38  // Lookup finds the update with the highest timestamp that is smaller or equal than 'now'
    39  // It takes a hint which should be the epoch where the last known update was
    40  // If you don't know in what epoch the last update happened, simply submit lookup.NoClue
    41  // read() will be called on each lookup attempt
    42  // Returns an error only if read() returns an error
    43  // Returns nil if an update was not found
    44  var Lookup Algorithm = FluzCapacitorAlgorithm
    45  
    46  // ReadFunc is a handler called by Lookup each time it attempts to find a value
    47  // It should return <nil> if a value is not found
    48  // It should return <nil> if a value is found, but its timestamp is higher than "now"
    49  // It should only return an error in case the handler wants to stop the
    50  // lookup process entirely.
    51  type ReadFunc func(epoch Epoch, now uint64) (interface{}, error)
    52  
    53  // NoClue is a hint that can be provided when the Lookup caller does not have
    54  // a clue about where the last update may be
    55  var NoClue = Epoch{}
    56  
    57  // getBaseTime returns the epoch base time of the given
    58  // time and level
    59  func getBaseTime(t uint64, level uint8) uint64 {
    60  	return t & (maxuint64 << level)
    61  }
    62  
    63  // Hint creates a hint based only on the last known update time
    64  func Hint(last uint64) Epoch {
    65  	return Epoch{
    66  		Time:  last,
    67  		Level: DefaultLevel,
    68  	}
    69  }
    70  
    71  // GetNextLevel returns the frequency level a next update should be placed at, provided where
    72  // the last update was and what time it is now.
    73  // This is the first nonzero bit of the XOR of 'last' and 'now', counting from the highest significant bit
    74  // but limited to not return a level that is smaller than the last-1
    75  func GetNextLevel(last Epoch, now uint64) uint8 {
    76  	// First XOR the last epoch base time with the current clock.
    77  	// This will set all the common most significant bits to zero.
    78  	mix := (last.Base() ^ now)
    79  
    80  	// Then, make sure we stop the below loop before one level below the current, by setting
    81  	// that level's bit to 1.
    82  	// If the next level is lower than the current one, it must be exactly level-1 and not lower.
    83  	mix |= (1 << (last.Level - 1))
    84  
    85  	// if the last update was more than 2^highestLevel seconds ago, choose the highest level
    86  	if mix > (maxuint64 >> (64 - HighestLevel - 1)) {
    87  		return HighestLevel
    88  	}
    89  
    90  	// set up a mask to scan for nonzero bits, starting at the highest level
    91  	mask := uint64(1 << (HighestLevel))
    92  
    93  	for i := uint8(HighestLevel); i > LowestLevel; i-- {
    94  		if mix&mask != 0 { // if we find a nonzero bit, this is the level the next update should be at.
    95  			return i
    96  		}
    97  		mask = mask >> 1 // move our bit one position to the right
    98  	}
    99  	return 0
   100  }
   101  
   102  // GetNextEpoch returns the epoch where the next update should be located
   103  // according to where the previous update was
   104  // and what time it is now.
   105  func GetNextEpoch(last Epoch, now uint64) Epoch {
   106  	if last == NoClue {
   107  		return GetFirstEpoch(now)
   108  	}
   109  	level := GetNextLevel(last, now)
   110  	return Epoch{
   111  		Level: level,
   112  		Time:  now,
   113  	}
   114  }
   115  
   116  // GetFirstEpoch returns the epoch where the first update should be located
   117  // based on what time it is now.
   118  func GetFirstEpoch(now uint64) Epoch {
   119  	return Epoch{Level: HighestLevel, Time: now}
   120  }
   121  
   122  var worstHint = Epoch{Time: 0, Level: 63}
   123  
   124  // FluzCapacitorAlgorithm works by narrowing the epoch search area if an update is found
   125  // going back and forth in time
   126  // First, it will attempt to find an update where it should be now if the hint was
   127  // really the last update. If that lookup fails, then the last update must be either the hint itself
   128  // or the epochs right below. If however, that lookup succeeds, then the update must be
   129  // that one or within the epochs right below.
   130  // see the guide for a more graphical representation
   131  func FluzCapacitorAlgorithm(now uint64, hint Epoch, read ReadFunc) (value interface{}, err error) {
   132  	var lastFound interface{}
   133  	var epoch Epoch
   134  	if hint == NoClue {
   135  		hint = worstHint
   136  	}
   137  
   138  	t := now
   139  
   140  	for {
   141  		epoch = GetNextEpoch(hint, t)
   142  		value, err = read(epoch, now)
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  		if value != nil {
   147  			lastFound = value
   148  			if epoch.Level == LowestLevel || epoch.Equals(hint) {
   149  				return value, nil
   150  			}
   151  			hint = epoch
   152  			continue
   153  		}
   154  		if epoch.Base() == hint.Base() {
   155  			if lastFound != nil {
   156  				return lastFound, nil
   157  			}
   158  			// we have reached the hint itself
   159  			if hint == worstHint {
   160  				return nil, nil
   161  			}
   162  			// check it out
   163  			value, err = read(hint, now)
   164  			if err != nil {
   165  				return nil, err
   166  			}
   167  			if value != nil {
   168  				return value, nil
   169  			}
   170  			// bad hint.
   171  			epoch = hint
   172  			hint = worstHint
   173  		}
   174  		base := epoch.Base()
   175  		if base == 0 {
   176  			return nil, nil
   177  		}
   178  		t = base - 1
   179  	}
   180  }