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 }