package org.apache.cassandra.io.util; /* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ import java.io.*; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; public class MappedFileDataInput extends InputStream implements FileDataInput { private final MappedByteBuffer buffer; private final String filename; private int position; private int markedPosition; private final long absoluteStartPosition; public MappedFileDataInput(MappedByteBuffer buffer, String filename, long absoluteStartPosition) { this(buffer, filename, absoluteStartPosition, 0); } public MappedFileDataInput(MappedByteBuffer buffer, String filename, long absoluteStartPosition, int position) { assert buffer != null; assert buffer.order()==ByteOrder.BIG_ENDIAN; this.absoluteStartPosition = absoluteStartPosition; this.buffer = buffer; this.filename = filename; this.position = position; } public long getAbsolutePosition() { return absoluteStartPosition + position; } // don't make this public, this is only for seeking WITHIN the current mapped segment private void seekInternal(int pos) throws IOException { position = pos; } @Override public boolean markSupported() { return true; } @Override public void mark(int ignored) { markedPosition = position; } @Override public void reset() throws IOException { seekInternal(markedPosition); } public void mark() { mark(-1); } public int bytesPastMark() { assert position >= markedPosition; return position - markedPosition; } public boolean isEOF() throws IOException { return position == buffer.capacity(); } public boolean isEOF(int len) throws IOException { return position + len > buffer.capacity(); } public String getPath() { return filename; } public int read() throws IOException { if (isEOF()) return -1; return buffer.get(position++) & 0xFF; } public int skipBytes(int n) throws IOException { assert n >= 0 : "skipping negative bytes is illegal: " + n; if (n == 0) return 0; int oldPosition = position; assert ((long)oldPosition) + n <= Integer.MAX_VALUE; position = Math.min(buffer.capacity(), position + n); return position - oldPosition; } public void skipLong() throws IOException { if ( (position+=8) > buffer.capacity()) { position = buffer.capacity(); throw new EOFException(); }; } /* !! DataInput methods below are copied from the implementation in Apache Harmony RandomAccessFile. */ /** * Reads a boolean from the current position in this file. Blocks until one * byte has been read, the end of the file is reached or an exception is * thrown. * * @return the next boolean value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. */ public final boolean readBoolean() throws IOException { int temp = this.read(); if (temp < 0) { throw new EOFException(); } return temp != 0; } /** * Reads an 8-bit byte from the current position in this file. Blocks until * one byte has been read, the end of the file is reached or an exception is * thrown. * * @return the next signed 8-bit byte value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. */ public final byte readByte() throws IOException { int temp = this.read(); if (temp < 0) { throw new EOFException(); } return (byte) temp; } /** * Reads a 16-bit character from the current position in this file. Blocks until * two bytes have been read, the end of the file is reached or an exception is * thrown. * * @return the next char value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. */ public final char readChar() throws IOException { if ( isEOF(2) ) { skipBytes(2); throw new EOFException(); } char c = buffer.getChar(position); position+=2; return c; } /** * Reads a 64-bit double from the current position in this file. Blocks * until eight bytes have been read, the end of the file is reached or an * exception is thrown. * * @return the next double value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. */ public final double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); } /** * Reads a 32-bit float from the current position in this file. Blocks * until four bytes have been read, the end of the file is reached or an * exception is thrown. * * @return the next float value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. */ public final float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); } /** * Reads bytes from this file into {@code buffer}. Blocks until {@code * buffer.length} number of bytes have been read, the end of the file is * reached or an exception is thrown. * * @param buffer * the buffer to read bytes into. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. * @throws NullPointerException * if {@code buffer} is {@code null}. */ public final void readFully(byte[] buffer) throws IOException { readFully(buffer, 0, buffer.length); } /** * Read bytes from this file into {@code buffer} starting at offset {@code * offset}. This method blocks until {@code count} number of bytes have been * read. * * @param buffer * the buffer to read bytes into. * @param offset * the initial position in {@code buffer} to store the bytes read * from this file. * @param count * the maximum number of bytes to store in {@code buffer}. * @throws EOFException * if the end of this file is detected. * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code count < 0}, or if {@code * offset + count} is greater than the length of {@code buffer}. * @throws IOException * if this file is closed or another I/O error occurs. * @throws NullPointerException * if {@code buffer} is {@code null}. */ public final void readFully(byte[] buffer, int offset, int count) throws IOException { // avoid int overflow if (offset < 0 || offset > buffer.length || count < 0 || count > buffer.length - offset) { throw new IndexOutOfBoundsException(); } if (isEOF(count)) { // rare path read(buffer, offset, count); throw new EOFException(); } // fast common path while (count-- > 0) { buffer[ offset ++ ] = this.buffer.get( position++ ); } } /** * Reads a 32-bit integer from the current position in this file. Blocks * until four bytes have been read, the end of the file is reached or an * exception is thrown. * * @return the next int value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. */ public final int readInt() throws IOException { if ( isEOF(4) ) { skipBytes(4); throw new EOFException(); } int i = buffer.getInt(position); position+=4; return i; } /** * Reads a line of text form the current position in this file. A line is * represented by zero or more characters followed by {@code '\n'}, {@code * '\r'}, {@code "\r\n"} or the end of file marker. The string does not * include the line terminating sequence. * <p> * Blocks until a line terminating sequence has been read, the end of the * file is reached or an exception is thrown. * * @return the contents of the line or {@code null} if no characters have * been read before the end of the file has been reached. * @throws IOException * if this file is closed or another I/O error occurs. */ public final String readLine() throws IOException { StringBuilder line = new StringBuilder(80); // Typical line length boolean foundTerminator = false; int unreadPosition = 0; while (true) { int nextByte = read(); switch (nextByte) { case -1: return line.length() != 0 ? line.toString() : null; case (byte) '\r': if (foundTerminator) { seekInternal(unreadPosition); return line.toString(); } foundTerminator = true; /* Have to be able to peek ahead one byte */ unreadPosition = position; break; case (byte) '\n': return line.toString(); default: if (foundTerminator) { seekInternal(unreadPosition); return line.toString(); } line.append((char) nextByte); } } } /** * Reads a 64-bit long from the current position in this file. Blocks until * eight bytes have been read, the end of the file is reached or an * exception is thrown. * * @return the next long value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. */ public final long readLong() throws IOException { if ( isEOF(8) ) { skipBytes(8); throw new EOFException(); } long l = buffer.getLong(position); position+=8; return l; } /** * Reads a 16-bit short from the current position in this file. Blocks until * two bytes have been read, the end of the file is reached or an exception * is thrown. * * @return the next short value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. */ public final short readShort() throws IOException { if ( isEOF(2) ) { skipBytes(2); throw new EOFException(); } short s = buffer.getShort(position); position +=2; return s; } /** * Reads an unsigned 8-bit byte from the current position in this file and * returns it as an integer. Blocks until one byte has been read, the end of * the file is reached or an exception is thrown. * * @return the next unsigned byte value from this file as an int. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. */ public final int readUnsignedByte() throws IOException { int temp = this.read(); if (temp < 0) { throw new EOFException(); } return temp; } /** * Reads an unsigned 16-bit short from the current position in this file and * returns it as an integer. Blocks until two bytes have been read, the end of * the file is reached or an exception is thrown. * * @return the next unsigned short value from this file as an int. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. */ public final int readUnsignedShort() throws IOException { if ( isEOF(2) ) { skipBytes(2); throw new EOFException(); } int i = buffer.getShort(position) & 0xFFFF; position +=2; return i; } /** * Reads a string that is encoded in {@link DataInput modified UTF-8} from * this file. The number of bytes that must be read for the complete string * is determined by the first two bytes read from the file. Blocks until all * required bytes have been read, the end of the file is reached or an * exception is thrown. * * @return the next string encoded in {@link DataInput modified UTF-8} from * this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. * @throws UTFDataFormatException * if the bytes read cannot be decoded into a character string. */ public final String readUTF() throws IOException { int utflen = readUnsignedShort(); if (isEOF(utflen)) { skipBytes(utflen); throw new EOFException(); } char[] chars = null; chars = new char[utflen]; int c, c2, c3; int count = 0; int chararr_count=0; while (count < utflen) { c = this.buffer.get( position ) & 0xff; if (c > 127) break; chars[count++]=(char)c; position++; } if (count==utflen) return new String(chars); chararr_count = count; while (count < utflen) { c = this.buffer.get( position++ ) & 0xff; switch (c >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: /* 0xxxxxxx*/ count++; chars[chararr_count++]=(char)c; break; case 12: case 13: /* 110x xxxx 10xx xxxx*/ count += 2; if (count > utflen) { skipBytes(1); throw new UTFDataFormatException( "malformed input: partial character at end"); } c2 = this.buffer.get( position++ ); if ((c2 & 0xC0) != 0x80) { skipBytes(utflen - count); throw new UTFDataFormatException( "malformed input around byte " + count); } chars[chararr_count++]=(char)(((c & 0x1F) << 6) | (c2 & 0x3F)); break; case 14: /* 1110 xxxx 10xx xxxx 10xx xxxx */ count += 3; if (count > utflen) { skipBytes(2); throw new UTFDataFormatException( "malformed input: partial character at end"); } c2 = this.buffer.get( position++ ); c3 = this.buffer.get( position++ ); if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) { skipBytes( utflen - count ); throw new UTFDataFormatException( "malformed input around byte " + (count-1)); } chars[chararr_count++]=(char)(((c & 0x0F) << 12) | ((c2 & 0x3F) << 6) | ((c3 & 0x3F) << 0)); break; default: /* 10xx xxxx, 1111 xxxx */ skipBytes( utflen - count ); throw new UTFDataFormatException( "malformed input around byte " + count); } } // The number of chars produced may be less than utflen return new String(chars, 0, chararr_count); } }