block_cache.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // Copyright 2015 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum 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. // go-ethereum 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 go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. package core
  17. import (
  18. "sync"
  19. "github.com/ethereum/go-ethereum/common"
  20. "github.com/ethereum/go-ethereum/core/types"
  21. )
  22. // BlockCache implements a caching mechanism specifically for blocks and uses FILO to pop
  23. type BlockCache struct {
  24. size int
  25. hashes []common.Hash
  26. blocks map[common.Hash]*types.Block
  27. mu sync.RWMutex
  28. }
  29. // Creates and returns a `BlockCache` with `size`. If `size` is smaller than 1 it will panic
  30. func NewBlockCache(size int) *BlockCache {
  31. if size < 1 {
  32. panic("block cache size not allowed to be smaller than 1")
  33. }
  34. bc := &BlockCache{size: size}
  35. bc.Clear()
  36. return bc
  37. }
  38. func (bc *BlockCache) Clear() {
  39. bc.blocks = make(map[common.Hash]*types.Block)
  40. bc.hashes = nil
  41. }
  42. func (bc *BlockCache) Push(block *types.Block) {
  43. bc.mu.Lock()
  44. defer bc.mu.Unlock()
  45. if len(bc.hashes) == bc.size {
  46. delete(bc.blocks, bc.hashes[0])
  47. // XXX There are a few other options on solving this
  48. // 1) use a poller / GC like mechanism to clean up untracked objects
  49. // 2) copy as below
  50. // re-use the slice and remove the reference to bc.hashes[0]
  51. // this will allow the element to be garbage collected.
  52. copy(bc.hashes, bc.hashes[1:])
  53. } else {
  54. bc.hashes = append(bc.hashes, common.Hash{})
  55. }
  56. hash := block.Hash()
  57. bc.blocks[hash] = block
  58. bc.hashes[len(bc.hashes)-1] = hash
  59. }
  60. func (bc *BlockCache) Delete(hash common.Hash) {
  61. bc.mu.Lock()
  62. defer bc.mu.Unlock()
  63. if _, ok := bc.blocks[hash]; ok {
  64. delete(bc.blocks, hash)
  65. for i, h := range bc.hashes {
  66. if hash == h {
  67. bc.hashes = bc.hashes[:i+copy(bc.hashes[i:], bc.hashes[i+1:])]
  68. // or ? => bc.hashes = append(bc.hashes[:i], bc.hashes[i+1]...)
  69. break
  70. }
  71. }
  72. }
  73. }
  74. func (bc *BlockCache) Get(hash common.Hash) *types.Block {
  75. bc.mu.RLock()
  76. defer bc.mu.RUnlock()
  77. if block, haz := bc.blocks[hash]; haz {
  78. return block
  79. }
  80. return nil
  81. }
  82. func (bc *BlockCache) Has(hash common.Hash) bool {
  83. bc.mu.RLock()
  84. defer bc.mu.RUnlock()
  85. _, ok := bc.blocks[hash]
  86. return ok
  87. }
  88. func (bc *BlockCache) Each(cb func(int, *types.Block)) {
  89. bc.mu.Lock()
  90. defer bc.mu.Unlock()
  91. i := 0
  92. for _, block := range bc.blocks {
  93. cb(i, block)
  94. i++
  95. }
  96. }