com.bigdata.relation.accesspath.AbstractUnsynchronizedArrayBuffer Maven / Gradle / Ivy
/*
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Oct 27, 2008
*/
package com.bigdata.relation.accesspath;
import org.apache.log4j.Logger;
/**
* An abstract implementation of an unsynchronized buffer backed by a fixed
* capacity array.
*
* This implementation is NOT thread-safe.
*
* @author Bryan Thompson
*/
public abstract class AbstractUnsynchronizedArrayBuffer implements IBuffer {
private static final Logger log = Logger
.getLogger(AbstractUnsynchronizedArrayBuffer.class);
/**
* True iff the {@link #log} level is INFO or less.
*/
final static private boolean INFO = log.isInfoEnabled();
/**
* True iff the {@link #log} level is DEBUG or less.
*/
final static private boolean DEBUG = log.isDebugEnabled();
/**
* The capacity of the internal buffer each time it is allocated.
*/
final private int capacity;
/**
* The #of elements in the internal {@link #buffer}. This is reset
* each time we allocate the {@link #buffer}.
*/
private int size = 0;
/**
* The internal buffer -or- null
if we need to allocate one.
*/
private E[] buffer = null;
/**
* The component type for the backing array.
*/
private final Class extends E> cls;
/**
* Optional filter to keep elements out of the buffer.
*/
private final IElementFilter extends E> filter;
/**
* If {@link #size()} reports zero(0).
*/
@Override
final public boolean isEmpty() {
return size == 0;
}
/**
* The exact #of elements currently in the buffer.
*/
@Override
final public int size() {
return size;
}
/**
* @param capacity
* The capacity of the backing buffer.
* @param cls
* The component type for the backing array.
*/
public AbstractUnsynchronizedArrayBuffer(final int capacity,
final Class extends E> cls) {
this(capacity, cls, null/* filter */);
}
/**
* @param capacity
* The capacity of the backing buffer.
* @param cls
* The component type for the backing array.
* @param filter
* Filter to keep elements out of the buffer (optional).
*/
public AbstractUnsynchronizedArrayBuffer(final int capacity,
final Class extends E> cls, final IElementFilter filter) {
if (capacity <= 0)
throw new IllegalArgumentException();
if (cls == null)
throw new IllegalArgumentException();
this.capacity = capacity;
this.filter = filter;
this.cls = cls;
// /*
// * Note: The backing array is allocated dynamically to get the array
// * component type right.
// */
}
/**
* Applies the optional filter to restrict elements allowed into the buffer.
* When the optional filter was NOT specified all elements are allowed into
* the buffer.
*
* @param e
* A element.
*
* @return true if the element is allowed into the buffer.
*/
protected boolean accept(E e) {
if (filter != null && filter.canAccept(e)) {
return filter.isValid(e);
}
return true;
}
/**
* Not thread-safe.
*/
@Override
final public void add(E e) {
add2(e);
}
/**
* Adds an element to the buffer.
*
* Not thread-safe.
*
* @param e An element.
*
* @return true
iff adding the element caused the buffer to
* {@link #overflow()}
*/
@SuppressWarnings("unchecked")
public boolean add2(final E e) {
if (e == null)
throw new IllegalArgumentException();
if (!accept(e)) {
if (DEBUG)
log.debug("reject: " + e.toString());
return false;
}
boolean overflow = false;
if (buffer == null) {
// re-allocate on demand.
buffer = (E[]) java.lang.reflect.Array.newInstance(//
cls,//e.getClass(),
capacity);
size = 0;
} else if (size == buffer.length) {
overflow();
assert size == 0;
assert buffer == null;
// re-allocate so that we can store the new element.
buffer = (E[]) java.lang.reflect.Array.newInstance(//
cls,//e.getClass(),
capacity);
size = 0;
overflow = true;
}
if(DEBUG)
log.debug("accept: " + e);
buffer[size++] = e;
return overflow;
}
/**
* Adds all references in the internal buffer to the target buffer, resets
* the #of elements in the internal buffer to ZERO (0), and clears the
* internal array - it will be reallocated again on demand.
*/
@SuppressWarnings("unchecked")
protected void overflow() {
assert size >= 0;
if (size > 0) {
assert buffer != null;
assert buffer.length > 0;
final boolean dense = size == buffer.length;
if (INFO)
log.info("size=" + size + ", dense=" + dense);
final E[] a;
if (dense) {
a = buffer;
} else {
// make dense.
assert buffer[0] != null;
// allocate using dynamic type.
a = (E[]) java.lang.reflect.Array.newInstance(
// buffer[0].getClass(),
cls,
size);
// copy data into new a[].
System.arraycopy(buffer, 0, a, 0, size);
}
// clear reference - will be reallocated on demand.
buffer = null;
size = 0;
// add a chunk to the target buffer.
handleChunk( a );
}
}
/**
* Dispatch a chunk.
*
* @param chunk
* A chunk.
*/
abstract protected void handleChunk(E[] chunk);
@Override
public long flush() {
if (size == 0) {
// nothing is buffered.
return counter;
}
final int n = size;
// move everything onto the target buffer.
overflow();
counter += n;
// // tell the target buffer to flush itself.
// final long nwritten = target.flush();
if (INFO) {
log.info("wrote " + n + " elements, cumulative total=" + counter);
}
return counter;
}
private long counter = 0L;
@Override
public void reset() {
if(INFO) {
log.info("Resetting buffer state and counter.");
}
// will be reallocated on demand.
buffer = null;
// reset size to zero.
size = 0;
// clear the cumulative counter.
counter = 0L;
}
}