/* * Copyright (C) 2015 SoftIndex LLC. * * 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 io.datakernel.serializer; import io.datakernel.bytebuf.ByteBuf; import io.datakernel.bytebuf.ByteBufPool; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; public final class DataInputStreamEx implements Closeable { public static final int DEFAULT_BUFFER_SIZE = 65536; private InputStream inputStream; private ByteBuf buf; private char[] charArray = new char[128]; private DataInputStreamEx(InputStream inputStream, int bufferSize) { this.inputStream = inputStream; this.buf = ByteBufPool.allocate(bufferSize); } public static DataInputStreamEx create(InputStream inputStream) {return new DataInputStreamEx(inputStream, DEFAULT_BUFFER_SIZE);} public static DataInputStreamEx create(InputStream inputStream, int bufferSize) {return new DataInputStreamEx(inputStream, bufferSize);} public void changeInputStream(InputStream inputStream) throws IOException { if (this.inputStream != null) { this.inputStream.close(); } this.inputStream = inputStream; } private void ensureReadSize(int size) { if (buf.readRemaining() + buf.writeRemaining() >= size) { return; } int headPos = buf.readPosition(); int remainingToRead = buf.readRemaining(); ByteBuf newBuf = buf.limit() >= size ? buf : ByteBufPool.allocate(size + size / 2); System.arraycopy(buf.array(), headPos, newBuf.array(), 0, remainingToRead); newBuf.readPosition(0); newBuf.writePosition(remainingToRead); if (buf != newBuf) { buf.recycle(); buf = newBuf; } } private void doEnsureRead(int size) throws IOException { while (buf.readRemaining() < size) { ensureReadSize(size); int bytesRead = inputStream.read(buf.array(), buf.writePosition(), buf.writeRemaining()); if (bytesRead == -1) throw new IOException("Could not read message"); buf.moveWritePosition(bytesRead); } } private void ensureRead(int size) throws IOException { if (buf.readRemaining() < size) { doEnsureRead(size); } } private int readSize() throws IOException { int result; byte b = readByte(); if (b >= 0) { result = b; } else { result = b & 0x7f; if ((b = readByte()) >= 0) { result |= b << 7; } else { result |= (b & 0x7f) << 7; if ((b = readByte()) >= 0) { result |= b << 14; } else { throw new IOException(); } } } return result; } @Override public void close() throws IOException { inputStream.close(); buf.recycle(); } public boolean isEndOfStream() throws IOException { if (buf.canRead()) { return false; } else { int bytesRead = inputStream.read(buf.array(), 0, buf.limit()); if (bytesRead == -1) return true; assert buf.limit() != 0 && bytesRead != 0; buf.readPosition(0); buf.writePosition(bytesRead); return false; } } public <T> T deserialize(BufferSerializer<T> serializer) throws IOException, DeserializeException { int messageSize = readSize(); ensureRead(messageSize); int oldHead = buf.readPosition(); T item; try { item = serializer.deserialize(buf); } catch (Exception e) { throw new DeserializeException(e); } if (buf.readPosition() - oldHead != messageSize) { throw new DeserializeException("Deserialized size != parsed data size"); } return item; } private char[] ensureCharArray(int length) { if (charArray.length < length) { charArray = new char[length + (length >>> 2)]; } return charArray; } public int read(byte[] b) throws IOException { return read(b, 0, b.length); } public int read(byte[] b, int off, int len) throws IOException { ensureRead(len); buf.drainTo(b, off, len); return len; } public byte readByte() throws IOException { ensureRead(1); return buf.get(); } public boolean readBoolean() throws IOException { return readByte() != 0; } public short readShort() throws IOException { ensureRead(2); short result = (short) (((buf.peek(0) & 0xFF) << 8) | (buf.peek(1) & 0xFF)); buf.moveReadPosition(2); return result; } public int readInt() throws IOException { ensureRead(4); int result = ((buf.peek(0) & 0xFF) << 24) | ((buf.peek(1) & 0xFF) << 16) | ((buf.peek(2) & 0xFF) << 8) | (buf.peek(3) & 0xFF); buf.moveReadPosition(4); return result; } public long readLong() throws IOException { ensureRead(8); long result = ((long) buf.peek(0) << 56) | ((long) (buf.peek(1) & 0xFF) << 48) | ((long) (buf.peek(2) & 0xFF) << 40) | ((long) (buf.peek(3) & 0xFF) << 32) | ((long) (buf.peek(4) & 0xFF) << 24) | ((buf.peek(5) & 0xFF) << 16) | ((buf.peek(6) & 0xFF) << 8) | ((buf.peek(7) & 0xFF)); buf.moveReadPosition(8); return result; } public int readVarInt() throws IOException { int result; byte b = readByte(); if (b >= 0) { result = b; } else { result = b & 0x7f; if ((b = readByte()) >= 0) { result |= b << 7; } else { result |= (b & 0x7f) << 7; if ((b = readByte()) >= 0) { result |= b << 14; } else { result |= (b & 0x7f) << 14; if ((b = readByte()) >= 0) { result |= b << 21; } else { result |= (b & 0x7f) << 21; if ((b = readByte()) >= 0) { result |= b << 28; } else throw new IllegalArgumentException(); } } } } return result; } public long readVarLong() throws IOException { long result = 0; for (int offset = 0; offset < 64; offset += 7) { byte b = readByte(); result |= (long) (b & 0x7F) << offset; if ((b & 0x80) == 0) return result; } throw new IllegalArgumentException(); } public float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); } public double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); } public char readChar() throws IOException { ensureRead(2); char c = (char) (((buf.peek(0) & 0xFF) << 8) | ((buf.peek(1) & 0xFF))); buf.moveReadPosition(2); return c; } public String readUTF8() throws IOException { int length = readVarInt(); if (length == 0) return ""; ensureRead(length); buf.moveReadPosition(length); try { return new String(buf.array(), buf.readPosition() - length, length, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(); } } public String readIso88591() throws IOException { int length = readVarInt(); if (length == 0) return ""; ensureRead(length); char[] chars = ensureCharArray(length); for (int i = 0; i < length; i++) { int c = readByte() & 0xff; chars[i] = (char) c; } return new String(chars, 0, length); } public String readUTF16() throws IOException { int length = readVarInt(); if (length == 0) return ""; ensureRead(length * 2); char[] chars = ensureCharArray(length); for (int i = 0; i < length; i++) { byte b1 = buf.get(); byte b2 = buf.get(); chars[i] = (char) (((b1 & 0xFF) << 8) + (b2 & 0xFF)); } return new String(chars, 0, length); } }