package org.rrd4j.core.jrrd;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
/**
* This class is used read information from an RRD file. Writing
* to RRD files is not currently supported. It uses NIO's RandomAccessFile to read the file
* <p/>
* Currently this can read RRD files that were generated on Solaris (Sparc)
* and Linux (x86).
*
* @author <a href="mailto:ciaran@codeloop.com">Ciaran Treanor</a>
* @version $Revision: 1.1 $
*/
class RRDFile implements Constants {
private int alignment;
private int longSize = 4;
private final FileInputStream underlying;
private final MappedByteBuffer mappedByteBuffer;
private ByteOrder order;
RRDFile(String name) throws IOException {
this(new File(name));
}
RRDFile(File file) throws IOException {
long len = file.length();
if (len > Integer.MAX_VALUE) {
throw new IllegalArgumentException(
"RRDFile cannot read files larger than 2**31 because of limitations of java.nio.ByteBuffer");
}
boolean ok = false;
try {
underlying = new FileInputStream(file);
mappedByteBuffer = underlying.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, len);
initDataLayout(file);
ok = true;
} finally {
if (!ok) {
try {
close();
} catch (Throwable ignored) {
}
// and then rethrow
}
}
}
private void initDataLayout(File file) throws IOException {
if (file.exists()) { // Load the data formats from the file
byte[] buffer = new byte[32];
mappedByteBuffer.get(buffer);
ByteBuffer bbuffer = ByteBuffer.wrap(buffer);
int index;
if ((index = indexOf(FLOAT_COOKIE_BIG_ENDIAN, buffer)) != -1) {
order = ByteOrder.BIG_ENDIAN;
}
else if ((index = indexOf(FLOAT_COOKIE_LITTLE_ENDIAN, buffer))
!= -1) {
order = ByteOrder.LITTLE_ENDIAN;
}
else {
throw new IOException("Invalid RRD file");
}
mappedByteBuffer.order(order);
bbuffer.order(order);
switch (index) {
case 12:
alignment = 4;
break;
case 16:
alignment = 8;
break;
default:
throw new RuntimeException("Unsupported architecture");
}
bbuffer.position(index + 8);
//We cannot have dsCount && rracount == 0
//If one is 0, it's a 64 bits rrd
int int1 = bbuffer.getInt(); //Should be dsCount in ILP32
int int2 = bbuffer.getInt(); //Should be rraCount in ILP32
if(int1 == 0 || int2 ==0) {
longSize = 8;
}
}
else { // Default to data formats for this hardware architecture
}
// Reset file pointer to start of file
mappedByteBuffer.rewind();
}
private int indexOf(byte[] pattern, byte[] array) {
return (new String(array)).indexOf(new String(pattern));
}
boolean isBigEndian() {
return order == ByteOrder.BIG_ENDIAN;
}
int getAlignment() {
return alignment;
}
double readDouble() throws IOException {
return mappedByteBuffer.getDouble();
}
int readInt() throws IOException {
return mappedByteBuffer.getInt();
}
int readLong() throws IOException {
if(longSize == 4) {
return mappedByteBuffer.getInt();
}
else {
return (int) mappedByteBuffer.getLong();
}
}
String readString(int maxLength) throws IOException {
byte[] array = new byte[maxLength];
mappedByteBuffer.get(array);
return new String(array, 0, maxLength).trim();
}
void skipBytes(int n) throws IOException {
mappedByteBuffer.position(mappedByteBuffer.position() + n);
}
int align(int boundary) throws IOException {
int skip = (int) (boundary - (mappedByteBuffer.position() % boundary)) % boundary;
if (skip != 0) {
mappedByteBuffer.position(mappedByteBuffer.position() + skip);
}
return skip;
}
int align() throws IOException {
return align(alignment);
}
long info() throws IOException {
return mappedByteBuffer.position();
}
long getFilePointer() throws IOException {
return mappedByteBuffer.position();
}
void close() throws IOException {
if (underlying != null) {
underlying.close();
}
}
void read(ByteBuffer bb) throws IOException{
int count = bb.remaining();
bb.put((ByteBuffer) mappedByteBuffer.duplicate().limit(mappedByteBuffer.position() + count));
mappedByteBuffer.position(mappedByteBuffer.position() + count);
}
UnivalArray getUnivalArray(int size) throws IOException {
return new UnivalArray(this, size);
}
/**
* @return the long size in bits for this file
*/
int getBits() {
return longSize * 8;
}
public void seek(long position) {
mappedByteBuffer.position((int) position);
}
public void seekToEndOfFile() {
mappedByteBuffer.position(mappedByteBuffer.limit());
}
}