/**
* Helios, OpenSource Monitoring
* Brought to you by the Helios Development Group
*
* Copyright 2013, Helios Development Group and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.helios.apmrouter.collections;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.LongBuffer;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
/**
* <p>Title: LongAccumulator</p>
* <p>Description: Direct buffered long accumulator</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.collections.LongAccumulator</code></p>
*/
public class LongAccumulator {
/** The read/write lock */
protected final ReentrantReadWriteLock readWriteLock;
/** The read lock */
protected final ReadLock readLock;
/** The write lock */
protected final WriteLock writeLock;
/** The capacity of the buffer */
protected final int size;
/** The accumulator byte buffer */
protected final ByteBuffer buffer;
/** The accumulator byte buffer as a long buffer */
protected final LongBuffer longBuffer;
/** The current number of longs in the buffer */
protected int currentSize = 0;
/** An empty long array constant */
public static final long[] EMPTY_ARR = new long[0];
/** The number of bytes in a long */
public static final int LONG_BYTES = 8;
/**
* Creates a new LongAccumulator
* @param direct true for an off-heap buffer, false for a heap-buffer
* @param fair true for a fair lock, false for an unfair lock
* @param size the number of longs in this buffer
*/
public LongAccumulator(boolean direct, boolean fair, int size) {
readWriteLock = new ReentrantReadWriteLock(fair);
readLock = readWriteLock.readLock();
writeLock = readWriteLock.writeLock();
this.size = size;
buffer = direct ? ByteBuffer.allocateDirect(size*LONG_BYTES) : ByteBuffer.allocate(size*LONG_BYTES);
log("Buff:" + buffer);
longBuffer = buffer.asLongBuffer();
log("LBuff:" + longBuffer);
}
/**
* Returns the content of the buffer as a long array
* @return the content of the buffer as a long array
*/
public long[] getLongs() {
readLock.lock();
try {
if(currentSize==0) return EMPTY_ARR;
long[] arr = new long[currentSize];
for(int i = 0; i < currentSize; i++) {
arr[i]= longBuffer.get(i);
}
return arr;
} finally {
readLock.unlock();
}
}
/**
* Clears the buffer, setting the current size to zero.
*/
public void clear() {
writeLock.lock();
try {
if(currentSize==0) return;
buffer.clear();
currentSize = 0;
} finally {
writeLock.unlock();
}
}
/**
* Returns the current number of longs in the buffer
* @return the current number of longs in the buffer
*/
public int getCurrentSize() {
readLock.lock();
try {
return currentSize;
} finally {
readLock.unlock();
}
}
/**
* {@inheritDoc}
* @see java.lang.Object#toString()
*/
public String toString() {
StringBuilder b = new StringBuilder("Long Accumulator[ ");
long[] longs = getLongs();
b.append("\n\tSize:").append(longs.length);
b.append("\n\tContent:").append(Arrays.toString(longs));
b.append("\n]");
return b.toString();
}
/**
* Pushes the passed longs into the end of the buffer, compacting the existing ones to the left, and dropping the overflow.
* @param values the longs to push into the buffer
*/
public void push(long...values) {
if(values==null || values.length==0) return;
writeLock.lock();
try {
for(long value: values) {
if(size==currentSize) {
longBuffer.position(1);
longBuffer.compact();
} else {
currentSize++;
longBuffer.position(currentSize-1);
}
longBuffer.put(value);
}
} finally {
writeLock.unlock();
}
}
/**
* Returns the minimum value in the buffer, or zero if the buffer is empty.
* @return the minimum value in the buffer
*/
public long min() {
long[] arr = getLongs();
if(arr.length==0) return 0;
Arrays.sort(arr);
return arr[0];
}
/**
* Returns the maximum value in the buffer, or zero if the buffer is empty.
* @return the maximum value in the buffer
*/
public long max() {
long[] arr = getLongs();
if(arr.length==0) return 0;
Arrays.sort(arr);
return arr[arr.length-1];
}
/**
* Returns the average value in the buffer, or zero if the buffer is empty.
* @return the average value in the buffer
*/
public long avg() {
long[] arr = getLongs();
if(arr.length==0) return 0;
BigInteger total = BigInteger.valueOf(0);
for(long l: arr) {
total = total.add(BigInteger.valueOf(l));
}
return avg(total, arr.length);
}
/**
* Calcs a long average
* @param total The total sum of the longs
* @param length The number of longs
* @return the average
*/
protected long avg(BigInteger total, int length) {
BigInteger count = BigInteger.valueOf(length);
if(total.doubleValue()==0 || count.doubleValue()==0) return 0;
return total.divide(count).longValue();
}
public static void main(String[] args) {
LongAccumulator la = new LongAccumulator(true, false, 10000);
Random random = new Random(System.currentTimeMillis());
for(int i = 0; i < la.size*2; i++) {
//la.push(Math.abs(random.nextInt(100)));
la.push(Math.abs(random.nextLong()));
}
log(la);
log("Min:" + la.min());
log("Max:" + la.max());
log("Avg:" + la.avg());
}
public static void log(Object msg) {
System.out.println(msg);
}
}