package com.ctriposs.baiji.io; import com.ctriposs.baiji.exception.BaijiRuntimeException; import java.io.IOException; import java.io.InputStream; /** * An {@link Decoder} for binary-format data. * <p/> * This class may read-ahead and buffer bytes from the source beyond what is * required to serve its read methods. * * @see Encoder */ public class BinaryDecoder implements Decoder { private final InputStream _stream; public BinaryDecoder(InputStream stream) { _stream = stream; } @Override public void readNull() throws IOException { } @Override public boolean readBoolean() throws IOException { byte b = read(); if (b == 0) { return false; } if (b == 1) { return true; } throw new BaijiRuntimeException("Not a boolean value in the stream: " + b); } @Override public int readInt() throws IOException { return (int) readLong(); } @Override public long readLong() throws IOException { byte b = read(); long n = b & 0x7FL; int shift = 7; while ((b & 0x80) != 0) { b = read(); n |= (b & 0x7FL) << shift; shift += 7; } long value = n; return (-(value & 0x01L)) ^ ((value >> 1) & 0x7fffffffffffffffL); } @Override public float readFloat() throws IOException { int bits = (_stream.read() & 0xff) | (_stream.read() & 0xff) << 8 | (_stream.read() & 0xff) << 16 | (_stream.read() & 0xff) << 24; return Float.intBitsToFloat(bits); } @Override public double readDouble() throws IOException { long bits = (_stream.read() & 0xffL) | (_stream.read() & 0xffL) << 8 | (_stream.read() & 0xffL) << 16 | (_stream.read() & 0xffL) << 24 | (_stream.read() & 0xffL) << 32 | (_stream.read() & 0xffL) << 40 | (_stream.read() & 0xffL) << 48 | (_stream.read() & 0xffL) << 56; return Double.longBitsToDouble(bits); } @Override public byte[] readBytes() throws IOException { return read(readLong()); } @Override public String readString() throws IOException { int length = readInt(); byte[] buffer = new byte[length]; read(buffer, 0, length); return new String(buffer, 0, length, "utf-8"); } @Override public int readEnum() throws IOException { return readInt(); } @Override public long readArrayStart() throws IOException { return doReadItemCount(); } @Override public long readArrayNext() throws IOException { return doReadItemCount(); } @Override public long readMapStart() throws IOException { return doReadItemCount(); } @Override public long readMapNext() throws IOException { return doReadItemCount(); } @Override public int readUnionIndex() throws IOException { return readInt(); } // read p bytes into a new byte buffer private byte[] read(long p) throws IOException { return read((int) p); } private byte[] read(int p) throws IOException { byte[] buffer = new byte[p]; read(buffer, 0, buffer.length); return buffer; } private byte read() throws IOException { int n = _stream.read(); if (n >= 0) { return (byte) n; } throw new BaijiRuntimeException("End of stream reached"); } private void read(byte[] buffer, int start, int len) throws IOException { while (len > 0) { int n = _stream.read(buffer, start, len); if (n <= 0) { throw new BaijiRuntimeException("End of stream reached"); } start += n; len -= n; } } private long doReadItemCount() throws IOException { long result = readLong(); if (result < 0) { readLong(); // Consume byte-count if present result = -result; } return result; } }