src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java
changeset 50113 caf115bb98ad
child 57360 5d043a159d5c
child 58863 c16ac7a2eba4
equal deleted inserted replaced
50112:7a2a740815b7 50113:caf115bb98ad
       
     1 /*
       
     2  * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package jdk.jfr.internal.consumer;
       
    27 
       
    28 import java.io.DataInput;
       
    29 import java.io.EOFException;
       
    30 import java.io.File;
       
    31 import java.io.IOException;
       
    32 import java.io.RandomAccessFile;
       
    33 import java.nio.charset.Charset;
       
    34 
       
    35 public final class RecordingInput implements DataInput, AutoCloseable {
       
    36 
       
    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 
       
    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 
       
    48     private static final class Block {
       
    49         private byte[] bytes = new byte[0];
       
    50         private long blockPosition;
       
    51 
       
    52         boolean contains(long position) {
       
    53             return position >= blockPosition && position < blockPosition + bytes.length;
       
    54         }
       
    55 
       
    56         public void read(RandomAccessFile file, int amount) throws IOException {
       
    57             blockPosition = file.getFilePointer();
       
    58             // reuse byte array, if possible
       
    59             if (amount != bytes.length) {
       
    60                 bytes = new byte[amount];
       
    61             }
       
    62             file.readFully(bytes);
       
    63         }
       
    64 
       
    65         public byte get(long position) {
       
    66             return bytes[(int) (position - blockPosition)];
       
    67         }
       
    68     }
       
    69 
       
    70     private final RandomAccessFile file;
       
    71     private final long size;
       
    72     private Block currentBlock = new Block();
       
    73     private Block previousBlock = new Block();
       
    74     private long position;
       
    75     private final int blockSize;
       
    76 
       
    77     private RecordingInput(File f, int blockSize) throws IOException {
       
    78         this.size = f.length();
       
    79         this.blockSize = blockSize;
       
    80         this.file = new RandomAccessFile(f, "r");
       
    81         if (size < 8) {
       
    82             throw new IOException("Not a valid Flight Recorder file. File length is only " + size + " bytes.");
       
    83         }
       
    84     }
       
    85 
       
    86     public RecordingInput(File f) throws IOException {
       
    87         this(f, DEFAULT_BLOCK_SIZE);
       
    88     }
       
    89 
       
    90     @Override
       
    91     public final byte readByte() throws IOException {
       
    92         if (!currentBlock.contains(position)) {
       
    93             position(position);
       
    94         }
       
    95         return currentBlock.get(position++);
       
    96     }
       
    97 
       
    98     @Override
       
    99     public final void readFully(byte[] dest, int offset, int length) throws IOException {
       
   100         // TODO: Optimize, use Arrays.copy if all bytes are in current block
       
   101         // array
       
   102         for (int i = 0; i < length; i++) {
       
   103             dest[i + offset] = readByte();
       
   104         }
       
   105     }
       
   106 
       
   107     @Override
       
   108     public final void readFully(byte[] dst) throws IOException {
       
   109         readFully(dst, 0, dst.length);
       
   110     }
       
   111 
       
   112     public final short readRawShort() throws IOException {
       
   113         // copied from java.io.Bits
       
   114         byte b0 = readByte();
       
   115         byte b1 = readByte();
       
   116         return (short) ((b1 & 0xFF) + (b0 << 8));
       
   117     }
       
   118 
       
   119     @Override
       
   120     public final double readDouble() throws IOException {
       
   121         // copied from java.io.Bits
       
   122         return Double.longBitsToDouble(readRawLong());
       
   123     }
       
   124 
       
   125     @Override
       
   126     public final float readFloat() throws IOException {
       
   127         // copied from java.io.Bits
       
   128         return Float.intBitsToFloat(readRawInt());
       
   129     }
       
   130 
       
   131     public final int readRawInt() throws IOException {
       
   132         // copied from java.io.Bits
       
   133         byte b0 = readByte();
       
   134         byte b1 = readByte();
       
   135         byte b2 = readByte();
       
   136         byte b3 = readByte();
       
   137         return ((b3 & 0xFF)) + ((b2 & 0xFF) << 8) + ((b1 & 0xFF) << 16) + ((b0) << 24);
       
   138     }
       
   139 
       
   140     public final long readRawLong() throws IOException {
       
   141         // copied from java.io.Bits
       
   142         byte b0 = readByte();
       
   143         byte b1 = readByte();
       
   144         byte b2 = readByte();
       
   145         byte b3 = readByte();
       
   146         byte b4 = readByte();
       
   147         byte b5 = readByte();
       
   148         byte b6 = readByte();
       
   149         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);
       
   151     }
       
   152 
       
   153     public final long position() throws IOException {
       
   154         return position;
       
   155     }
       
   156 
       
   157     public final void position(long newPosition) throws IOException {
       
   158         if (!currentBlock.contains(newPosition)) {
       
   159             if (!previousBlock.contains(newPosition)) {
       
   160                 if (newPosition > size()) {
       
   161                     throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size() + " bytes.");
       
   162                 }
       
   163                 long blockStart = trimToFileSize(calculateBlockStart(newPosition));
       
   164                 file.seek(blockStart);
       
   165                 // trim amount to file size
       
   166                 long amount = Math.min(size() - blockStart, blockSize);
       
   167                 previousBlock.read(file, (int) amount);
       
   168             }
       
   169             // swap previous and current
       
   170             Block tmp = currentBlock;
       
   171             currentBlock = previousBlock;
       
   172             previousBlock = tmp;
       
   173         }
       
   174         position = newPosition;
       
   175     }
       
   176 
       
   177     private final long trimToFileSize(long position) throws IOException {
       
   178         return Math.min(size(), Math.max(0, position));
       
   179     }
       
   180 
       
   181     private final long calculateBlockStart(long newPosition) {
       
   182         // align to end of current block
       
   183         if (currentBlock.contains(newPosition - blockSize)) {
       
   184             return currentBlock.blockPosition + currentBlock.bytes.length;
       
   185         }
       
   186         // align before current block
       
   187         if (currentBlock.contains(newPosition + blockSize)) {
       
   188             return currentBlock.blockPosition - blockSize;
       
   189         }
       
   190         // not near current block, pick middle
       
   191         return newPosition - blockSize / 2;
       
   192     }
       
   193 
       
   194     public final long size() throws IOException {
       
   195         return size;
       
   196     }
       
   197 
       
   198     public final void close() throws IOException {
       
   199         file.close();
       
   200     }
       
   201 
       
   202     @Override
       
   203     public final int skipBytes(int n) throws IOException {
       
   204         long position = position();
       
   205         position(position + n);
       
   206         return (int) (position() - position);
       
   207     }
       
   208 
       
   209     @Override
       
   210     public final boolean readBoolean() throws IOException {
       
   211         return readByte() != 0;
       
   212     }
       
   213 
       
   214     @Override
       
   215     public int readUnsignedByte() throws IOException {
       
   216         return readByte() & 0x00FF;
       
   217     }
       
   218 
       
   219     @Override
       
   220     public int readUnsignedShort() throws IOException {
       
   221         return readShort() & 0xFFFF;
       
   222     }
       
   223 
       
   224     @Override
       
   225     public final String readLine() throws IOException {
       
   226         throw new UnsupportedOperationException();
       
   227     }
       
   228 
       
   229     // NOTE, this method should really be called readString
       
   230     // but can't be renamed without making RecordingInput a
       
   231     // public class.
       
   232     //
       
   233     // This method DOES Not read as expected (s2 + utf8 encoded character)
       
   234     // instead it read:
       
   235     // byte encoding
       
   236     // int size
       
   237     // data (byte or char)
       
   238     //
       
   239     // where encoding
       
   240     //
       
   241     // 0, means null
       
   242     // 1, means UTF8 encoded byte array
       
   243     // 2, means char array
       
   244     // 3, means latin-1 (ISO-8859-1) encoded byte array
       
   245     // 4, means ""
       
   246     @Override
       
   247     public String readUTF() throws IOException {
       
   248         return readEncodedString(readByte());
       
   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     }
       
   277 
       
   278     @Override
       
   279     public char readChar() throws IOException {
       
   280         return (char) readLong();
       
   281     }
       
   282 
       
   283     @Override
       
   284     public short readShort() throws IOException {
       
   285         return (short) readLong();
       
   286     }
       
   287 
       
   288     @Override
       
   289     public int readInt() throws IOException {
       
   290         return (int) readLong();
       
   291     }
       
   292 
       
   293     @Override
       
   294     public long readLong() throws IOException {
       
   295         // can be optimized by branching checks, but will do for now
       
   296         byte b0 = readByte();
       
   297         long ret = (b0 & 0x7FL);
       
   298         if (b0 >= 0) {
       
   299             return ret;
       
   300         }
       
   301         int b1 = readByte();
       
   302         ret += (b1 & 0x7FL) << 7;
       
   303         if (b1 >= 0) {
       
   304             return ret;
       
   305         }
       
   306         int b2 = readByte();
       
   307         ret += (b2 & 0x7FL) << 14;
       
   308         if (b2 >= 0) {
       
   309             return ret;
       
   310         }
       
   311         int b3 = readByte();
       
   312         ret += (b3 & 0x7FL) << 21;
       
   313         if (b3 >= 0) {
       
   314             return ret;
       
   315         }
       
   316         int b4 = readByte();
       
   317         ret += (b4 & 0x7FL) << 28;
       
   318         if (b4 >= 0) {
       
   319             return ret;
       
   320         }
       
   321         int b5 = readByte();
       
   322         ret += (b5 & 0x7FL) << 35;
       
   323         if (b5 >= 0) {
       
   324             return ret;
       
   325         }
       
   326         int b6 = readByte();
       
   327         ret += (b6 & 0x7FL) << 42;
       
   328         if (b6 >= 0) {
       
   329             return ret;
       
   330         }
       
   331         int b7 = readByte();
       
   332         ret += (b7 & 0x7FL) << 49;
       
   333         if (b7 >= 0) {
       
   334             return ret;
       
   335         }
       
   336         int b8 = readByte(); // read last byte raw
       
   337         return ret + (((long) (b8 & 0XFF)) << 56);
       
   338     }
       
   339 }