github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/cachekit/alloc.go (about) 1 // Copyright 2020 Insolar Network Ltd. 2 // All rights reserved. 3 // This material is licensed under the Insolar License version 1.0, 4 // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md. 5 6 package cachekit 7 8 import ( 9 "math" 10 11 "github.com/insolar/vanilla/throw" 12 ) 13 14 func newAllocationTracker(pageSize int) allocationTracker { 15 if pageSize <= 0 { 16 panic(throw.IllegalValue()) 17 } 18 return allocationTracker{ 19 pages: []allocPage{ make(allocPage, 0, pageSize) }, 20 freeIdx: math.MinInt32, 21 } 22 } 23 24 type allocationTracker struct { 25 pages []allocPage 26 freeIdx int32 27 freeCnt uint32 28 } 29 30 type allocPage []int32 31 32 func (p *allocationTracker) Add(value int32) int { 33 switch { 34 case value < 0: 35 panic(throw.IllegalValue()) 36 case p.freeIdx != math.MinInt32: 37 return p.reuse(value) 38 } 39 40 pgN := len(p.pages) - 1 41 pg := &p.pages[pgN] 42 pgSize := cap(*pg) 43 n := len(*pg) 44 45 if n == pgSize { 46 p.pages = append(p.pages, make(allocPage, 0, pgSize)) 47 pgN++ 48 n = 0 49 pg = &p.pages[pgN] 50 } 51 52 *pg = append(*pg, value) 53 return pgN * pgSize + n 54 } 55 56 func (p *allocationTracker) get(index int) *int32 { 57 pgSize := cap(p.pages[0]) 58 pgN := index / pgSize 59 if pgN >= len(p.pages) { 60 return nil 61 } 62 n := index % pgSize 63 if n >= len(p.pages[pgN]) { 64 return nil 65 } 66 return &p.pages[pgN][n] 67 } 68 69 70 func (p *allocationTracker) AllocatedCount() int { 71 n := len(p.pages) 72 if n == 0 { 73 return 0 74 } 75 return n * cap(p.pages[0]) 76 } 77 78 func (p *allocationTracker) Count() int { 79 pgN := len(p.pages) 80 if pgN == 0 { 81 return 0 82 } 83 84 pgSize := cap(p.pages[0]) 85 pgN-- 86 87 return pgN * pgSize + len(p.pages[pgN]) - int(p.freeCnt) 88 } 89 90 func (p *allocationTracker) Get(index int) (int32, bool) { 91 switch vi := p.get(index); { 92 case vi == nil: 93 return -1, false 94 case *vi < 0: 95 return -1, false 96 default: 97 return *vi, true 98 } 99 } 100 101 func (p *allocationTracker) Set(index int, value int32) bool { 102 switch vi := p.get(index); { 103 case value < 0: 104 panic(throw.IllegalValue()) 105 case vi == nil: 106 return false 107 case *vi < 0: 108 return false 109 default: 110 *vi = value 111 return true 112 } 113 } 114 115 func (p *allocationTracker) Inc(index int) (int32, bool, bool) { 116 switch vi := p.get(index); { 117 case vi == nil: 118 return -1, false, false 119 case *vi < 0: 120 return -1, false, false 121 default: 122 switch n := *vi; { 123 case n < 0: 124 panic(throw.IllegalValue()) 125 case n == math.MaxInt32: 126 return math.MaxInt32, true, true 127 default: 128 n++ 129 *vi = n 130 return n, false, true 131 } 132 } 133 } 134 135 func (p *allocationTracker) Dec(index int) (int32, bool) { 136 switch vi := p.get(index); { 137 case vi == nil: 138 return -1, false 139 case *vi < 0: 140 return -1, false 141 default: 142 switch n := *vi; { 143 case n < 0: 144 panic(throw.IllegalValue()) 145 case n == 0: 146 return 0, true 147 default: 148 n-- 149 *vi = n 150 return n, true 151 } 152 } 153 } 154 155 func (p *allocationTracker) Delete(index int) bool { 156 switch vi := p.get(index); { 157 case vi == nil: 158 return false 159 case *vi < 0: 160 return false 161 case p.freeIdx >= 0: 162 panic(throw.Impossible()) 163 default: 164 *vi = p.freeIdx 165 p.freeIdx = -int32(index + 1) 166 p.freeCnt++ 167 return true 168 } 169 } 170 171 func (p *allocationTracker) reuse(value int32) int { 172 free := p.freeIdx 173 switch { 174 case free >= 0: 175 panic(throw.IllegalState()) 176 case free == math.MinInt32: 177 // this method can't be called then 178 panic(throw.Impossible()) 179 case p.freeCnt == 0: 180 panic(throw.Impossible()) 181 } 182 183 n := -int(free)-1 184 switch vi := p.get(n); { 185 case vi == nil: 186 panic(throw.Impossible()) 187 case *vi >= 0: 188 panic(throw.Impossible()) 189 default: 190 p.freeIdx = *vi 191 p.freeCnt-- 192 *vi = value 193 } 194 return n 195 }