/******************************************************************************* * Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor. * Copyright (c) 2011 The OpenNMS Group, Inc. * * This library 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 library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *******************************************************************************/ package org.jrobin.core.jrrd; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import org.jrobin.core.RrdException; /** * This class is a quick hack to read information from an RRD file. Writing * to RRD files is not currently supported. As I said, this is a quick hack. * Some thought should be put into the overall design of the file IO. * <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$ */ public class RRDFile implements Constants { boolean bigEndian; boolean debug; int alignment; RandomAccessFile ras; byte[] buffer; RRDFile(String name) throws IOException, RrdException { this(new File(name)); } RRDFile(File file) throws IOException, RrdException { ras = new RandomAccessFile(file, "r"); buffer = new byte[128]; this.debug = false; initDataLayout(file); } private void initDataLayout(File file) throws IOException, RrdException { if (file.exists()) { // Load the data formats from the file int bytes = ras.read(buffer, 0, 24); if (bytes < 24) { throw new RrdException("Invalid RRD file"); } int index; if ((index = indexOf(FLOAT_COOKIE_BIG_ENDIAN, buffer)) != -1) { bigEndian = true; } else if ((index = indexOf(FLOAT_COOKIE_LITTLE_ENDIAN, buffer)) != -1) { bigEndian = false; } else { throw new RrdException("Invalid RRD file"); } switch (index) { case 12: alignment = 4; break; case 16: alignment = 8; break; default : throw new RuntimeException("Unsupported architecture - neither 32-bit nor 64-bit, or maybe the file is corrupt"); } } else { // Default to data formats for this hardware architecture } ras.seek(0); // Reset file pointer to start of file } private int indexOf(byte[] pattern, byte[] array) { return (new String(array)).indexOf(new String(pattern)); } boolean isBigEndian() { return bigEndian; } int getAlignment() { return alignment; } double readDouble() throws IOException, RrdException { if(debug) { System.out.print("Read 8 bytes (Double) from offset "+ras.getFilePointer()+":"); } //double value; byte[] tx = new byte[8]; if(ras.read(buffer, 0, 8) != 8) { throw new RrdException("Invalid RRD file"); } if (bigEndian) { tx = buffer; } else { for (int i = 0; i < 8; i++) { tx[7 - i] = buffer[i]; } } DataInputStream reverseDis = new DataInputStream(new ByteArrayInputStream(tx)); Double result = reverseDis.readDouble(); if(this.debug) { System.out.println(result); } return result; } int readInt() throws IOException, RrdException { return readInt(false); } /** * Reads the next integer (4 or 8 bytes depending on alignment), advancing the file pointer * and returns it * If the alignment is 8-bytes (64-bit), then 8 bytes are read, but only the lower 4-bytes (32-bits) are * returned. The upper 4 bytes are ignored. * * @return the 32-bit integer read from the file * @throws IOException - A file access error * @throws RrdException - Not enough bytes were left in the file to read the integer. */ int readInt(boolean dump) throws IOException, RrdException { //An integer is "alignment" bytes long - 4 bytes on 32-bit, 8 on 64-bit. if(this.debug) { System.out.print("Read "+alignment+" bytes (int) from offset "+ras.getFilePointer()+":"); } if(ras.read(buffer, 0, alignment) != alignment) { throw new RrdException("Invalid RRD file"); } int value; if (bigEndian) { if(alignment == 8) { //For big-endian, the low 4-bytes of the 64-bit integer are the last 4 bytes value = (0xFF & buffer[7]) | ((0xFF & buffer[6]) << 8) | ((0xFF & buffer[5]) << 16) | ((0xFF & buffer[4]) << 24); } else { value = (0xFF & buffer[3]) | ((0xFF & buffer[2]) << 8) | ((0xFF & buffer[1]) << 16) | ((0xFF & buffer[0]) << 24); } } else { //For little-endian, there's no difference between 4 and 8 byte alignment. // The first 4 bytes are the low end of a 64-bit number value = (0xFF & buffer[0]) | ((0xFF & buffer[1]) << 8) | ((0xFF & buffer[2]) << 16) | ((0xFF & buffer[3]) << 24); } if(this.debug) { System.out.println(value); } return value; } String readString(int maxLength) throws IOException, RrdException { if(this.debug) { System.out.print("Read "+maxLength+" bytes (string) from offset "+ras.getFilePointer()+":"); } maxLength = ras.read(buffer, 0, maxLength); if(maxLength == -1) { throw new RrdException("Invalid RRD file"); } String result = new String(buffer, 0, maxLength).trim(); if(this.debug) { System.out.println( result +":"); } return result; } void skipBytes(final int n) throws IOException { int bytesSkipped = ras.skipBytes(n); if(this.debug) { System.out.println("Skipping "+bytesSkipped+" bytes"); } } int align(int boundary) throws IOException { int skip = (int) (boundary - (ras.getFilePointer() % boundary)) % boundary; if (skip != 0) { skip = ras.skipBytes(skip); } if(this.debug) { System.out.println("Aligning to boundary "+ boundary +". Offset is now "+ras.getFilePointer()); } return skip; } int align() throws IOException { return align(alignment); } long info() throws IOException { return ras.getFilePointer(); } long getFilePointer() throws IOException { return ras.getFilePointer(); } void close() throws IOException { ras.close(); } }