github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/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  import "context"
    24  
    25  const maxuint64 = ^uint64(0)
    26  
    27  // LowestLevel establishes the frequency resolution of the lookup algorithm as a power of 2.
    28  const LowestLevel uint8 = 0 // default is 0 (1 second)
    29  
    30  // HighestLevel sets the lowest frequency the algorithm will operate at, as a power of 2.
    31  // 25 -> 2^25 equals to roughly one year.
    32  const HighestLevel = 25 // default is 25 (~1 year)
    33  
    34  // DefaultLevel sets what level will be chosen to search when there is no hint
    35  const DefaultLevel = HighestLevel
    36  
    37  //Algorithm is the function signature of a lookup algorithm
    38  type Algorithm func(ctx context.Context, now uint64, hint Epoch, read ReadFunc) (value interface{}, err error)
    39  
    40  // Lookup finds the update with the highest timestamp that is smaller or equal than 'now'
    41  // It takes a hint which should be the epoch where the last known update was
    42  // If you don't know in what epoch the last update happened, simply submit lookup.NoClue
    43  // read() will be called on each lookup attempt
    44  // Returns an error only if read() returns an error
    45  // Returns nil if an update was not found
    46  var Lookup Algorithm = FluzCapacitorAlgorithm
    47  
    48  // ReadFunc is a handler called by Lookup each time it attempts to find a value
    49  // It should return <nil> if a value is not found
    50  // It should return <nil> if a value is found, but its timestamp is higher than "now"
    51  // It should only return an error in case the handler wants to stop the
    52  // lookup process entirely.
    53  type ReadFunc func(ctx context.Context, epoch Epoch, now uint64) (interface{}, error)
    54  
    55  // NoClue is a hint that can be provided when the Lookup caller does not have
    56  // a clue about where the last update may be
    57  var NoClue = Epoch{}
    58  
    59  // getBaseTime returns the epoch base time of the given
    60  // time and level
    61  func getBaseTime(t uint64, level uint8) uint64 {
    62  	return t & (maxuint64 << level)
    63  }
    64  
    65  // Hint creates a hint based only on the last known update time
    66  func Hint(last uint64) Epoch {
    67  	return Epoch{
    68  		Time:  last,
    69  		Level: DefaultLevel,
    70  	}
    71  }
    72  
    73  // GetNextLevel returns the frequency level a next update should be placed at, provided where
    74  // the last update was and what time it is now.
    75  // This is the first nonzero bit of the XOR of 'last' and 'now', counting from the highest significant bit
    76  // but limited to not return a level that is smaller than the last-1
    77  func GetNextLevel(last Epoch, now uint64) uint8 {
    78  	// First XOR the last epoch base time with the current clock.
    79  	// This will set all the common most significant bits to zero.
    80  	mix := (last.Base() ^ now)
    81  
    82  	// Then, make sure we stop the below loop before one level below the current, by setting
    83  	// that level's bit to 1.
    84  	// If the next level is lower than the current one, it must be exactly level-1 and not lower.
    85  	mix |= (1 << (last.Level - 1))
    86  
    87  	// if the last update was more than 2^highestLevel seconds ago, choose the highest level
    88  	if mix > (maxuint64 >> (64 - HighestLevel - 1)) {
    89  		return HighestLevel
    90  	}
    91  
    92  	// set up a mask to scan for nonzero bits, starting at the highest level
    93  	mask := uint64(1 << (HighestLevel))
    94  
    95  	for i := uint8(HighestLevel); i > LowestLevel; i-- {
    96  		if mix&mask != 0 { // if we find a nonzero bit, this is the level the next update should be at.
    97  			return i
    98  		}
    99  		mask = mask >> 1 // move our bit one position to the right
   100  	}
   101  	return 0
   102  }
   103  
   104  // GetNextEpoch returns the epoch where the next update should be located
   105  // according to where the previous update was
   106  // and what time it is now.
   107  func GetNextEpoch(last Epoch, now uint64) Epoch {
   108  	if last == NoClue {
   109  		return GetFirstEpoch(now)
   110  	}
   111  	level := GetNextLevel(last, now)
   112  	return Epoch{
   113  		Level: level,
   114  		Time:  now,
   115  	}
   116  }
   117  
   118  // GetFirstEpoch returns the epoch where the first update should be located
   119  // based on what time it is now.
   120  func GetFirstEpoch(now uint64) Epoch {
   121  	return Epoch{Level: HighestLevel, Time: now}
   122  }
   123  
   124  var worstHint = Epoch{Time: 0, Level: 63}
   125  
   126  // FluzCapacitorAlgorithm works by narrowing the epoch search area if an update is found
   127  // going back and forth in time
   128  // First, it will attempt to find an update where it should be now if the hint was
   129  // really the last update. If that lookup fails, then the last update must be either the hint itself
   130  // or the epochs right below. If however, that lookup succeeds, then the update must be
   131  // that one or within the epochs right below.
   132  // see the guide for a more graphical representation
   133  func FluzCapacitorAlgorithm(ctx context.Context, now uint64, hint Epoch, read ReadFunc) (value interface{}, err error) {
   134  	var lastFound interface{}
   135  	var epoch Epoch
   136  	if hint == NoClue {
   137  		hint = worstHint
   138  	}
   139  
   140  	t := now
   141  
   142  	for {
   143  		epoch = GetNextEpoch(hint, t)
   144  		value, err = read(ctx, epoch, now)
   145  		if err != nil {
   146  			return nil, err
   147  		}
   148  		if value != nil {
   149  			lastFound = value
   150  			if epoch.Level == LowestLevel || epoch.Equals(hint) {
   151  				return value, nil
   152  			}
   153  			hint = epoch
   154  			continue
   155  		}
   156  		if epoch.Base() == hint.Base() {
   157  			if lastFound != nil {
   158  				return lastFound, nil
   159  			}
   160  			// we have reached the hint itself
   161  			if hint == worstHint {
   162  				return nil, nil
   163  			}
   164  			// check it out
   165  			value, err = read(ctx, hint, now)
   166  			if err != nil {
   167  				return nil, err
   168  			}
   169  			if value != nil {
   170  				return value, nil
   171  			}
   172  			// bad hint.
   173  			t = hint.Base()
   174  			hint = worstHint
   175  			continue
   176  		}
   177  		base := epoch.Base()
   178  		if base == 0 {
   179  			return nil, nil
   180  		}
   181  		t = base - 1
   182  	}
   183  }