git.gammaspectra.live/P2Pool/consensus/v3@v3.8.0/p2pool/cache/legacy/legacy.go (about)

     1  package legacy
     2  
     3  import (
     4  	"encoding/binary"
     5  	"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/cache"
     6  	"git.gammaspectra.live/P2Pool/consensus/v3/p2pool/sidechain"
     7  	"git.gammaspectra.live/P2Pool/consensus/v3/utils"
     8  	"io"
     9  	"os"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  )
    14  
    15  const blockSize = 96 * 1024
    16  const NumBlocks = 4608
    17  const cacheSize = blockSize * NumBlocks
    18  
    19  type Cache struct {
    20  	f                 *os.File
    21  	flushRunning      atomic.Bool
    22  	storeIndex        atomic.Uint32
    23  	loadingStarted    sync.Once
    24  	loadingInProgress atomic.Bool
    25  	consensus         *sidechain.Consensus
    26  }
    27  
    28  func NewCache(consensus *sidechain.Consensus, path string) (*Cache, error) {
    29  	if f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666); err != nil {
    30  		return nil, err
    31  	} else {
    32  		if _, err = f.Seek(cacheSize-1, io.SeekStart); err != nil {
    33  			_ = f.Close()
    34  			return nil, err
    35  		}
    36  		//create sparse file
    37  		if _, err = f.Write([]byte{0}); err != nil {
    38  			_ = f.Close()
    39  			return nil, err
    40  		}
    41  
    42  		return &Cache{
    43  			f:         f,
    44  			consensus: consensus,
    45  		}, nil
    46  	}
    47  }
    48  
    49  func (c *Cache) Store(block *sidechain.PoolBlock) {
    50  	if c.loadingInProgress.Load() {
    51  		return
    52  	}
    53  	if blob, err := block.AppendBinaryFlags(make([]byte, 0, block.BufferLength()), false, false); err != nil {
    54  		return
    55  	} else {
    56  		if (len(blob) + 4) > blockSize {
    57  			//block too big
    58  			return
    59  		}
    60  		storeIndex := (c.storeIndex.Add(1) % NumBlocks) * blockSize
    61  		_, _ = c.f.WriteAt(binary.LittleEndian.AppendUint32(nil, uint32(len(blob))), int64(storeIndex))
    62  		_, _ = c.f.WriteAt(blob, int64(storeIndex)+4)
    63  	}
    64  }
    65  
    66  func (c *Cache) LoadAll(l cache.Loadee) {
    67  	c.loadingStarted.Do(func() {
    68  		c.loadingInProgress.Store(true)
    69  		defer c.loadingInProgress.Store(false)
    70  		utils.Logf("Cache", "Loading cached blocks")
    71  
    72  		var blobLen [4]byte
    73  		buf := make([]byte, 0, blockSize)
    74  
    75  		var blocksLoaded int
    76  		for i := 0; i < NumBlocks; i++ {
    77  			storeIndex := (c.storeIndex.Add(1) % NumBlocks) * blockSize
    78  
    79  			if _, err := c.f.ReadAt(blobLen[:], int64(storeIndex)); err != nil {
    80  				return
    81  			}
    82  			blobLength := binary.LittleEndian.Uint32(blobLen[:])
    83  			if (blobLength + 4) > blockSize {
    84  				//block too big
    85  				continue
    86  			}
    87  			if _, err := c.f.ReadAt(buf[:blobLength], int64(storeIndex)+4); err != nil {
    88  				continue
    89  			}
    90  
    91  			block := &sidechain.PoolBlock{
    92  				Metadata: sidechain.PoolBlockReceptionMetadata{
    93  					LocalTime: time.Now().UTC(),
    94  				},
    95  			}
    96  
    97  			if err := block.UnmarshalBinary(c.consensus, &sidechain.NilDerivationCache{}, buf[:blobLength]); err != nil {
    98  				continue
    99  			}
   100  
   101  			l.AddCachedBlock(block)
   102  
   103  			blocksLoaded++
   104  		}
   105  
   106  		utils.Logf("Cache", "Loaded %d cached blocks", blocksLoaded)
   107  	})
   108  }
   109  
   110  func (c *Cache) Close() {
   111  	_ = c.f.Close()
   112  }
   113  
   114  func (c *Cache) Flush() {
   115  	if !c.flushRunning.Swap(true) {
   116  		defer c.flushRunning.Store(false)
   117  		_ = c.f.Sync()
   118  	}
   119  }