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  }