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