/*
* Copyright (c) 2007, 2011, 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.tools.visualvm.charts.xy;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import org.netbeans.lib.profiler.charts.Timeline;
import org.netbeans.lib.profiler.charts.xy.synchronous.SynchronousXYItem;
/**
*
* @author Jiri Sedlacek
*/
public class XYStorage implements Timeline {
private static final String SNAPSHOT_HEADER = "XYStorageSnapshot"; // NOI18N
private static final int SNAPSHOT_VERSION = 1;
public static final long NO_VALUE = Long.MIN_VALUE - 1;
private final int valuesLimit;
private final int bufferStep;
private int valuesCount;
private long[] timestamps;
private long[][] values;
private int cycleIndex;
public XYStorage(int valuesLimit, int bufferStep) {
this.valuesLimit = valuesLimit;
this.bufferStep = bufferStep;
initialize();
}
public synchronized SynchronousXYItem addItem(String name, long minValue, long maxValue) {
final int itemIndex = addItemImpl();
return new XYItem(name, minValue, maxValue) {
public long getYValue(int valueIndex) {
return getValue(itemIndex, valueIndex);
}
};
}
public synchronized void addValues(long timestamp, long[] values) {
updateStorage();
setTimestamp(Math.min(valuesCount, valuesLimit - 1), timestamp);
for (int i = 0; i < values.length; i++)
setValue(i, Math.min(valuesCount, valuesLimit - 1), values[i]);
if (valuesCount < valuesLimit) valuesCount++;
}
public synchronized void saveValues(OutputStream os) throws IOException {
DataOutputStream dos = null;
try {
int icount = values.length;
int vcount = getTimestampsCount();
dos = new DataOutputStream(os);
dos.writeUTF(SNAPSHOT_HEADER); // Snapshot format
dos.writeInt(SNAPSHOT_VERSION); // Snapshot version
dos.writeInt(icount); // Items count
dos.writeInt(vcount); // Values count
for (int vidx = 0; vidx < vcount; vidx++) {
dos.writeLong(getTimestamp(vidx));
for (int iidx = 0; iidx < icount; iidx++)
dos.writeLong(getValue(iidx, vidx));
}
} finally {
if (dos != null) dos.close();
}
}
public synchronized void loadValues(InputStream is) throws IOException {
DataInputStream dis = null;
try {
dis = new DataInputStream(is);
if (!SNAPSHOT_HEADER.equals(dis.readUTF()))
throw new IOException("Unknown snapshot format"); // NOI18N
if (SNAPSHOT_VERSION != dis.readInt())
throw new IOException("Unsupported snapshot version"); // NOI18N
if (values.length != dis.readInt())
throw new IOException("Snapshot doesn't match number of items"); // NOI18N
int vcount = dis.readInt();
long[] vals = new long[values.length];
for (int vidx = 0; vidx < vcount; vidx++) {
long timestamp = dis.readLong();
for (int iidx = 0; iidx < vals.length; iidx++)
vals[iidx] = dis.readLong();
addValues(timestamp, vals);
}
} finally {
if (dis != null) dis.close();
}
}
private void initialize() {
reset();
}
private void reset() {
valuesCount = 0;
cycleIndex = 0;
timestamps = null;
if (values != null) {
if (values.length == 0) values = null;
else for (int i = 0; i < values.length; i++)
values[i] = new long[bufferStep];
}
}
private int addItemImpl() {
int itemIndex = 0;
if (timestamps == null) {
timestamps = new long[bufferStep];
values = new long[1][];
values[0] = new long[bufferStep];
} else {
values = extendArray(values, 1);
itemIndex = values.length - 1;
values[itemIndex] = new long[timestamps.length];
if (values[itemIndex].length > 0)
Arrays.fill(values[itemIndex], NO_VALUE);
}
return itemIndex;
}
private int getIndex(int index) {
if (cycleIndex != 0) {
index += cycleIndex;
if (index >= valuesCount) index -= valuesCount;
}
return index;
}
public int getTimestampsCount() {
return valuesCount;
}
private void setTimestamp(int index, long value) {
timestamps[getIndex(index)] = value;
}
public long getTimestamp(int index) {
return timestamps[getIndex(index)];
}
private void setValue(int itemIndex, int valueIndex, long value) {
values[itemIndex][getIndex(valueIndex)] = value;
}
private long getValue(int itemIndex, int valueIndex) {
return values[itemIndex][getIndex(valueIndex)];
}
boolean isFull() {
return valuesCount == valuesLimit;
}
private void updateStorage() {
int bufferSize = timestamps.length;
if (valuesCount == bufferSize && bufferSize < valuesLimit) {
int extent = Math.min(bufferStep, valuesLimit - bufferSize);
timestamps = extendArray(timestamps, extent);
for (int i = 0; i < values.length; i++)
values[i] = extendArray(values[i], extent);
cycleIndex = 0;
} else if (isFull()) {
cycleIndex++;
if (cycleIndex == valuesLimit) cycleIndex = 0;
}
}
private static long[] extendArray(long[] array, int extraLength) {
int originalLength = array.length;
long[] newArray = new long[originalLength + extraLength];
System.arraycopy(array, 0, newArray, 0, originalLength);
return newArray;
}
private static long[][] extendArray(long[][] array, int extraLength) {
int originalLength = array.length;
long[][] newArray = new long[originalLength + extraLength][];
System.arraycopy(array, 0, newArray, 0, originalLength);
return newArray;
}
}