1 package org.simantics.graph.representation;
3 import java.io.Closeable;
4 import java.io.EOFException;
6 import java.io.FileInputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.UTFDataFormatException;
10 import java.nio.ByteBuffer;
11 import java.nio.channels.ReadableByteChannel;
14 * Must be closed after using by invoking {@link #close()}.
16 public class ByteFileReader implements Closeable {
18 final char[] chars = new char[3*128];
20 final private File file;
23 * May be <code>null</code>. If specified, it will be closed in
26 private InputStream stream;
29 * A readable channel must always be specified since it is used for all
30 * reading. Channel is never closed by this class.
32 private ReadableByteChannel channel;
34 final private ByteBuffer byteBuffer;
36 final protected byte[] bytes;
39 protected int byteIndex = 0;
41 final protected ReadableByteChannel getChannel() {
45 final protected ByteBuffer getByteBuffer() {
49 final protected byte[] getBytes() {
54 final protected String utf(byte[] bytearr, int index, int target) throws UTFDataFormatException {
55 // Copied from DataInputStream
56 int utflen = target - index;
57 char[] chararr = utflen > chars.length ? new char[utflen] : chars;
63 while (count < target) {
64 c = (int) bytearr[count] & 0xff;
67 chararr[chararr_count++]=(char)c;
70 while (count < target) {
71 c = (int) bytearr[count] & 0xff;
73 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
76 chararr[chararr_count++]=(char)c;
79 /* 110x xxxx 10xx xxxx*/
82 throw new UTFDataFormatException(
83 "malformed input: partial character at end (" + (count-index) + " > " + utflen + ")");
84 char2 = (int) bytearr[count-1];
85 if ((char2 & 0xC0) != 0x80)
86 throw new UTFDataFormatException(
87 "malformed input around byte " + count);
88 chararr[chararr_count++]=(char)(((c & 0x1F) << 6) |
92 /* 1110 xxxx 10xx xxxx 10xx xxxx */
95 throw new UTFDataFormatException(
96 "malformed input: partial character at end (" + (count-index) + " > " + utflen + ")");
97 char2 = (int) bytearr[count-2];
98 char3 = (int) bytearr[count-1];
99 if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
100 throw new UTFDataFormatException(
101 "malformed input around byte " + (count-1));
102 chararr[chararr_count++]=(char)(((c & 0x0F) << 12) |
103 ((char2 & 0x3F) << 6) |
104 ((char3 & 0x3F) << 0));
107 /* 10xx xxxx, 1111 xxxx */
108 throw new UTFDataFormatException(
109 "malformed input around byte " + count);
112 // The number of chars produced may be less than utflen
113 return new String(chararr, 0, chararr_count);
116 final protected byte[] safeBytes(int amount) throws IOException {
117 byte[] result = new byte[amount];
118 int has = size-byteIndex;
120 ReadableByteChannel c = channel;
121 ByteBuffer bb = byteBuffer;
122 System.arraycopy(bytes, byteIndex, result, 0, has);
123 ByteBuffer bb2 = ByteBuffer.wrap(result);
125 // For some peculiar reason this seems to avoid OOM with large blocks as compared to c.read(bb2
126 while(has < amount) {
127 int todo = Math.min(amount-has, 65536);
129 int got = c.read(bb2);
130 if(got == -1) throw new IOException("Unexpected end-of-file");
132 // For some unknown reason this is needed!
133 // Spec indicates that read would increment position but it does not.
140 System.arraycopy(bytes, byteIndex, result, 0, amount);
148 final protected int getByte() throws IOException {
149 int has = size-byteIndex;
152 ReadableByteChannel c = channel;
153 ByteBuffer bb = byteBuffer;
156 throw new EOFException("Unexpected end-of-file");
163 result = bytes[byteIndex++] & 0xff;
167 public int getDynamicUInt32() throws IOException {
168 int length = getByte();
174 length += (getByte()<<3);
175 length += (getByte()<<11);
176 length += (getByte()<<19);
177 length += 0x10204080;
181 length += (getByte()<<4);
182 length += (getByte()<<12);
183 length += (getByte()<<20);
189 length += (getByte()<<5);
190 length += (getByte()<<13);
196 length += (getByte()<<6);
203 final protected int safeInt() throws IOException {
205 byte[] bytes = this.bytes;
207 if(byteIndex >= (size-5)) {
209 ReadableByteChannel c = channel;
210 ByteBuffer bb = byteBuffer;
211 if(byteIndex == size) {
213 if(size == -1) throw new EOFException("Unexpected end-of-file");
217 result |= ((int)(bytes[byteIndex++]&0xff)<<24);
218 if(byteIndex == size) {
220 if(size == -1) throw new EOFException("Unexpected end-of-file");
224 result |= ((int)(bytes[byteIndex++]&0xff)<<16);
225 if(byteIndex == size) {
227 if(size == -1) throw new EOFException("Unexpected end-of-file");
231 result |= ((int)(bytes[byteIndex++]&0xff)<<8);
232 if(byteIndex == size) {
234 if(size == -1) throw new EOFException("Unexpected end-of-file");
238 result |= ((int)(bytes[byteIndex++]&0xff));
239 if(byteIndex == size) {
246 return ((bytes[byteIndex++]&0xff)<<24) | ((bytes[byteIndex++]&0xff)<<16) | ((bytes[byteIndex++]&0xff)<<8) | ((bytes[byteIndex++]&0xff));
251 final protected int getSize() {
255 public ByteFileReader(File file, int size) throws IOException {
257 bytes = new byte[size];
258 byteBuffer = ByteBuffer.wrap(bytes);
262 FileInputStream fis = new FileInputStream(file);
264 channel = fis.getChannel();
265 this.size = channel.read(byteBuffer);
266 byteBuffer.position(0);
270 public ByteFileReader(FileInputStream stream, int size) throws IOException {
271 this(stream, stream.getChannel(), size);
274 public ByteFileReader(InputStream stream, ReadableByteChannel channel, int size) throws IOException {
276 bytes = new byte[size];
277 byteBuffer = ByteBuffer.wrap(bytes);
280 this.stream = stream;
281 this.channel = channel;
282 this.size = channel.read(byteBuffer);
283 byteBuffer.position(0);
287 public void close() throws IOException {
288 if (stream != null) {
294 public void reset() throws IOException {
296 if(file == null) throw new IllegalStateException("No file - cannot reset");
298 FileInputStream fis = new FileInputStream(file);
300 channel = fis.getChannel();
301 this.size = channel.read(byteBuffer);
302 byteBuffer.position(0);