github.com/ethersphere/bee/v2@v2.2.0/pkg/feeds/epochs/epoch.go (about) 1 // Copyright 2021 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package epochs implements time-based feeds using epochs as index 6 // and provide sequential as well as concurrent lookup algorithms 7 package epochs 8 9 import ( 10 "encoding/binary" 11 "fmt" 12 13 "github.com/ethersphere/bee/v2/pkg/crypto" 14 "github.com/ethersphere/bee/v2/pkg/feeds" 15 ) 16 17 const ( 18 maxLevel = 32 19 ) 20 21 var _ feeds.Index = (*epoch)(nil) 22 23 // epoch is referencing a slot in the epoch grid and represents an update 24 // it implements the feeds.Index interface 25 type epoch struct { 26 start uint64 27 level uint8 28 } 29 30 func (e *epoch) String() string { 31 return fmt.Sprintf("%d/%d", e.start, e.level) 32 } 33 34 // MarshalBinary implements the BinaryMarshaler interface 35 func (e *epoch) MarshalBinary() ([]byte, error) { 36 epochBytes := make([]byte, 8) 37 binary.BigEndian.PutUint64(epochBytes, e.start) 38 return crypto.LegacyKeccak256(append(epochBytes, e.level)) 39 } 40 41 func next(e feeds.Index, last int64, at uint64) feeds.Index { 42 if e == nil { 43 return &epoch{0, maxLevel} 44 } 45 return e.Next(last, at) 46 } 47 48 // Next implements feeds.Index advancement 49 func (e *epoch) Next(last int64, at uint64) feeds.Index { 50 if e.start+e.length() > at { 51 return e.childAt(at) 52 } 53 return lca(at, uint64(last)).childAt(at) 54 } 55 56 // lca calculates the lowest common ancestor epoch given two unix times 57 func lca(at, after uint64) *epoch { 58 if after == 0 { 59 return &epoch{0, maxLevel} 60 } 61 diff := at - after 62 length := uint64(1) 63 var level uint8 64 for level < maxLevel && (length < diff || at/length != after/length) { 65 length <<= 1 66 level++ 67 } 68 start := (after / length) * length 69 return &epoch{start, level} 70 } 71 72 // parent returns the ancestor of an epoch 73 // the call is unsafe in that it must not be called on a toplevel epoch 74 func (e *epoch) parent() *epoch { 75 length := e.length() << 1 76 start := (e.start / length) * length 77 return &epoch{start, e.level + 1} 78 } 79 80 // left returns the left sister of an epoch 81 // it is unsafe in that it must not be called on a left sister epoch 82 func (e *epoch) left() *epoch { 83 return &epoch{e.start - e.length(), e.level} 84 } 85 86 // at returns the left of right child epoch of an epoch depending on where `at` falls 87 // it is unsafe in that it must not be called with an at that does not fall within the epoch 88 func (e *epoch) childAt(at uint64) *epoch { 89 e = &epoch{e.start, e.level - 1} 90 if at&e.length() > 0 { 91 e.start |= e.length() 92 } 93 return e 94 } 95 96 // isLeft returns true if epoch is a left sister of its parent 97 func (e *epoch) isLeft() bool { 98 return e.start&e.length() == 0 99 } 100 101 // length returns the span of the epoch 102 func (e *epoch) length() uint64 { 103 return 1 << e.level 104 }