package org.simantics.graph.representation; import java.io.Closeable; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; /** * Must be closed after using by invoking {@link #close()}. */ public class ByteFileReader implements Closeable { final char[] chars = new char[3*128]; final private File file; /** * May be null. If specified, it will be closed in * {@link #close()}. */ private InputStream stream; /** * A readable channel must always be specified since it is used for all * reading. Channel is never closed by this class. */ private ReadableByteChannel channel; final private ByteBuffer byteBuffer; final protected byte[] bytes; private int size; protected int byteIndex = 0; final protected ReadableByteChannel getChannel() { return channel; } final protected ByteBuffer getByteBuffer() { return byteBuffer; } final protected byte[] getBytes() { return bytes; } final protected String utf(byte[] bytes, int index, int target) { int i = 0; while(index < target) { int c = bytes[index++]&0xff; if(c <= 0x7F) { chars[i++] = (char)(c&0x7F); } else if (c > 0x07FF) { int c2 = bytes[index++]&0xff; int c3 = bytes[index++]&0xff; chars[i++] = (char)(((c&0xf)<<12) + ((c2&0x3f)<<6) + (c3&0x3f)); } else { int c2 = bytes[index++]&0xff; chars[i++] = (char)(((c&0x1f)<<6) + (c2&0x3f)); } } return new String(chars, 0, i); } final protected byte[] safeBytes(int amount) throws IOException { byte[] result = new byte[amount]; int has = size-byteIndex; if(amount >= has) { ReadableByteChannel c = channel; ByteBuffer bb = byteBuffer; System.arraycopy(bytes, byteIndex, result, 0, has); ByteBuffer bb2 = ByteBuffer.wrap(result); bb2.position(has); // For some peculiar reason this seems to avoid OOM with large blocks as compared to c.read(bb2 while(has < amount) { int todo = Math.min(amount-has, 65536); bb2.limit(has+todo); int got = c.read(bb2); if(got == -1) throw new IOException("Unexpected end-of-file"); has += got; } size = c.read(bb); bb.position(0); byteIndex = 0; } else { System.arraycopy(bytes, byteIndex, result, 0, amount); byteIndex += amount; } return result; } final protected int getByte() throws IOException { int has = size-byteIndex; int result; if(has == 0) { ReadableByteChannel c = channel; ByteBuffer bb = byteBuffer; size = c.read(bb); if(size == -1) { throw new EOFException("Unexpected end-of-file"); } bb.position(0); byteIndex = 0; if(size == 0) return -1; } result = bytes[byteIndex]; if(result < 0) result += 256; ++byteIndex; return result; } public int getDynamicUInt32() throws IOException { int length = getByte()&0xff; if(length >= 0x80) { if(length >= 0xc0) { if(length >= 0xe0) { if(length >= 0xf0) { length &= 0x0f; length += ((getByte()&0xff)<<3); length += ((getByte()&0xff)<<11); length += ((getByte()&0xff)<<19); length += 0x10204080; } else { length &= 0x1f; length += ((getByte()&0xff)<<4); length += ((getByte()&0xff)<<12); length += ((getByte()&0xff)<<20); length += 0x204080; } } else { length &= 0x3f; length += ((getByte()&0xff)<<5); length += ((getByte()&0xff)<<13); length += 0x4080; } } else { length &= 0x7f; length += ((getByte()&0xff)<<6); length += 0x80; } } return length; } final protected int safeInt() throws IOException { if(byteIndex >= (size-5)) { int result = 0; ReadableByteChannel c = channel; ByteBuffer bb = byteBuffer; if(byteIndex == size) { size = c.read(bb); if(size == -1) throw new EOFException("Unexpected end-of-file"); bb.position(0); byteIndex = 0; } result |= ((int)(bytes[byteIndex++]&0xff)<<24); if(byteIndex == size) { size = c.read(bb); if(size == -1) throw new EOFException("Unexpected end-of-file"); bb.position(0); byteIndex = 0; } result |= ((int)(bytes[byteIndex++]&0xff)<<16); if(byteIndex == size) { size = c.read(bb); if(size == -1) throw new EOFException("Unexpected end-of-file"); bb.position(0); byteIndex = 0; } result |= ((int)(bytes[byteIndex++]&0xff)<<8); if(byteIndex == size) { size = c.read(bb); if(size == -1) throw new EOFException("Unexpected end-of-file"); bb.position(0); byteIndex = 0; } result |= ((int)(bytes[byteIndex++]&0xff)<<0); if(byteIndex == size) { size = c.read(bb); bb.position(0); byteIndex = 0; } return result; } else { return ((bytes[byteIndex++]&0xff)<<24) | ((bytes[byteIndex++]&0xff)<<16) | ((bytes[byteIndex++]&0xff)<<8) | ((bytes[byteIndex++]&0xff)); } } final protected int getSize() { return size; } public ByteFileReader(File file, int size) throws IOException { bytes = new byte[size]; byteBuffer = ByteBuffer.wrap(bytes); this.file = file; FileInputStream fis = new FileInputStream(file); stream = fis; channel = fis.getChannel(); this.size = channel.read(byteBuffer); byteBuffer.position(0); } public ByteFileReader(FileInputStream stream, int size) throws IOException { this(stream, stream.getChannel(), size); } public ByteFileReader(InputStream stream, ReadableByteChannel channel, int size) throws IOException { bytes = new byte[size]; byteBuffer = ByteBuffer.wrap(bytes); this.file = null; this.stream = stream; this.channel = channel; this.size = channel.read(byteBuffer); byteBuffer.position(0); } public void close() throws IOException { if (stream != null) { stream.close(); stream = null; } } public void reset() throws IOException { if(file == null) throw new IllegalStateException("No file - cannot reset"); FileInputStream fis = new FileInputStream(file); stream = fis; channel = fis.getChannel(); this.size = channel.read(byteBuffer); byteBuffer.position(0); } }