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()); } }