/** * 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. */ package org.apache.trevni; import java.io.EOFException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; /** Used to read values. */ class InputBuffer { private Input in; private long inLength; private long offset; // pos of next read from in private byte[] buf; // data from input private int pos; // position within buffer private int limit; // end of valid buffer data private CharsetDecoder utf8 = Charset.forName("UTF-8").newDecoder(); private int bitCount; // position in booleans private int runLength; // length of run private int runValue; // value of run public InputBuffer(Input in) throws IOException { this(in, 0); } public InputBuffer(Input in, long position) throws IOException { this.in = in; this.inLength = in.length(); this.offset = position; if (in instanceof InputBytes) { // use buffer directly this.buf = ((InputBytes)in).getBuffer(); this.limit = (int)in.length(); this.offset = limit; this.pos = (int)position; } else { // create new buffer this.buf = new byte[8192]; // big enough for primitives } } public void seek(long position) throws IOException { runLength = 0; if (position >= (offset-limit) && position <= offset) { pos = (int)(limit - (offset - position)); // seek in buffer; return; } pos = 0; limit = 0; offset = position; } public long tell() { return (offset-limit)+pos; } public long length() { return inLength; } public <T extends Comparable> T readValue(ValueType type) throws IOException { switch (type) { case NULL: return (T)null; case BOOLEAN: return (T)Boolean.valueOf(readBoolean()); case INT: return (T)Integer.valueOf(readInt()); case LONG: return (T)Long.valueOf(readLong()); case FIXED32: return (T)Integer.valueOf(readFixed32()); case FIXED64: return (T)Long.valueOf(readFixed64()); case FLOAT: return (T)Float.valueOf(readFloat()); case DOUBLE: return (T)Double.valueOf(readDouble()); case STRING: return (T)readString(); case BYTES: return (T)readBytes(null); default: throw new TrevniRuntimeException("Unknown value type: "+type); } } public void skipValue(ValueType type) throws IOException { switch (type) { case NULL: break; case BOOLEAN: readBoolean(); break; case INT: readInt(); break; case LONG: readLong(); break; case FIXED32: case FLOAT: skip(4); break; case FIXED64: case DOUBLE: skip(8); break; case STRING: case BYTES: skipBytes(); break; default: throw new TrevniRuntimeException("Unknown value type: "+type); } } public boolean readBoolean() throws IOException { if (bitCount == 0) read(); int bits = buf[pos-1] & 0xff; int bit = (bits >> bitCount) & 1; bitCount++; if (bitCount == 8) bitCount = 0; return bit == 0 ? false : true; } public int readLength() throws IOException { bitCount = 0; if (runLength > 0) { runLength--; // in run return runValue; } int length = readInt(); if (length >= 0) // not a run return length; runLength = (1-length)>>>1; // start of run runValue = (length+1) & 1; return runValue; } public int readInt() throws IOException { if ((limit - pos) < 5) { // maybe not in buffer int b = read(); int n = b & 0x7f; for (int shift = 7; b > 0x7f; shift += 7) { b = read(); n ^= (b & 0x7f) << shift; } return (n >>> 1) ^ -(n & 1); // back to two's-complement } int len = 1; int b = buf[pos] & 0xff; int n = b & 0x7f; if (b > 0x7f) { b = buf[pos + len++] & 0xff; n ^= (b & 0x7f) << 7; if (b > 0x7f) { b = buf[pos + len++] & 0xff; n ^= (b & 0x7f) << 14; if (b > 0x7f) { b = buf[pos + len++] & 0xff; n ^= (b & 0x7f) << 21; if (b > 0x7f) { b = buf[pos + len++] & 0xff; n ^= (b & 0x7f) << 28; if (b > 0x7f) { throw new IOException("Invalid int encoding"); } } } } } pos += len; if (pos > limit) throw new EOFException(); return (n >>> 1) ^ -(n & 1); // back to two's-complement } public long readLong() throws IOException { if ((limit - pos) < 10) { // maybe not in buffer int b = read(); long n = b & 0x7f; for (int shift = 7; b > 0x7f; shift += 7) { b = read(); n ^= (b & 0x7fL) << shift; } return (n >>> 1) ^ -(n & 1); // back to two's-complement } int b = buf[pos++] & 0xff; int n = b & 0x7f; long l; if (b > 0x7f) { b = buf[pos++] & 0xff; n ^= (b & 0x7f) << 7; if (b > 0x7f) { b = buf[pos++] & 0xff; n ^= (b & 0x7f) << 14; if (b > 0x7f) { b = buf[pos++] & 0xff; n ^= (b & 0x7f) << 21; if (b > 0x7f) { // only the low 28 bits can be set, so this won't carry // the sign bit to the long l = innerLongDecode((long)n); } else { l = n; } } else { l = n; } } else { l = n; } } else { l = n; } if (pos > limit) { throw new EOFException(); } return (l >>> 1) ^ -(l & 1); // back to two's-complement } // splitting readLong up makes it faster because of the JVM does more // optimizations on small methods private long innerLongDecode(long l) throws IOException { int len = 1; int b = buf[pos] & 0xff; l ^= (b & 0x7fL) << 28; if (b > 0x7f) { b = buf[pos + len++] & 0xff; l ^= (b & 0x7fL) << 35; if (b > 0x7f) { b = buf[pos + len++] & 0xff; l ^= (b & 0x7fL) << 42; if (b > 0x7f) { b = buf[pos + len++] & 0xff; l ^= (b & 0x7fL) << 49; if (b > 0x7f) { b = buf[pos + len++] & 0xff; l ^= (b & 0x7fL) << 56; if (b > 0x7f) { b = buf[pos + len++] & 0xff; l ^= (b & 0x7fL) << 63; if (b > 0x7f) { throw new IOException("Invalid long encoding"); } } } } } } pos += len; return l; } public float readFloat() throws IOException { return Float.intBitsToFloat(readFixed32()); } public int readFixed32() throws IOException { if ((limit - pos) < 4) // maybe not in buffer return read() | (read() << 8) | (read() << 16) | (read() << 24); int len = 1; int n = (buf[pos] & 0xff) | ((buf[pos + len++] & 0xff) << 8) | ((buf[pos + len++] & 0xff) << 16) | ((buf[pos + len++] & 0xff) << 24); if ((pos + 4) > limit) throw new EOFException(); pos += 4; return n; } public double readDouble() throws IOException { return Double.longBitsToDouble(readFixed64()); } public long readFixed64() throws IOException { return (readFixed32() & 0xFFFFFFFFL) | (((long)readFixed32()) << 32); } public String readString() throws IOException { int length = readInt(); if (length <= (limit - pos)) { // in buffer String result = utf8.decode(ByteBuffer.wrap(buf, pos, length)).toString(); pos += length; return result; } byte[] bytes = new byte[length]; readFully(bytes, 0, length); return utf8.decode(ByteBuffer.wrap(bytes, 0, length)).toString(); } public byte[] readBytes() throws IOException { byte[] result = new byte[readInt()]; readFully(result); return result; } public ByteBuffer readBytes(ByteBuffer old) throws IOException { int length = readInt(); ByteBuffer result; if (old != null && length <= old.capacity()) { result = old; result.clear(); } else { result = ByteBuffer.allocate(length); } readFully(result.array(), result.position(), length); result.limit(length); return result; } public void skipBytes() throws IOException { skip(readInt()); } private void skip(long length) throws IOException { seek(tell()+length); } public int read() throws IOException { if (pos >= limit) { limit = readInput(buf, 0, buf.length); pos = 0; } return buf[pos++] & 0xFF; } public void readFully(byte[] bytes) throws IOException { readFully(bytes, 0, bytes.length); } public void readFully(byte[] bytes, int start, int len) throws IOException { int buffered = limit - pos; if (len > buffered) { // buffer is insufficient System.arraycopy(buf, pos, bytes, start, buffered); // consume buffer start += buffered; len -= buffered; pos += buffered; if (len > buf.length) { // bigger than buffer do { int read = readInput(bytes, start, len); // read directly into result len -= read; start += read; } while (len > 0); return; } limit = readInput(buf, 0, buf.length); // refill buffer pos = 0; } System.arraycopy(buf, pos, bytes, start, len); // copy from buffer pos += len; } private int readInput(byte[] b, int start, int len) throws IOException { int read = in.read(offset, b, start, len); if (read < 0) throw new EOFException(); offset += read; return read; } }