/* * 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. */ package org.apache.lucene.util; /** Acts like forever growing T[], but internally uses a * circular buffer to reuse instances of T. * * @lucene.internal */ public abstract class RollingBuffer<T extends RollingBuffer.Resettable> { /** * Implement to reset an instance */ public static interface Resettable { public void reset(); } @SuppressWarnings("unchecked") private T[] buffer = (T[]) new RollingBuffer.Resettable[8]; // Next array index to write to: private int nextWrite; // Next position to write: private int nextPos; // How many valid Position are held in the // array: private int count; public RollingBuffer() { for(int idx=0;idx<buffer.length;idx++) { buffer[idx] = newInstance(); } } protected abstract T newInstance(); public void reset() { nextWrite--; while (count > 0) { if (nextWrite == -1) { nextWrite = buffer.length - 1; } buffer[nextWrite--].reset(); count--; } nextWrite = 0; nextPos = 0; count = 0; } // For assert: private boolean inBounds(int pos) { return pos < nextPos && pos >= nextPos - count; } private int getIndex(int pos) { int index = nextWrite - (nextPos - pos); if (index < 0) { index += buffer.length; } return index; } /** Get T instance for this absolute position; * this is allowed to be arbitrarily far "in the * future" but cannot be before the last freeBefore. */ public T get(int pos) { //System.out.println("RA.get pos=" + pos + " nextPos=" + nextPos + " nextWrite=" + nextWrite + " count=" + count); while (pos >= nextPos) { if (count == buffer.length) { @SuppressWarnings("unchecked") T[] newBuffer = (T[]) new Resettable[ArrayUtil.oversize(1+count, RamUsageEstimator.NUM_BYTES_OBJECT_REF)]; //System.out.println(" grow length=" + newBuffer.length); System.arraycopy(buffer, nextWrite, newBuffer, 0, buffer.length-nextWrite); System.arraycopy(buffer, 0, newBuffer, buffer.length-nextWrite, nextWrite); for(int i=buffer.length;i<newBuffer.length;i++) { newBuffer[i] = newInstance(); } nextWrite = buffer.length; buffer = newBuffer; } if (nextWrite == buffer.length) { nextWrite = 0; } // Should have already been reset: nextWrite++; nextPos++; count++; } assert inBounds(pos): "pos=" + pos + " nextPos=" + nextPos + " count=" + count; final int index = getIndex(pos); //System.out.println(" pos=" + pos + " nextPos=" + nextPos + " -> index=" + index); //assert buffer[index].pos == pos; return buffer[index]; } /** Returns the maximum position looked up, or -1 if no * position has been looked up since reset/init. */ public int getMaxPos() { return nextPos-1; } /** Returns how many active positions are in the buffer. */ public int getBufferSize() { return count; } public void freeBefore(int pos) { final int toFree = count - (nextPos - pos); assert toFree >= 0; assert toFree <= count: "toFree=" + toFree + " count=" + count; int index = nextWrite - count; if (index < 0) { index += buffer.length; } for(int i=0;i<toFree;i++) { if (index == buffer.length) { index = 0; } //System.out.println(" fb idx=" + index); buffer[index].reset(); index++; } count -= toFree; } }