/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed 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. */ package com.hazelcast.internal.serialization.impl; import com.hazelcast.internal.serialization.InternalSerializationService; import com.hazelcast.nio.Bits; import com.hazelcast.nio.BufferObjectDataInput; import com.hazelcast.nio.serialization.Data; import java.io.EOFException; import java.io.IOException; import java.nio.ByteOrder; import static com.hazelcast.nio.Bits.CHAR_SIZE_IN_BYTES; import static com.hazelcast.nio.Bits.INT_SIZE_IN_BYTES; import static com.hazelcast.nio.Bits.LONG_SIZE_IN_BYTES; import static com.hazelcast.nio.Bits.NULL_ARRAY_LENGTH; import static com.hazelcast.nio.Bits.SHORT_SIZE_IN_BYTES; class ByteArrayObjectDataInput extends VersionedObjectDataInput implements BufferObjectDataInput { private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; byte[] data; int size; int pos; int mark; final InternalSerializationService service; char[] charBuffer; private final boolean bigEndian; ByteArrayObjectDataInput(byte[] data, InternalSerializationService service, ByteOrder byteOrder) { this(data, 0, service, byteOrder); } ByteArrayObjectDataInput(byte[] data, int offset, InternalSerializationService service, ByteOrder byteOrder) { this.data = data; this.size = data != null ? data.length : 0; this.service = service; this.pos = offset; this.bigEndian = byteOrder == ByteOrder.BIG_ENDIAN; } @Override public void init(byte[] data, int offset) { this.data = data; this.size = data != null ? data.length : 0; this.pos = offset; } @Override public void clear() { this.data = null; this.pos = 0; this.size = 0; this.mark = 0; if (charBuffer != null && charBuffer.length > UTF_BUFFER_SIZE * 8) { this.charBuffer = new char[UTF_BUFFER_SIZE * 8]; } } @Override public int read() throws EOFException { return (pos < size) ? (data[pos++] & 0xff) : -1; } @Override public int read(int position) throws EOFException { return (position < size) ? (data[position] & 0xff) : -1; } @Override public final int read(byte[] b, int off, int len) throws EOFException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } if (pos >= size) { return -1; } if (pos + len > size) { len = size - pos; } System.arraycopy(data, pos, b, off, len); pos += len; return len; } @Override public final boolean readBoolean() throws EOFException { final int ch = read(); if (ch < 0) { throw new EOFException(); } return (ch != 0); } @Override public final boolean readBoolean(int position) throws EOFException { final int ch = read(position); if (ch < 0) { throw new EOFException(); } return (ch != 0); } /** * See the general contract of the <code>readByte</code> method of * <code>DataInput</code>. * <p/> * Bytes for this operation are read from the contained input stream. * * @return the next byte of this input stream as a signed 8-bit * <code>byte</code>. * @throws java.io.EOFException if this input stream has reached the end. * @throws java.io.IOException if an I/O error occurs. * @see java.io.FilterInputStream#in */ @Override public final byte readByte() throws EOFException { final int ch = read(); if (ch < 0) { throw new EOFException(); } return (byte) (ch); } @Override public final byte readByte(int position) throws EOFException { final int ch = read(position); if (ch < 0) { throw new EOFException(); } return (byte) (ch); } /** * See the general contract of the <code>readChar</code> method of * <code>DataInput</code>. * <p/> * Bytes for this operation are read from the contained input stream. * * @return the next two bytes of this input stream as a Unicode character. * @throws java.io.EOFException if this input stream reaches the end before reading two * bytes. * @throws java.io.IOException if an I/O error occurs. * @see java.io.FilterInputStream#in */ @Override public final char readChar() throws EOFException { final char c = readChar(pos); pos += CHAR_SIZE_IN_BYTES; return c; } @Override public char readChar(int position) throws EOFException { checkAvailable(position, CHAR_SIZE_IN_BYTES); return Bits.readChar(data, position, bigEndian); } /** * See the general contract of the <code>readDouble</code> method of * <code>DataInput</code>. * <p/> * Bytes for this operation are read from the contained input stream. * * @return the next eight bytes of this input stream, interpreted as a * <code>double</code>. * @throws java.io.EOFException if this input stream reaches the end before reading eight * bytes. * @throws java.io.IOException if an I/O error occurs. * @see java.io.DataInputStream#readLong() * @see Double#longBitsToDouble(long) */ @Override public double readDouble() throws EOFException { return Double.longBitsToDouble(readLong()); } @Override public double readDouble(int position) throws EOFException { return Double.longBitsToDouble(readLong(position)); } @Override public double readDouble(ByteOrder byteOrder) throws EOFException { return Double.longBitsToDouble(readLong(byteOrder)); } @Override public double readDouble(int position, ByteOrder byteOrder) throws EOFException { return Double.longBitsToDouble(readLong(position, byteOrder)); } /** * See the general contract of the <code>readFloat</code> method of * <code>DataInput</code>. * <p/> * Bytes for this operation are read from the contained input stream. * * @return the next four bytes of this input stream, interpreted as a * <code>float</code>. * @throws java.io.EOFException if this input stream reaches the end before reading four * bytes. * @throws java.io.IOException if an I/O error occurs. * @see java.io.DataInputStream#readInt() * @see Float#intBitsToFloat(int) */ @Override public float readFloat() throws EOFException { return Float.intBitsToFloat(readInt()); } @Override public float readFloat(int position) throws EOFException { return Float.intBitsToFloat(readInt(position)); } @Override public float readFloat(ByteOrder byteOrder) throws EOFException { return Float.intBitsToFloat(readInt(byteOrder)); } @Override public float readFloat(int position, ByteOrder byteOrder) throws EOFException { return Float.intBitsToFloat(readInt(position, byteOrder)); } @Override public void readFully(final byte[] b) throws IOException { if (read(b) == -1) { throw new EOFException("End of stream reached"); } } @Override public void readFully(final byte[] b, final int off, final int len) throws EOFException { if (read(b, off, len) == -1) { throw new EOFException("End of stream reached"); } } /** * See the general contract of the <code>readInt</code> method of * <code>DataInput</code>. * <p/> * Bytes for this operation are read from the contained input stream. * * @return the next four bytes of this input stream, interpreted as an * <code>int</code>. * @throws java.io.EOFException if this input stream reaches the end before reading four * bytes. * @throws java.io.IOException if an I/O error occurs. * @see java.io.FilterInputStream#in */ @Override public final int readInt() throws EOFException { final int i = readInt(pos); pos += INT_SIZE_IN_BYTES; return i; } public int readInt(int position) throws EOFException { checkAvailable(position, INT_SIZE_IN_BYTES); return Bits.readInt(data, position, bigEndian); } @Override public final int readInt(ByteOrder byteOrder) throws EOFException { final int i = readInt(pos, byteOrder); pos += INT_SIZE_IN_BYTES; return i; } @Override public int readInt(int position, ByteOrder byteOrder) throws EOFException { checkAvailable(position, INT_SIZE_IN_BYTES); return Bits.readInt(data, position, byteOrder == ByteOrder.BIG_ENDIAN); } @Deprecated public final String readLine() throws EOFException { throw new UnsupportedOperationException(); } /** * See the general contract of the <code>readLong</code> method of * <code>DataInput</code>. * <p/> * Bytes for this operation are read from the contained input stream. * * @return the next eight bytes of this input stream, interpreted as a * <code>long</code>. * @throws java.io.EOFException if this input stream reaches the end before reading eight * bytes. * @throws java.io.IOException if an I/O error occurs. * @see java.io.FilterInputStream#in */ @Override public final long readLong() throws EOFException { final long l = readLong(pos); pos += LONG_SIZE_IN_BYTES; return l; } public long readLong(int position) throws EOFException { checkAvailable(position, LONG_SIZE_IN_BYTES); return Bits.readLong(data, position, bigEndian); } @Override public final long readLong(ByteOrder byteOrder) throws EOFException { final long l = readLong(pos, byteOrder); pos += LONG_SIZE_IN_BYTES; return l; } @Override public long readLong(int position, ByteOrder byteOrder) throws EOFException { checkAvailable(position, LONG_SIZE_IN_BYTES); return Bits.readLong(data, position, byteOrder == ByteOrder.BIG_ENDIAN); } /** * See the general contract of the <code>readShort</code> method of * <code>DataInput</code>. * <p/> * Bytes for this operation are read from the contained input stream. * * @return the next two bytes of this input stream, interpreted as a signed * 16-bit number. * @throws java.io.EOFException if this input stream reaches the end before reading two * bytes. * @throws java.io.IOException if an I/O error occurs. * @see java.io.FilterInputStream#in */ @Override public final short readShort() throws EOFException { short s = readShort(pos); pos += SHORT_SIZE_IN_BYTES; return s; } @Override public short readShort(int position) throws EOFException { checkAvailable(position, SHORT_SIZE_IN_BYTES); return Bits.readShort(data, position, bigEndian); } @Override public final short readShort(ByteOrder byteOrder) throws EOFException { short s = readShort(pos, byteOrder); pos += SHORT_SIZE_IN_BYTES; return s; } @Override public short readShort(int position, ByteOrder byteOrder) throws EOFException { checkAvailable(position, SHORT_SIZE_IN_BYTES); return Bits.readShort(data, position, byteOrder == ByteOrder.BIG_ENDIAN); } @Override public byte[] readByteArray() throws IOException { int len = readInt(); if (len == NULL_ARRAY_LENGTH) { return null; } if (len > 0) { byte[] b = new byte[len]; readFully(b); return b; } return EMPTY_BYTE_ARRAY; } @Override public boolean[] readBooleanArray() throws EOFException { int len = readInt(); if (len == NULL_ARRAY_LENGTH) { return null; } if (len > 0) { boolean[] values = new boolean[len]; for (int i = 0; i < len; i++) { values[i] = readBoolean(); } return values; } return new boolean[0]; } @Override public char[] readCharArray() throws EOFException { int len = readInt(); if (len == NULL_ARRAY_LENGTH) { return null; } if (len > 0) { char[] values = new char[len]; for (int i = 0; i < len; i++) { values[i] = readChar(); } return values; } return new char[0]; } @Override public int[] readIntArray() throws EOFException { int len = readInt(); if (len == NULL_ARRAY_LENGTH) { return null; } if (len > 0) { int[] values = new int[len]; for (int i = 0; i < len; i++) { values[i] = readInt(); } return values; } return new int[0]; } @Override public long[] readLongArray() throws EOFException { int len = readInt(); if (len == NULL_ARRAY_LENGTH) { return null; } if (len > 0) { long[] values = new long[len]; for (int i = 0; i < len; i++) { values[i] = readLong(); } return values; } return new long[0]; } @Override public double[] readDoubleArray() throws EOFException { int len = readInt(); if (len == NULL_ARRAY_LENGTH) { return null; } if (len > 0) { double[] values = new double[len]; for (int i = 0; i < len; i++) { values[i] = readDouble(); } return values; } return new double[0]; } @Override public float[] readFloatArray() throws EOFException { int len = readInt(); if (len == NULL_ARRAY_LENGTH) { return null; } if (len > 0) { float[] values = new float[len]; for (int i = 0; i < len; i++) { values[i] = readFloat(); } return values; } return new float[0]; } @Override public short[] readShortArray() throws EOFException { int len = readInt(); if (len == NULL_ARRAY_LENGTH) { return null; } if (len > 0) { short[] values = new short[len]; for (int i = 0; i < len; i++) { values[i] = readShort(); } return values; } return new short[0]; } @Override public String[] readUTFArray() throws IOException { int len = readInt(); if (len == NULL_ARRAY_LENGTH) { return null; } if (len > 0) { String[] values = new String[len]; for (int i = 0; i < len; i++) { values[i] = readUTF(); } return values; } return new String[0]; } /** * See the general contract of the <code>readUnsignedByte</code> method of * <code>DataInput</code>. * <p/> * Bytes for this operation are read from the contained input stream. * * @return the next byte of this input stream, interpreted as an unsigned * 8-bit number. * @throws java.io.EOFException if this input stream has reached the end. * @throws java.io.IOException if an I/O error occurs. * @see java.io.FilterInputStream#in */ @Override public int readUnsignedByte() throws EOFException { return readByte() & 0xFF; } /** * See the general contract of the <code>readUnsignedShort</code> method of * <code>DataInput</code>. * <p/> * Bytes for this operation are read from the contained input stream. * * @return the next two bytes of this input stream, interpreted as an * unsigned 16-bit integer. * @throws java.io.EOFException if this input stream reaches the end before reading two * bytes. * @throws java.io.IOException if an I/O error occurs. * @see java.io.FilterInputStream#in */ @Override public int readUnsignedShort() throws EOFException { return readShort() & 0xffff; } /** * See the general contract of the <code>readUTF</code> method of * <code>DataInput</code>. * <p/> * Bytes for this operation are read from the contained input stream. * * @return a Unicode string. * @throws java.io.EOFException if this input stream reaches the end before reading all * the bytes. * @throws java.io.IOException if an I/O error occurs. * @throws java.io.UTFDataFormatException if the bytes do not represent a valid modified UTF-8 * encoding of a string. * @see java.io.DataInputStream#readUTF(java.io.DataInput) */ @Override public final String readUTF() throws IOException { int charCount = readInt(); if (charCount == NULL_ARRAY_LENGTH) { return null; } if (charBuffer == null || charCount > charBuffer.length) { charBuffer = new char[charCount]; } byte b; for (int i = 0; i < charCount; i++) { b = readByte(); if (b < 0) { charBuffer[i] = Bits.readUtf8Char(this, b); } else { charBuffer[i] = (char) b; } } return new String(charBuffer, 0, charCount); } @Override public final Object readObject() throws EOFException { return service.readObject(this); } @Override public <T> T readObject(Class aClass) throws IOException { return service.readObject(this, aClass); } @Override public <T> T readDataAsObject() throws IOException { // a future optimization would be to skip the construction of the Data object. Data data = readData(); return data == null ? null : (T) service.toObject(data); } @Override public final Data readData() throws IOException { byte[] bytes = readByteArray(); return bytes == null ? null : new HeapData(bytes); } @Override public final long skip(long n) { if (n <= 0 || n >= Integer.MAX_VALUE) { return 0L; } return skipBytes((int) n); } @Override public final int skipBytes(final int n) { if (n <= 0) { return 0; } int skip = n; final int pos = position(); if (pos + skip > size) { skip = size - pos; } position(pos + skip); return skip; } /** * Returns this buffer's position. */ @Override public final int position() { return pos; } @Override public final void position(int newPos) { if ((newPos > size) || (newPos < 0)) { throw new IllegalArgumentException(); } pos = newPos; if (mark > pos) { mark = -1; } } final void checkAvailable(int pos, int k) throws EOFException { if (pos < 0) { throw new IllegalArgumentException("Negative pos! -> " + pos); } if ((size - pos) < k) { throw new EOFException("Cannot read " + k + " bytes!"); } } @Override public final int available() { return size - pos; } @Override public final boolean markSupported() { return true; } @Override public final void mark(int readlimit) { mark = pos; } @Override public final void reset() { pos = mark; } @Override public final void close() { data = null; charBuffer = null; } @Override public final ClassLoader getClassLoader() { return service.getClassLoader(); } public ByteOrder getByteOrder() { return bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN; } @Override public String toString() { return "ByteArrayObjectDataInput{" + "size=" + size + ", pos=" + pos + ", mark=" + mark + '}'; } }