pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / util / ByteBlockPool.java
diff --git a/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/util/ByteBlockPool.java b/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/util/ByteBlockPool.java
new file mode 100644 (file)
index 0000000..24530d5
--- /dev/null
@@ -0,0 +1,285 @@
+package org.apache.lucene.util;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.lucene.store.DataOutput;
+
+import static org.apache.lucene.util.RamUsageEstimator.NUM_BYTES_OBJECT_REF;
+
+/** 
+ * Class that Posting and PostingVector use to write byte
+ * streams into shared fixed-size byte[] arrays.  The idea
+ * is to allocate slices of increasing lengths For
+ * example, the first slice is 5 bytes, the next slice is
+ * 14, etc.  We start by writing our bytes into the first
+ * 5 bytes.  When we hit the end of the slice, we allocate
+ * the next slice and then write the address of the new
+ * slice into the last 4 bytes of the previous slice (the
+ * "forwarding address").
+ *
+ * Each slice is filled with 0's initially, and we mark
+ * the end with a non-zero byte.  This way the methods
+ * that are writing into the slice don't need to record
+ * its length and instead allocate a new slice once they
+ * hit a non-zero byte. 
+ * 
+ * @lucene.internal
+ **/
+public final class ByteBlockPool {
+  public final static int BYTE_BLOCK_SHIFT = 15;
+  public final static int BYTE_BLOCK_SIZE = 1 << BYTE_BLOCK_SHIFT;
+  public final static int BYTE_BLOCK_MASK = BYTE_BLOCK_SIZE - 1;
+
+  public abstract static class Allocator {
+    protected final int blockSize;
+
+    public Allocator(int blockSize) {
+      this.blockSize = blockSize;
+    }
+
+    public abstract void recycleByteBlocks(byte[][] blocks, int start, int end);
+
+    public void recycleByteBlocks(List<byte[]> blocks) {
+      final byte[][] b = blocks.toArray(new byte[blocks.size()][]);
+      recycleByteBlocks(b, 0, b.length);
+    }
+
+    public byte[] getByteBlock() {
+      return new byte[blockSize];
+    }
+  }
+  
+  public static final class DirectAllocator extends Allocator {
+    
+    public DirectAllocator() {
+      this(BYTE_BLOCK_SIZE);
+    }
+
+    public DirectAllocator(int blockSize) {
+      super(blockSize);
+    }
+
+    @Override
+    public void recycleByteBlocks(byte[][] blocks, int start, int end) {
+    }
+    
+  }
+  
+  public static class DirectTrackingAllocator extends Allocator {
+    private final AtomicLong bytesUsed;
+    
+    public DirectTrackingAllocator(AtomicLong bytesUsed) {
+      this(BYTE_BLOCK_SIZE, bytesUsed);
+    }
+
+    public DirectTrackingAllocator(int blockSize, AtomicLong bytesUsed) {
+      super(blockSize);
+      this.bytesUsed = bytesUsed;
+    }
+
+    public byte[] getByteBlock() {
+      bytesUsed.addAndGet(blockSize);
+      return new byte[blockSize];
+    }
+    @Override
+    public void recycleByteBlocks(byte[][] blocks, int start, int end) {
+      bytesUsed.addAndGet(-((end-start)* blockSize));
+      for (int i = start; i < end; i++) {
+        blocks[i] = null;
+      }
+    }
+    
+  };
+
+
+  public byte[][] buffers = new byte[10][];
+
+  int bufferUpto = -1;                        // Which buffer we are upto
+  public int byteUpto = BYTE_BLOCK_SIZE;             // Where we are in head buffer
+
+  public byte[] buffer;                              // Current head buffer
+  public int byteOffset = -BYTE_BLOCK_SIZE;          // Current head offset
+
+  private final Allocator allocator;
+
+  public ByteBlockPool(Allocator allocator) {
+    this.allocator = allocator;
+  }
+  
+  public void dropBuffersAndReset() {
+    if (bufferUpto != -1) {
+      // Recycle all but the first buffer
+      allocator.recycleByteBlocks(buffers, 0, 1+bufferUpto);
+
+      // Re-use the first buffer
+      bufferUpto = -1;
+      byteUpto = BYTE_BLOCK_SIZE;
+      byteOffset = -BYTE_BLOCK_SIZE;
+      buffers = new byte[10][];
+      buffer = null;
+    }
+  }
+
+  public void reset() {
+    if (bufferUpto != -1) {
+      // We allocated at least one buffer
+
+      for(int i=0;i<bufferUpto;i++)
+        // Fully zero fill buffers that we fully used
+        Arrays.fill(buffers[i], (byte) 0);
+
+      // Partial zero fill the final buffer
+      Arrays.fill(buffers[bufferUpto], 0, byteUpto, (byte) 0);
+          
+      if (bufferUpto > 0)
+        // Recycle all but the first buffer
+        allocator.recycleByteBlocks(buffers, 1, 1+bufferUpto);
+
+      // Re-use the first buffer
+      bufferUpto = 0;
+      byteUpto = 0;
+      byteOffset = 0;
+      buffer = buffers[0];
+    }
+  }
+  
+  public void nextBuffer() {
+    if (1+bufferUpto == buffers.length) {
+      byte[][] newBuffers = new byte[ArrayUtil.oversize(buffers.length+1,
+                                                        NUM_BYTES_OBJECT_REF)][];
+      System.arraycopy(buffers, 0, newBuffers, 0, buffers.length);
+      buffers = newBuffers;
+    }
+    buffer = buffers[1+bufferUpto] = allocator.getByteBlock();
+    bufferUpto++;
+
+    byteUpto = 0;
+    byteOffset += BYTE_BLOCK_SIZE;
+  }
+
+  public int newSlice(final int size) {
+    if (byteUpto > BYTE_BLOCK_SIZE-size)
+      nextBuffer();
+    final int upto = byteUpto;
+    byteUpto += size;
+    buffer[byteUpto-1] = 16;
+    return upto;
+  }
+
+  // Size of each slice.  These arrays should be at most 16
+  // elements (index is encoded with 4 bits).  First array
+  // is just a compact way to encode X+1 with a max.  Second
+  // array is the length of each slice, ie first slice is 5
+  // bytes, next slice is 14 bytes, etc.
+  
+  public final static int[] nextLevelArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 9};
+  public final static int[] levelSizeArray = {5, 14, 20, 30, 40, 40, 80, 80, 120, 200};
+  public final static int FIRST_LEVEL_SIZE = levelSizeArray[0];
+
+  public int allocSlice(final byte[] slice, final int upto) {
+
+    final int level = slice[upto] & 15;
+    final int newLevel = nextLevelArray[level];
+    final int newSize = levelSizeArray[newLevel];
+
+    // Maybe allocate another block
+    if (byteUpto > BYTE_BLOCK_SIZE-newSize)
+      nextBuffer();
+
+    final int newUpto = byteUpto;
+    final int offset = newUpto + byteOffset;
+    byteUpto += newSize;
+
+    // Copy forward the past 3 bytes (which we are about
+    // to overwrite with the forwarding address):
+    buffer[newUpto] = slice[upto-3];
+    buffer[newUpto+1] = slice[upto-2];
+    buffer[newUpto+2] = slice[upto-1];
+
+    // Write forwarding address at end of last slice:
+    slice[upto-3] = (byte) (offset >>> 24);
+    slice[upto-2] = (byte) (offset >>> 16);
+    slice[upto-1] = (byte) (offset >>> 8);
+    slice[upto] = (byte) offset;
+        
+    // Write new level:
+    buffer[byteUpto-1] = (byte) (16|newLevel);
+
+    return newUpto+3;
+  }
+
+  // Fill in a BytesRef from term's length & bytes encoded in
+  // byte block
+  public final BytesRef setBytesRef(BytesRef term, int textStart) {
+    final byte[] bytes = term.bytes = buffers[textStart >> BYTE_BLOCK_SHIFT];
+    int pos = textStart & BYTE_BLOCK_MASK;
+    if ((bytes[pos] & 0x80) == 0) {
+      // length is 1 byte
+      term.length = bytes[pos];
+      term.offset = pos+1;
+    } else {
+      // length is 2 bytes
+      term.length = (bytes[pos]&0x7f) + ((bytes[pos+1]&0xff)<<7);
+      term.offset = pos+2;
+    }
+    assert term.length >= 0;
+    return term;
+  }
+  
+  /**
+   * Copies the given {@link BytesRef} at the current positions (
+   * {@link #byteUpto} across buffer boundaries
+   */
+  public final void copy(final BytesRef bytes) {
+    int length = bytes.length;
+    int offset = bytes.offset;
+    int overflow = (length + byteUpto) - BYTE_BLOCK_SIZE;
+    do {
+      if (overflow <= 0) { 
+        System.arraycopy(bytes.bytes, offset, buffer, byteUpto, length);
+        byteUpto += length;
+        break;
+      } else {
+        final int bytesToCopy = length-overflow;
+        System.arraycopy(bytes.bytes, offset, buffer, byteUpto, bytesToCopy);
+        offset += bytesToCopy;
+        length -= bytesToCopy;
+        nextBuffer();
+        overflow = overflow - BYTE_BLOCK_SIZE;
+      }
+    }  while(true);
+  }
+  
+  /**
+   * Writes the pools content to the given {@link DataOutput}
+   */
+  public final void writePool(final DataOutput out) throws IOException {
+    int bytesOffset = byteOffset;
+    int block = 0;
+    while (bytesOffset > 0) {
+      out.writeBytes(buffers[block++], BYTE_BLOCK_SIZE);
+      bytesOffset -= BYTE_BLOCK_SIZE;
+    }
+    out.writeBytes(buffers[block], byteUpto);
+  }
+}
+