github.com/balzaczyy/golucene@v0.0.0-20151210033525-d0be9ee89713/core/util/byteBlockPool.go (about) 1 package util 2 3 import ( 4 "sync/atomic" 5 ) 6 7 // util/ByteBlockPool.java 8 9 /* 10 An array holding the offset into the LEVEL_SIZE_ARRAY to quickly 11 navigate to the next slice level. 12 */ 13 var NEXT_LEVEL_ARRAY = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 9} 14 15 /* An array holding the level sizes for byte slices. */ 16 var LEVEL_SIZE_ARRAY = []int{5, 14, 20, 30, 40, 40, 80, 80, 120, 200} 17 18 /* THe first level size for new slices */ 19 var FIRST_LEVEL_SIZE = LEVEL_SIZE_ARRAY[0] 20 21 /* 22 Class that Posting and PostingVector use to writ ebyte streams into 23 shared fixed-size []byte arrays. The idea is to allocate slices of 24 increasing lengths. For example, the first slice is 5 bytes, the next 25 slice is 14, etc. We start by writing out bytes into the first 5 26 bytes. When we hit the end of the slice, we allocate the next slice 27 and then write the address of the next slice into the last 4 bytes of 28 the previous slice (the "forwarding address"). 29 30 Each slice is filled with 0's initially, and we mark the end with a 31 non-zero byte. This way the methods that are writing into the slice 32 don't need to record its length and instead allocate a new slice once 33 they hit a non-zero byte. 34 */ 35 type ByteBlockPool struct { 36 Buffers [][]byte 37 bufferUpto int 38 ByteUpto int 39 Buffer []byte 40 ByteOffset int 41 allocator ByteAllocator 42 } 43 44 func NewByteBlockPool(allocator ByteAllocator) *ByteBlockPool { 45 return &ByteBlockPool{ 46 bufferUpto: -1, 47 ByteUpto: BYTE_BLOCK_SIZE, 48 ByteOffset: -BYTE_BLOCK_SIZE, 49 allocator: allocator, 50 } 51 } 52 53 /* Expert: Resets the pool to its initial state reusing the first buffer. */ 54 func (pool *ByteBlockPool) Reset(zeroFillBuffers, reuseFirst bool) { 55 // TODO consolidate with IntBlockPool.Reset() 56 if pool.bufferUpto != -1 { 57 // We allocated at least one buffer 58 if zeroFillBuffers { 59 panic("not implemented yet") 60 } 61 62 if pool.bufferUpto > 0 || !reuseFirst { 63 offset := 0 64 if reuseFirst { 65 offset = 1 66 } 67 // Recycle all but the first buffer 68 pool.allocator.recycle(pool.Buffers[offset : 1+pool.bufferUpto]) 69 for i := offset; i <= pool.bufferUpto; i++ { 70 pool.Buffers[i] = nil 71 } 72 } 73 if reuseFirst { 74 panic("not implemented yet") 75 } else { 76 pool.bufferUpto = -1 77 pool.ByteUpto = BYTE_BLOCK_SIZE 78 pool.ByteOffset = -BYTE_BLOCK_SIZE 79 pool.Buffer = nil 80 } 81 } 82 } 83 84 /* 85 Advances the pool to its next buffer. This method should be called 86 once after the constructor to initialize the pool. In contrast to the 87 constructor, a ByteBlockPool.Reset() call will advance the pool to 88 its first buffer immediately. 89 */ 90 func (pool *ByteBlockPool) NextBuffer() { 91 if 1+pool.bufferUpto == len(pool.Buffers) { 92 newBuffers := make([][]byte, Oversize(len(pool.Buffers)+1, NUM_BYTES_OBJECT_REF)) 93 copy(newBuffers, pool.Buffers) 94 pool.Buffers = newBuffers 95 } 96 pool.Buffer = pool.allocator.allocate() 97 pool.Buffers[1+pool.bufferUpto] = pool.Buffer 98 pool.bufferUpto++ 99 100 pool.ByteUpto = 0 101 pool.ByteOffset += BYTE_BLOCK_SIZE 102 } 103 104 /* Allocates a new slice with the given size. */ 105 func (pool *ByteBlockPool) NewSlice(size int) int { 106 if pool.ByteUpto > BYTE_BLOCK_SIZE-size { 107 pool.NextBuffer() 108 } 109 upto := pool.ByteUpto 110 pool.ByteUpto += size 111 pool.Buffer[pool.ByteUpto-1] = 16 112 return upto 113 } 114 115 /* 116 Creates a new byte slice with the given starting size and returns the 117 slices offset in the pool. 118 */ 119 func (p *ByteBlockPool) AllocSlice(slice []byte, upto int) int { 120 level := slice[upto] & 15 121 newLevel := NEXT_LEVEL_ARRAY[level] 122 newSize := LEVEL_SIZE_ARRAY[newLevel] 123 124 // maybe allocate another block 125 if p.ByteUpto > BYTE_BLOCK_SIZE-newSize { 126 p.NextBuffer() 127 } 128 129 newUpto := p.ByteUpto 130 offset := newUpto + p.ByteOffset 131 p.ByteUpto += newSize 132 133 // copy forward the post 3 bytes (which we are about to overwrite 134 // with the forwarding address): 135 p.Buffer[newUpto] = slice[upto-3] 136 p.Buffer[newUpto+1] = slice[upto-2] 137 p.Buffer[newUpto+2] = slice[upto-1] 138 139 // write forwarding address at end of last slice: 140 slice[upto-3] = byte(uint(offset) >> 24) 141 slice[upto-2] = byte(uint(offset) >> 16) 142 slice[upto-1] = byte(uint(offset) >> 8) 143 slice[upto] = byte(offset) 144 145 // write new level: 146 p.Buffer[p.ByteUpto-1] = byte(16 | newLevel) 147 148 return newUpto + 3 149 } 150 151 /* Fill in a BytesRef from term's length & bytes encoded in byte block */ 152 func (p *ByteBlockPool) SetBytesRef(term *BytesRef, textStart int) { 153 bytes := p.Buffers[textStart>>BYTE_BLOCK_SHIFT] 154 term.Bytes = bytes 155 pos := textStart & BYTE_BLOCK_MASK 156 if (bytes[pos] & 0x80) == 0 { 157 // length is 1 byte 158 term.Length = int(bytes[pos]) 159 term.Offset = pos + 1 160 } else { 161 // length is 2 bytes 162 term.Length = (int(bytes[pos]) & 0x7f) + ((int(bytes[pos+1]) & 0xff) << 7) 163 term.Offset = pos + 2 164 } 165 assert(term.Length >= 0) 166 } 167 168 /* Abstract class for allocating and freeing byte blocks. */ 169 type ByteAllocator interface { 170 recycle(blocks [][]byte) 171 allocate() []byte 172 } 173 174 type ByteAllocatorImpl struct { 175 blockSize int 176 } 177 178 func newByteAllocator(blockSize int) *ByteAllocatorImpl { 179 return &ByteAllocatorImpl{blockSize} 180 } 181 182 func (a *ByteAllocatorImpl) allocate() []byte { 183 assert(a.blockSize <= 1000000) // should not allocate more than 1MB? 184 return make([]byte, a.blockSize) 185 } 186 187 /* A simple Allocator that never recycles, but tracks how much total RAM is in use. */ 188 type DirectTrackingAllocator struct { 189 *ByteAllocatorImpl 190 bytesUsed Counter 191 } 192 193 func NewDirectTrackingAllocator(bytesUsed Counter) *DirectTrackingAllocator { 194 return &DirectTrackingAllocator{ 195 ByteAllocatorImpl: newByteAllocator(BYTE_BLOCK_SIZE), 196 bytesUsed: bytesUsed, 197 } 198 } 199 200 func (alloc *DirectTrackingAllocator) recycle(blocks [][]byte) { 201 alloc.bytesUsed.AddAndGet(int64(-len(blocks) * alloc.blockSize)) 202 for i, _ := range blocks { 203 blocks[i] = nil 204 } 205 } 206 207 // util/Counter.java 208 209 type Counter interface { 210 AddAndGet(delta int64) int64 211 Get() int64 212 } 213 214 func NewCounter() Counter { 215 return &serialCounter{0} 216 } 217 218 func NewAtomicCounter() Counter { 219 return &atomicCounter{0} 220 } 221 222 type serialCounter struct { 223 count int64 224 } 225 226 func (sc *serialCounter) AddAndGet(delta int64) int64 { 227 sc.count += delta 228 return sc.count 229 } 230 231 func (sc *serialCounter) Get() int64 { 232 return sc.count 233 } 234 235 type atomicCounter struct { 236 count int64 237 } 238 239 func (ac *atomicCounter) AddAndGet(delta int64) int64 { 240 return atomic.AddInt64(&ac.count, delta) 241 } 242 243 func (ac *atomicCounter) Get() int64 { 244 return atomic.LoadInt64(&ac.count) 245 }