| /* |
| * 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 |
| * |
| * https://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.Buffer; |
| 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 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; |
| ((Buffer) result).clear(); |
| } else { |
| result = ByteBuffer.allocate(length); |
| } |
| readFully(result.array(), result.position(), length); |
| ((Buffer) 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; |
| } |
| |
| } |