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  }