/*
* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the Classpath exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.btrace.aggregation;
import java.util.concurrent.atomic.AtomicLong;
/**
* Aggregation function that calculates a power-of-two frequency distribution of the values.
* <p>
*
* @author Christian Glencross
*/
class Quantize implements AggregationValue {
private static final int ZERO_INDEX = 64;
// Array of buckets, where each bucket contains a count of the number of
// occurrences in a certain range determined by a base 2 logarithmic function.
// For example:
// buckets[ZERO_INDEX - 3] counts numbers in the range -4 to -7 inclusive
// buckets[ZERO_INDEX - 2] counts -2s and -3s,
// buckets[ZERO_INDEX - 1] counts the number of -1s
// buckets[ZERO_INDEX] (the mid point of the array) counts the number of zeroes
// buckets[ZERO_INDEX + 1] counts the number of 1s
// buckets[ZERO_INDEX + 2] counts 2s and 3s,
// buckets[ZERO_INDEX + 3] counts numbers in the range 4 to 7
private AtomicLong[] buckets = new AtomicLong[ZERO_INDEX * 2];
public Quantize() {
super();
for (int i = 0; i < buckets.length; i++) {
buckets[i] = new AtomicLong();
}
}
/*
* (non-Javadoc)
*
* @see com.sun.btrace.aggregation.Aggregation#add(int)
*/
@Override
public void add(long data) {
int pos = getBucketIndex(data);
buckets[pos].incrementAndGet();
}
/**
* Computes log to base two of the value.
*
* @param the
* value for which to calculate the log, must be positive
*/
static int logBase2(long value) {
int pos = 0;
for (int off = (ZERO_INDEX >> 1); off > 0; off >>= 1) {
if (value >= 1L << off) {
value >>= off;
pos += off;
}
}
return pos;
}
/**
* This implementation of get value returns the label of the bucket containing the largest value. This is used by
* the {@link Aggregation#truncate(int)} method to sort values in the aggregation when determining which elements to
* delete.
*/
@Override
public long getValue() {
for (int i = buckets.length - 1; i >= 0; i--) {
long value = buckets[i].get();
if (value > 0) {
return getBucketLabel(i);
}
}
return 0;
}
/*
* (non-Javadoc)
*
* @see com.sun.btrace.aggregation.Aggregation#clear()
*/
@Override
public void clear() {
for (int i = 0; i < buckets.length; i++) {
buckets[i].set(0);
}
}
@Override
public HistogramData getData() {
int minIndex = buckets.length;
int maxIndex = -1;
for (int i = 0; i < buckets.length; i++) {
if (buckets[i].get() != 0) {
minIndex = Math.min(i, minIndex);
maxIndex = Math.max(i, maxIndex);
}
}
if (minIndex > maxIndex) {
// No data points
return null;
}
if (maxIndex < buckets.length - 1) {
maxIndex++;
}
if (minIndex > 0) {
minIndex--;
}
int rows = maxIndex - minIndex + 1;
long[] values = new long[rows];
long[] counts = new long[rows];
for (int i = 0; i < rows; i++) {
values[i] = getBucketLabel(minIndex + i);
counts[i] = buckets[minIndex + i].get();
}
return new HistogramData(values, counts);
}
private static int getBucketIndex(long data) {
if (data == 0) {
return ZERO_INDEX;
} else if (data > 0) {
return ZERO_INDEX + 1 + logBase2(data);
} else if (data == Integer.MIN_VALUE) {
// Special case since 0 - MIN_VALUE overflows
return 0;
} else {
return ZERO_INDEX - 1 - logBase2(0 - data);
}
}
private static long getBucketLabel(int index) {
if (index == ZERO_INDEX) {
return 0;
} else if (index == 0) {
return Long.MIN_VALUE;
} else if (index > ZERO_INDEX) {
index = index - ZERO_INDEX - 1;
return 1 << index;
} else {
index = ZERO_INDEX - index - 1;
return 0 - (1 << index);
}
}
}