28 import java.io.DataInput; |
28 import java.io.DataInput; |
29 import java.io.EOFException; |
29 import java.io.EOFException; |
30 import java.io.File; |
30 import java.io.File; |
31 import java.io.IOException; |
31 import java.io.IOException; |
32 import java.io.RandomAccessFile; |
32 import java.io.RandomAccessFile; |
33 import java.nio.charset.Charset; |
|
34 |
33 |
35 public final class RecordingInput implements DataInput, AutoCloseable { |
34 public final class RecordingInput implements DataInput, AutoCloseable { |
36 |
35 |
37 public static final byte STRING_ENCODING_NULL = 0; |
|
38 public static final byte STRING_ENCODING_EMPTY_STRING = 1; |
|
39 public static final byte STRING_ENCODING_CONSTANT_POOL = 2; |
|
40 public static final byte STRING_ENCODING_UTF8_BYTE_ARRAY = 3; |
|
41 public static final byte STRING_ENCODING_CHAR_ARRAY = 4; |
|
42 public static final byte STRING_ENCODING_LATIN1_BYTE_ARRAY = 5; |
|
43 |
36 |
44 private final static int DEFAULT_BLOCK_SIZE = 16 * 1024 * 1024; |
37 private final static int DEFAULT_BLOCK_SIZE = 16 * 1024 * 1024; |
45 private final static Charset UTF8 = Charset.forName("UTF-8"); |
|
46 private final static Charset LATIN1 = Charset.forName("ISO-8859-1"); |
|
47 |
38 |
48 private static final class Block { |
39 private static final class Block { |
49 private byte[] bytes = new byte[0]; |
40 private byte[] bytes = new byte[0]; |
50 private long blockPosition; |
41 private long blockPosition; |
51 |
42 private int size; |
52 boolean contains(long position) { |
43 boolean contains(long position) { |
53 return position >= blockPosition && position < blockPosition + bytes.length; |
44 return position >= blockPosition && position < blockPosition + size; |
54 } |
45 } |
55 |
46 |
56 public void read(RandomAccessFile file, int amount) throws IOException { |
47 public void read(RandomAccessFile file, int amount) throws IOException { |
57 blockPosition = file.getFilePointer(); |
48 blockPosition = file.getFilePointer(); |
58 // reuse byte array, if possible |
49 // reuse byte array, if possible |
59 if (amount != bytes.length) { |
50 if (amount > bytes.length) { |
60 bytes = new byte[amount]; |
51 bytes = new byte[amount]; |
61 } |
52 } |
62 file.readFully(bytes); |
53 this.size = amount; |
|
54 file.readFully(bytes, 0 , amount); |
63 } |
55 } |
64 |
56 |
65 public byte get(long position) { |
57 public byte get(long position) { |
66 return bytes[(int) (position - blockPosition)]; |
58 return bytes[(int) (position - blockPosition)]; |
67 } |
59 } |
68 } |
60 } |
69 |
61 |
70 private final RandomAccessFile file; |
62 private final RandomAccessFile file; |
71 private final long size; |
63 private final String filename; |
72 private Block currentBlock = new Block(); |
64 private Block currentBlock = new Block(); |
73 private Block previousBlock = new Block(); |
65 private Block previousBlock = new Block(); |
74 private long position; |
66 private long position; |
75 private final int blockSize; |
67 private final int blockSize; |
76 |
68 private long size = -1; // Fail fast if setSize(...) has not been called before parsing |
77 private RecordingInput(File f, int blockSize) throws IOException { |
69 |
78 this.size = f.length(); |
70 public RecordingInput(File f, int blockSize) throws IOException { |
79 this.blockSize = blockSize; |
71 this.blockSize = blockSize; |
|
72 this.filename = f.getAbsolutePath().toString(); |
80 this.file = new RandomAccessFile(f, "r"); |
73 this.file = new RandomAccessFile(f, "r"); |
81 if (size < 8) { |
74 if (f.length() < 8) { |
82 throw new IOException("Not a valid Flight Recorder file. File length is only " + size + " bytes."); |
75 throw new IOException("Not a valid Flight Recorder file. File length is only " + f.length() + " bytes."); |
83 } |
76 } |
84 } |
77 } |
85 |
78 |
86 public RecordingInput(File f) throws IOException { |
79 public RecordingInput(File f) throws IOException { |
87 this(f, DEFAULT_BLOCK_SIZE); |
80 this(f, DEFAULT_BLOCK_SIZE); |
|
81 } |
|
82 public void positionPhysical(long position) throws IOException { |
|
83 file.seek(position); |
|
84 } |
|
85 public final byte readPhysicalByte() throws IOException { |
|
86 return file.readByte(); |
|
87 } |
|
88 public long readPhysicalLong() throws IOException { |
|
89 return file.readLong(); |
88 } |
90 } |
89 |
91 |
90 @Override |
92 @Override |
91 public final byte readByte() throws IOException { |
93 public final byte readByte() throws IOException { |
92 if (!currentBlock.contains(position)) { |
94 if (!currentBlock.contains(position)) { |
148 byte b6 = readByte(); |
150 byte b6 = readByte(); |
149 byte b7 = readByte(); |
151 byte b7 = readByte(); |
150 return ((b7 & 0xFFL)) + ((b6 & 0xFFL) << 8) + ((b5 & 0xFFL) << 16) + ((b4 & 0xFFL) << 24) + ((b3 & 0xFFL) << 32) + ((b2 & 0xFFL) << 40) + ((b1 & 0xFFL) << 48) + (((long) b0) << 56); |
152 return ((b7 & 0xFFL)) + ((b6 & 0xFFL) << 8) + ((b5 & 0xFFL) << 16) + ((b4 & 0xFFL) << 24) + ((b3 & 0xFFL) << 32) + ((b2 & 0xFFL) << 40) + ((b1 & 0xFFL) << 48) + (((long) b0) << 56); |
151 } |
153 } |
152 |
154 |
153 public final long position() throws IOException { |
155 public final long position() { |
154 return position; |
156 return position; |
155 } |
157 } |
156 |
158 |
157 public final void position(long newPosition) throws IOException { |
159 public final void position(long newPosition) throws IOException { |
158 if (!currentBlock.contains(newPosition)) { |
160 if (!currentBlock.contains(newPosition)) { |
159 if (!previousBlock.contains(newPosition)) { |
161 if (!previousBlock.contains(newPosition)) { |
160 if (newPosition > size()) { |
162 if (newPosition > size) { |
161 throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size() + " bytes."); |
163 throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size + " bytes."); |
162 } |
164 } |
163 long blockStart = trimToFileSize(calculateBlockStart(newPosition)); |
165 long blockStart = trimToFileSize(calculateBlockStart(newPosition)); |
164 file.seek(blockStart); |
166 file.seek(blockStart); |
165 // trim amount to file size |
167 // trim amount to file size |
166 long amount = Math.min(size() - blockStart, blockSize); |
168 long amount = Math.min(size - blockStart, blockSize); |
167 previousBlock.read(file, (int) amount); |
169 previousBlock.read(file, (int) amount); |
168 } |
170 } |
169 // swap previous and current |
171 // swap previous and current |
170 Block tmp = currentBlock; |
172 Block tmp = currentBlock; |
171 currentBlock = previousBlock; |
173 currentBlock = previousBlock; |
243 // 2, means char array |
245 // 2, means char array |
244 // 3, means latin-1 (ISO-8859-1) encoded byte array |
246 // 3, means latin-1 (ISO-8859-1) encoded byte array |
245 // 4, means "" |
247 // 4, means "" |
246 @Override |
248 @Override |
247 public String readUTF() throws IOException { |
249 public String readUTF() throws IOException { |
248 return readEncodedString(readByte()); |
250 throw new UnsupportedOperationException("Use StringParser"); |
249 } |
|
250 |
|
251 public String readEncodedString(byte encoding) throws IOException { |
|
252 if (encoding == STRING_ENCODING_NULL) { |
|
253 return null; |
|
254 } |
|
255 if (encoding == STRING_ENCODING_EMPTY_STRING) { |
|
256 return ""; |
|
257 } |
|
258 int size = readInt(); |
|
259 if (encoding == STRING_ENCODING_CHAR_ARRAY) { |
|
260 char[] c = new char[size]; |
|
261 for (int i = 0; i < size; i++) { |
|
262 c[i] = readChar(); |
|
263 } |
|
264 return new String(c); |
|
265 } |
|
266 byte[] bytes = new byte[size]; |
|
267 readFully(bytes); // TODO: optimize, check size, and copy only if needed |
|
268 if (encoding == STRING_ENCODING_UTF8_BYTE_ARRAY) { |
|
269 return new String(bytes, UTF8); |
|
270 } |
|
271 |
|
272 if (encoding == STRING_ENCODING_LATIN1_BYTE_ARRAY) { |
|
273 return new String(bytes, LATIN1); |
|
274 } |
|
275 throw new IOException("Unknown string encoding " + encoding); |
|
276 } |
251 } |
277 |
252 |
278 @Override |
253 @Override |
279 public char readChar() throws IOException { |
254 public char readChar() throws IOException { |
280 return (char) readLong(); |
255 return (char) readLong(); |