/* * Copyright (c) 2012 Eike Stepper (Berlin, Germany) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Eike Stepper - initial API and implementation */ package org.eclipse.net4j.util.io; import java.io.Closeable; import java.io.DataInput; import java.io.DataInputStream; import java.io.DataOutput; import java.io.EOFException; import java.io.File; import java.io.FileNotFoundException; import java.io.Flushable; import java.io.IOException; import java.io.RandomAccessFile; import java.io.UTFDataFormatException; /** * @author Eike Stepper * @since 3.3 */ public class DataInputOutputFile implements DataInput, DataOutput, Flushable, Closeable { private RandomAccessFile raf; private long position; private boolean eof; private byte buffer[]; private long start; private long end; private int used; private boolean modified; public DataInputOutputFile(File file, String mode) throws FileNotFoundException { this(file, mode, 2 * 4096); } public DataInputOutputFile(File file, String mode, int bufferSize) throws FileNotFoundException { raf = new RandomAccessFile(file, mode); buffer = new byte[bufferSize]; } public void close() throws IOException { if (raf != null) { flush(); raf.close(); raf = null; } } public void flush() throws IOException { if (modified) { modified = false; raf.seek(start); raf.write(buffer, 0, used); } } public void seek(long filePointer) throws IOException { if (filePointer >= start && filePointer < end) { position = filePointer; } else { if (modified) { flush(); } start = filePointer; position = filePointer; raf.seek(filePointer); used = raf.read(buffer, 0, buffer.length); if (used <= 0) { used = 0; eof = true; } else { eof = false; } end = start + used; } } public long getFilePointer() throws IOException { return position; } public long length() throws IOException { long fileLength = raf.length(); if (fileLength < end) { return end; } return fileLength; } public int skipBytes(int n) throws IOException { seek(getFilePointer() + n); return n; } public int read() throws IOException { if (position < end) { int offset = (int)(position - start); position++; return buffer[offset] & 0xff; } if (eof) { return -1; } seek(position); return read(); } public int read(byte[] array, int offset, int length) throws IOException { if (eof) { return -1; } int available = (int)(end - position); if (available < 1) { seek(position); return read(array, offset, length); } int bytesToCopy = Math.min(length, available); System.arraycopy(buffer, (int)(position - start), array, offset, bytesToCopy); position += bytesToCopy; if (bytesToCopy < length) { int remaining = length - bytesToCopy; if (remaining > buffer.length) { raf.seek(position); remaining = raf.read(array, offset + bytesToCopy, length - bytesToCopy); } else { seek(position); if (eof) { remaining = -1; } else { remaining = remaining > used ? used : remaining; System.arraycopy(buffer, 0, array, offset + bytesToCopy, remaining); } } if (remaining > 0) { position += remaining; return bytesToCopy + remaining; } } return bytesToCopy; } public int read(byte[] array) throws IOException { return read(array, 0, array.length); } public byte[] readBytes(int count) throws IOException { byte[] array = new byte[count]; readFully(array); return array; } public void readFully(byte[] array) throws IOException { readFully(array, 0, array.length); } public void readFully(byte[] array, int offset, int length) throws IOException { int n = 0; while (n < length) { int count = this.read(array, offset + n, length - n); if (count < 0) { throw new EOFException(); } n += count; } } public boolean readBoolean() throws IOException { int ch = this.read(); if (ch < 0) { throw new EOFException(); } return ch != 0; } public byte readByte() throws IOException { int ch = this.read(); if (ch < 0) { throw new EOFException(); } return (byte)ch; } public int readUnsignedByte() throws IOException { int ch = this.read(); if (ch < 0) { throw new EOFException(); } return ch; } public short readShort() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) { throw new EOFException(); } return (short)((ch1 << 8) + ch2); } public int readUnsignedShort() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) { throw new EOFException(); } return (ch1 << 8) + ch2; } public char readChar() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) { throw new EOFException(); } return (char)((ch1 << 8) + ch2); } public int readInt() throws IOException { int ch1 = this.read(); int ch2 = this.read(); int ch3 = this.read(); int ch4 = this.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) { throw new EOFException(); } return (ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4; } public long readLong() throws IOException { return ((long)readInt() << 32) + (readInt() & 0xFFFFFFFFL); } public float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); } public double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); } public String readLine() throws IOException { StringBuffer input = new StringBuffer(); int c = -1; boolean eol = false; while (!eol) { switch (c = read()) { case -1: case '\n': eol = true; break; case '\r': eol = true; long cur = getFilePointer(); if (read() != '\n') { seek(cur); } break; default: input.append((char)c); } } if (c == -1 && input.length() == 0) { return null; } return input.toString(); } public String readUTF() throws IOException { return DataInputStream.readUTF(this); } public void write(int byteValue) throws IOException { if (position < end) { int filePointer = (int)(position - start); buffer[filePointer] = (byte)byteValue; modified = true; position++; } else { if (used != buffer.length) { int filePointer = (int)(position - start); position++; buffer[filePointer] = (byte)byteValue; modified = true; end++; used++; } else { seek(position); write(byteValue); } } } public void write(byte[] array) throws IOException { write(array, 0, array.length); } public void write(byte[] array, int offset, int length) throws IOException { if (length < buffer.length) { int freeInBuffer = 0; int bytesToCopy = 0; if (position >= start) { freeInBuffer = (int)(start + buffer.length - position); } if (freeInBuffer > 0) { bytesToCopy = freeInBuffer > length ? length : freeInBuffer; System.arraycopy(array, offset, buffer, (int)(position - start), bytesToCopy); modified = true; long newBufferEnd = position + bytesToCopy; end = newBufferEnd > end ? newBufferEnd : end; used = (int)(end - start); position += bytesToCopy; } if (bytesToCopy < length) { seek(position); System.arraycopy(array, offset + bytesToCopy, buffer, (int)(position - start), length - bytesToCopy); modified = true; long newBufferEnd = position + (length - bytesToCopy); end = newBufferEnd > end ? newBufferEnd : end; used = (int)(end - start); position += length - bytesToCopy; } } else { if (modified) { flush(); } raf.seek(position); raf.write(array, offset, length); position += length; start = position; used = 0; end = start + used; } } public void writeBoolean(boolean value) throws IOException { write(value ? 1 : 0); } public void writeByte(int value) throws IOException { write(value); } public void writeShort(int value) throws IOException { write(value >>> 8 & 0xFF); write(value & 0xFF); } public void writeChar(int value) throws IOException { write(value >>> 8 & 0xFF); write(value & 0xFF); } public void writeInt(int value) throws IOException { write(value >>> 24 & 0xFF); write(value >>> 16 & 0xFF); write(value >>> 8 & 0xFF); write(value & 0xFF); } public void writeLong(long value) throws IOException { write((int)(value >>> 56) & 0xFF); write((int)(value >>> 48) & 0xFF); write((int)(value >>> 40) & 0xFF); write((int)(value >>> 32) & 0xFF); write((int)(value >>> 24) & 0xFF); write((int)(value >>> 16) & 0xFF); write((int)(value >>> 8) & 0xFF); write((int)value & 0xFF); } public void writeFloat(float value) throws IOException { writeInt(Float.floatToIntBits(value)); } public void writeDouble(double value) throws IOException { writeLong(Double.doubleToLongBits(value)); } public void writeBytes(String value) throws IOException { int length = value.length(); for (int i = 0; i < length; i++) { write((byte)value.charAt(i)); } } public void writeChars(String value) throws IOException { int length = value.length(); for (int i = 0; i < length; i++) { int c = value.charAt(i); write(c >>> 8 & 0xFF); write(c & 0xFF); } } public void writeUTF(String value) throws IOException { int length = value.length(); int utf = 0; for (int i = 0; i < length; i++) { int c = value.charAt(i); if (c >= 0x0001 && c <= 0x007F) { utf++; } else if (c > 0x07FF) { utf += 3; } else { utf += 2; } } if (utf > 65535) { throw new UTFDataFormatException(); } write(utf >>> 8 & 0xFF); write(utf & 0xFF); for (int i = 0; i < length; i++) { int c = value.charAt(i); if (c >= 0x0001 && c <= 0x007F) { write(c); } else if (c > 0x07FF) { write(0xE0 | c >> 12 & 0x0F); write(0x80 | c >> 6 & 0x3F); write(0x80 | c & 0x3F); } else { write(0xC0 | c >> 6 & 0x1F); write(0x80 | c & 0x3F); } } } }