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; |
33 import java.nio.file.Path; |
34 |
34 |
35 public final class RecordingInput implements DataInput, AutoCloseable { |
35 public final class RecordingInput implements DataInput, AutoCloseable { |
36 |
36 |
37 public static final byte STRING_ENCODING_NULL = 0; |
37 private final static int DEFAULT_BLOCK_SIZE = 64_000; |
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 |
|
44 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; |
|
42 private long blockPositionEnd; |
51 |
43 |
52 boolean contains(long position) { |
44 boolean contains(long position) { |
53 return position >= blockPosition && position < blockPosition + bytes.length; |
45 return position >= blockPosition && position < blockPositionEnd; |
54 } |
46 } |
55 |
47 |
56 public void read(RandomAccessFile file, int amount) throws IOException { |
48 public void read(RandomAccessFile file, int amount) throws IOException { |
57 blockPosition = file.getFilePointer(); |
49 blockPosition = file.getFilePointer(); |
58 // reuse byte array, if possible |
50 // reuse byte array, if possible |
59 if (amount != bytes.length) { |
51 if (amount > bytes.length) { |
60 bytes = new byte[amount]; |
52 bytes = new byte[amount]; |
61 } |
53 } |
62 file.readFully(bytes); |
54 this.blockPositionEnd = blockPosition + amount; |
|
55 file.readFully(bytes, 0, amount); |
63 } |
56 } |
64 |
57 |
65 public byte get(long position) { |
58 public byte get(long position) { |
66 return bytes[(int) (position - blockPosition)]; |
59 return bytes[(int) (position - blockPosition)]; |
67 } |
60 } |
68 } |
61 |
69 |
62 public void reset() { |
70 private final RandomAccessFile file; |
63 blockPosition = 0; |
71 private final long size; |
64 blockPositionEnd = 0; |
|
65 } |
|
66 } |
|
67 private final int blockSize; |
|
68 private final FileAccess fileAccess; |
|
69 private RandomAccessFile file; |
|
70 private String filename; |
72 private Block currentBlock = new Block(); |
71 private Block currentBlock = new Block(); |
73 private Block previousBlock = new Block(); |
72 private Block previousBlock = new Block(); |
74 private long position; |
73 private long position; |
75 private final int blockSize; |
74 private long size = -1; // Fail fast if setSize(...) has not been called |
76 |
75 // before parsing |
77 private RecordingInput(File f, int blockSize) throws IOException { |
76 |
78 this.size = f.length(); |
77 RecordingInput(File f, FileAccess fileAccess, int blockSize) throws IOException { |
79 this.blockSize = blockSize; |
78 this.blockSize = blockSize; |
80 this.file = new RandomAccessFile(f, "r"); |
79 this.fileAccess = fileAccess; |
81 if (size < 8) { |
80 initialize(f); |
82 throw new IOException("Not a valid Flight Recorder file. File length is only " + size + " bytes."); |
81 } |
83 } |
82 |
84 } |
83 private void initialize(File f) throws IOException { |
85 |
84 this.filename = fileAccess.getAbsolutePath(f); |
86 public RecordingInput(File f) throws IOException { |
85 this.file = fileAccess.openRAF(f, "r"); |
87 this(f, DEFAULT_BLOCK_SIZE); |
86 this.position = 0; |
|
87 this.size = -1; |
|
88 this.currentBlock.reset(); |
|
89 previousBlock.reset(); |
|
90 if (fileAccess.length(f) < 8) { |
|
91 throw new IOException("Not a valid Flight Recorder file. File length is only " + fileAccess.length(f) + " bytes."); |
|
92 } |
|
93 } |
|
94 |
|
95 public RecordingInput(File f, FileAccess fileAccess) throws IOException { |
|
96 this(f, fileAccess, DEFAULT_BLOCK_SIZE); |
|
97 } |
|
98 |
|
99 void positionPhysical(long position) throws IOException { |
|
100 file.seek(position); |
|
101 } |
|
102 |
|
103 byte readPhysicalByte() throws IOException { |
|
104 return file.readByte(); |
|
105 } |
|
106 |
|
107 long readPhysicalLong() throws IOException { |
|
108 return file.readLong(); |
88 } |
109 } |
89 |
110 |
90 @Override |
111 @Override |
91 public final byte readByte() throws IOException { |
112 public final byte readByte() throws IOException { |
92 if (!currentBlock.contains(position)) { |
113 if (!currentBlock.contains(position)) { |
107 @Override |
128 @Override |
108 public final void readFully(byte[] dst) throws IOException { |
129 public final void readFully(byte[] dst) throws IOException { |
109 readFully(dst, 0, dst.length); |
130 readFully(dst, 0, dst.length); |
110 } |
131 } |
111 |
132 |
112 public final short readRawShort() throws IOException { |
133 short readRawShort() throws IOException { |
113 // copied from java.io.Bits |
134 // copied from java.io.Bits |
114 byte b0 = readByte(); |
135 byte b0 = readByte(); |
115 byte b1 = readByte(); |
136 byte b1 = readByte(); |
116 return (short) ((b1 & 0xFF) + (b0 << 8)); |
137 return (short) ((b1 & 0xFF) + (b0 << 8)); |
117 } |
138 } |
118 |
139 |
119 @Override |
140 @Override |
120 public final double readDouble() throws IOException { |
141 public double readDouble() throws IOException { |
121 // copied from java.io.Bits |
142 // copied from java.io.Bits |
122 return Double.longBitsToDouble(readRawLong()); |
143 return Double.longBitsToDouble(readRawLong()); |
123 } |
144 } |
124 |
145 |
125 @Override |
146 @Override |
126 public final float readFloat() throws IOException { |
147 public float readFloat() throws IOException { |
127 // copied from java.io.Bits |
148 // copied from java.io.Bits |
128 return Float.intBitsToFloat(readRawInt()); |
149 return Float.intBitsToFloat(readRawInt()); |
129 } |
150 } |
130 |
151 |
131 public final int readRawInt() throws IOException { |
152 int readRawInt() throws IOException { |
132 // copied from java.io.Bits |
153 // copied from java.io.Bits |
133 byte b0 = readByte(); |
154 byte b0 = readByte(); |
134 byte b1 = readByte(); |
155 byte b1 = readByte(); |
135 byte b2 = readByte(); |
156 byte b2 = readByte(); |
136 byte b3 = readByte(); |
157 byte b3 = readByte(); |
137 return ((b3 & 0xFF)) + ((b2 & 0xFF) << 8) + ((b1 & 0xFF) << 16) + ((b0) << 24); |
158 return ((b3 & 0xFF)) + ((b2 & 0xFF) << 8) + ((b1 & 0xFF) << 16) + ((b0) << 24); |
138 } |
159 } |
139 |
160 |
140 public final long readRawLong() throws IOException { |
161 long readRawLong() throws IOException { |
141 // copied from java.io.Bits |
162 // copied from java.io.Bits |
142 byte b0 = readByte(); |
163 byte b0 = readByte(); |
143 byte b1 = readByte(); |
164 byte b1 = readByte(); |
144 byte b2 = readByte(); |
165 byte b2 = readByte(); |
145 byte b3 = readByte(); |
166 byte b3 = readByte(); |
148 byte b6 = readByte(); |
169 byte b6 = readByte(); |
149 byte b7 = readByte(); |
170 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); |
171 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 } |
172 } |
152 |
173 |
153 public final long position() throws IOException { |
174 public final long position() { |
154 return position; |
175 return position; |
155 } |
176 } |
156 |
177 |
157 public final void position(long newPosition) throws IOException { |
178 public final void position(long newPosition) throws IOException { |
158 if (!currentBlock.contains(newPosition)) { |
179 if (!currentBlock.contains(newPosition)) { |
159 if (!previousBlock.contains(newPosition)) { |
180 if (!previousBlock.contains(newPosition)) { |
160 if (newPosition > size()) { |
181 if (newPosition > size) { |
161 throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size() + " bytes."); |
182 throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size + " bytes."); |
162 } |
183 } |
163 long blockStart = trimToFileSize(calculateBlockStart(newPosition)); |
184 long blockStart = trimToFileSize(calculateBlockStart(newPosition)); |
164 file.seek(blockStart); |
185 file.seek(blockStart); |
165 // trim amount to file size |
186 // trim amount to file size |
166 long amount = Math.min(size() - blockStart, blockSize); |
187 long amount = Math.min(size - blockStart, blockSize); |
167 previousBlock.read(file, (int) amount); |
188 previousBlock.read(file, (int) amount); |
168 } |
189 } |
169 // swap previous and current |
190 // swap previous and current |
170 Block tmp = currentBlock; |
191 Block tmp = currentBlock; |
171 currentBlock = previousBlock; |
192 currentBlock = previousBlock; |
243 // 2, means char array |
265 // 2, means char array |
244 // 3, means latin-1 (ISO-8859-1) encoded byte array |
266 // 3, means latin-1 (ISO-8859-1) encoded byte array |
245 // 4, means "" |
267 // 4, means "" |
246 @Override |
268 @Override |
247 public String readUTF() throws IOException { |
269 public String readUTF() throws IOException { |
248 return readEncodedString(readByte()); |
270 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 } |
271 } |
277 |
272 |
278 @Override |
273 @Override |
279 public char readChar() throws IOException { |
274 public char readChar() throws IOException { |
280 return (char) readLong(); |
275 return (char) readLong(); |
290 return (int) readLong(); |
285 return (int) readLong(); |
291 } |
286 } |
292 |
287 |
293 @Override |
288 @Override |
294 public long readLong() throws IOException { |
289 public long readLong() throws IOException { |
295 // can be optimized by branching checks, but will do for now |
290 final byte[] bytes = currentBlock.bytes; |
|
291 final int index = (int) (position - currentBlock.blockPosition); |
|
292 |
|
293 if (index + 8 < bytes.length && index >= 0) { |
|
294 byte b0 = bytes[index]; |
|
295 long ret = (b0 & 0x7FL); |
|
296 if (b0 >= 0) { |
|
297 position += 1; |
|
298 return ret; |
|
299 } |
|
300 int b1 = bytes[index + 1]; |
|
301 ret += (b1 & 0x7FL) << 7; |
|
302 if (b1 >= 0) { |
|
303 position += 2; |
|
304 return ret; |
|
305 } |
|
306 int b2 = bytes[index + 2]; |
|
307 ret += (b2 & 0x7FL) << 14; |
|
308 if (b2 >= 0) { |
|
309 position += 3; |
|
310 return ret; |
|
311 } |
|
312 int b3 = bytes[index + 3]; |
|
313 ret += (b3 & 0x7FL) << 21; |
|
314 if (b3 >= 0) { |
|
315 position += 4; |
|
316 return ret; |
|
317 } |
|
318 int b4 = bytes[index + 4]; |
|
319 ret += (b4 & 0x7FL) << 28; |
|
320 if (b4 >= 0) { |
|
321 position += 5; |
|
322 return ret; |
|
323 } |
|
324 int b5 = bytes[index + 5]; |
|
325 ret += (b5 & 0x7FL) << 35; |
|
326 if (b5 >= 0) { |
|
327 position += 6; |
|
328 return ret; |
|
329 } |
|
330 int b6 = bytes[index + 6]; |
|
331 ret += (b6 & 0x7FL) << 42; |
|
332 if (b6 >= 0) { |
|
333 position += 7; |
|
334 return ret; |
|
335 } |
|
336 int b7 = bytes[index + 7]; |
|
337 ret += (b7 & 0x7FL) << 49; |
|
338 if (b7 >= 0) { |
|
339 position += 8; |
|
340 return ret; |
|
341 } |
|
342 int b8 = bytes[index + 8];// read last byte raw |
|
343 position += 9; |
|
344 return ret + (((long) (b8 & 0XFF)) << 56); |
|
345 } else { |
|
346 return readLongSlow(); |
|
347 } |
|
348 } |
|
349 |
|
350 private long readLongSlow() throws IOException { |
296 byte b0 = readByte(); |
351 byte b0 = readByte(); |
297 long ret = (b0 & 0x7FL); |
352 long ret = (b0 & 0x7FL); |
298 if (b0 >= 0) { |
353 if (b0 >= 0) { |
299 return ret; |
354 return ret; |
300 } |
355 } |
|
356 |
301 int b1 = readByte(); |
357 int b1 = readByte(); |
302 ret += (b1 & 0x7FL) << 7; |
358 ret += (b1 & 0x7FL) << 7; |
303 if (b1 >= 0) { |
359 if (b1 >= 0) { |
304 return ret; |
360 return ret; |
305 } |
361 } |
|
362 |
306 int b2 = readByte(); |
363 int b2 = readByte(); |
307 ret += (b2 & 0x7FL) << 14; |
364 ret += (b2 & 0x7FL) << 14; |
308 if (b2 >= 0) { |
365 if (b2 >= 0) { |
309 return ret; |
366 return ret; |
310 } |
367 } |
|
368 |
311 int b3 = readByte(); |
369 int b3 = readByte(); |
312 ret += (b3 & 0x7FL) << 21; |
370 ret += (b3 & 0x7FL) << 21; |
313 if (b3 >= 0) { |
371 if (b3 >= 0) { |
314 return ret; |
372 return ret; |
315 } |
373 } |
|
374 |
316 int b4 = readByte(); |
375 int b4 = readByte(); |
317 ret += (b4 & 0x7FL) << 28; |
376 ret += (b4 & 0x7FL) << 28; |
318 if (b4 >= 0) { |
377 if (b4 >= 0) { |
319 return ret; |
378 return ret; |
320 } |
379 } |
|
380 |
321 int b5 = readByte(); |
381 int b5 = readByte(); |
322 ret += (b5 & 0x7FL) << 35; |
382 ret += (b5 & 0x7FL) << 35; |
323 if (b5 >= 0) { |
383 if (b5 >= 0) { |
324 return ret; |
384 return ret; |
325 } |
385 } |
|
386 |
326 int b6 = readByte(); |
387 int b6 = readByte(); |
327 ret += (b6 & 0x7FL) << 42; |
388 ret += (b6 & 0x7FL) << 42; |
328 if (b6 >= 0) { |
389 if (b6 >= 0) { |
329 return ret; |
390 return ret; |
330 } |
391 } |
|
392 |
331 int b7 = readByte(); |
393 int b7 = readByte(); |
332 ret += (b7 & 0x7FL) << 49; |
394 ret += (b7 & 0x7FL) << 49; |
333 if (b7 >= 0) { |
395 if (b7 >= 0) { |
334 return ret; |
396 return ret; |
335 } |
397 |
|
398 } |
|
399 |
336 int b8 = readByte(); // read last byte raw |
400 int b8 = readByte(); // read last byte raw |
337 return ret + (((long) (b8 & 0XFF)) << 56); |
401 return ret + (((long) (b8 & 0XFF)) << 56); |
338 } |
402 } |
|
403 |
|
404 public void setValidSize(long size) { |
|
405 if (size > this.size) { |
|
406 this.size = size; |
|
407 } |
|
408 } |
|
409 |
|
410 public long getFileSize() throws IOException { |
|
411 return file.length(); |
|
412 } |
|
413 |
|
414 public String getFilename() { |
|
415 return filename; |
|
416 } |
|
417 |
|
418 // Purpose of this method is to reuse block cache from a |
|
419 // previous RecordingInput |
|
420 public void setFile(Path path) throws IOException { |
|
421 try { |
|
422 file.close(); |
|
423 } catch (IOException e) { |
|
424 // perhaps deleted |
|
425 } |
|
426 file = null; |
|
427 initialize(path.toFile()); |
|
428 } |
|
429 /* |
|
430 |
|
431 |
|
432 |
|
433 |
|
434 |
|
435 * |
|
436 * |
|
437 */ |
339 } |
438 } |