Mercurial > hg > Members > sugi > MessagePack-java
diff src/main/java/org/msgpack/unpacker/MessagePackUnpacker.java @ 0:cb825acd883a
first commit
author | sugi |
---|---|
date | Sat, 18 Oct 2014 15:06:15 +0900 |
parents | |
children | 769ba8da0840 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/org/msgpack/unpacker/MessagePackUnpacker.java Sat Oct 18 15:06:15 2014 +0900 @@ -0,0 +1,685 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009 - 2013 FURUHASHI Sadayuki +// +// 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 org.msgpack.unpacker; + +import java.io.IOException; +import java.io.EOFException; +import java.io.InputStream; +import java.math.BigInteger; +import org.msgpack.io.Input; +import org.msgpack.io.StreamInput; +import org.msgpack.io.BufferReferer; +import org.msgpack.MessagePack; +import org.msgpack.MessageTypeException; +import org.msgpack.packer.Unconverter; +import org.msgpack.type.ValueType; + +public class MessagePackUnpacker extends AbstractUnpacker { + private static final byte REQUIRE_TO_READ_HEAD = (byte) 0xc1; + + protected final Input in; + private final UnpackerStack stack = new UnpackerStack(); + + private byte headByte = REQUIRE_TO_READ_HEAD; + + private byte[] raw; + private int rawFilled; + + private final IntAccept intAccept = new IntAccept(); + private final LongAccept longAccept = new LongAccept(); + private final BigIntegerAccept bigIntegerAccept = new BigIntegerAccept(); + private final DoubleAccept doubleAccept = new DoubleAccept(); + private final ByteArrayAccept byteArrayAccept = new ByteArrayAccept(); + private final StringAccept stringAccept = new StringAccept(); + private final ArrayAccept arrayAccept = new ArrayAccept(); + private final MapAccept mapAccept = new MapAccept(); + private final ValueAccept valueAccept = new ValueAccept(); + private final SkipAccept skipAccept = new SkipAccept(); + + public MessagePackUnpacker(MessagePack msgpack, InputStream stream) { + this(msgpack, new StreamInput(stream)); + } + + protected MessagePackUnpacker(MessagePack msgpack, Input in) { + super(msgpack); + this.in = in; + } + + private byte getHeadByte() throws IOException { + byte b = headByte; + if (b == REQUIRE_TO_READ_HEAD) { + b = headByte = in.readByte(); + } + return b; + } + + final void readOne(Accept a) throws IOException { + stack.checkCount(); + if (readOneWithoutStack(a)) { + stack.reduceCount(); + } + } + + final boolean readOneWithoutStack(Accept a) throws IOException { + if (raw != null) { + readRawBodyCont(); + a.acceptRaw(raw); + raw = null; + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + + final int b = (int) getHeadByte(); + + if ((b & 0x80) == 0) { // Positive Fixnum + // System.out.println("positive fixnum "+b); + a.acceptInteger(b); + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + + if ((b & 0xe0) == 0xe0) { // Negative Fixnum + // System.out.println("negative fixnum "+b); + a.acceptInteger(b); + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + + if ((b & 0xe0) == 0xa0) { // FixRaw + int count = b & 0x1f; + if (count == 0) { + a.acceptEmptyRaw(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + if (!tryReferRawBody(a, count)) { + readRawBody(count); + a.acceptRaw(raw); + raw = null; + } + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + + if ((b & 0xf0) == 0x90) { // FixArray + int count = b & 0x0f; + // System.out.println("fixarray count:"+count); + a.acceptArray(count); + stack.reduceCount(); + stack.pushArray(count); + headByte = REQUIRE_TO_READ_HEAD; + return false; + } + + if ((b & 0xf0) == 0x80) { // FixMap + int count = b & 0x0f; + // System.out.println("fixmap count:"+count/2); + a.acceptMap(count); + stack.reduceCount(); + stack.pushMap(count); + headByte = REQUIRE_TO_READ_HEAD; + return false; + } + + return readOneWithoutStackLarge(a, b); + } + + private boolean readOneWithoutStackLarge(Accept a, final int b) + throws IOException { + switch (b & 0xff) { + case 0xc0: // nil + a.acceptNil(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + case 0xc2: // false + a.acceptBoolean(false); + headByte = REQUIRE_TO_READ_HEAD; + return true; + case 0xc3: // true + a.acceptBoolean(true); + headByte = REQUIRE_TO_READ_HEAD; + return true; + //case 0xc4: // bin 8 -> see 0xd9 + //case 0xc5: // bin 16 -> see 0xda + //case 0xc6: // bin 32 -> see 0xdb + case 0xca: // float + a.acceptFloat(in.getFloat()); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + case 0xcb: // double + a.acceptDouble(in.getDouble()); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + case 0xcc: // unsigned int 8 + a.acceptUnsignedInteger(in.getByte()); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + case 0xcd: // unsigned int 16 + a.acceptUnsignedInteger(in.getShort()); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + case 0xce: // unsigned int 32 + a.acceptUnsignedInteger(in.getInt()); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + case 0xcf: // unsigned int 64 + a.acceptUnsignedInteger(in.getLong()); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + case 0xd0: // signed int 8 + a.acceptInteger(in.getByte()); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + case 0xd1: // signed int 16 + a.acceptInteger(in.getShort()); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + case 0xd2: // signed int 32 + a.acceptInteger(in.getInt()); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + case 0xd3: // signed int 64 + a.acceptInteger(in.getLong()); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + case 0xc4: // bin 8 + case 0xd9: // str 8 + { + int count = in.getByte() & 0xff; + if (count == 0) { + a.acceptEmptyRaw(); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + if (count >= rawSizeLimit) { + String reason = String.format( + "Size of raw (%d) over limit at %d", + new Object[] { count, rawSizeLimit }); + throw new SizeLimitException(reason); + } + in.advance(); + if (!tryReferRawBody(a, count)) { + readRawBody(count); + a.acceptRaw(raw); + raw = null; + } + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + case 0xc5: // bin 16 + case 0xda: // raw 16 + { + int count = in.getShort() & 0xffff; + if (count == 0) { + a.acceptEmptyRaw(); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + if (count >= rawSizeLimit) { + String reason = String.format( + "Size of raw (%d) over limit at %d", + new Object[] { count, rawSizeLimit }); + throw new SizeLimitException(reason); + } + in.advance(); + if (!tryReferRawBody(a, count)) { + readRawBody(count); + a.acceptRaw(raw); + raw = null; + } + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + case 0xc6: // bin 32 + case 0xdb: // raw 32 + { + int count = in.getInt(); + if (count == 0) { + a.acceptEmptyRaw(); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + if (count < 0 || count >= rawSizeLimit) { + String reason = String.format( + "Size of raw (%d) over limit at %d", + new Object[] { count, rawSizeLimit }); + throw new SizeLimitException(reason); + } + in.advance(); + if (!tryReferRawBody(a, count)) { + readRawBody(count); + a.acceptRaw(raw); + raw = null; + } + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + case 0xdc: // array 16 + { + int count = in.getShort() & 0xffff; + if (count >= arraySizeLimit) { + String reason = String.format( + "Size of array (%d) over limit at %d", + new Object[] { count, arraySizeLimit }); + throw new SizeLimitException(reason); + } + a.acceptArray(count); + stack.reduceCount(); + stack.pushArray(count); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return false; + } + case 0xdd: // array 32 + { + int count = in.getInt(); + if (count < 0 || count >= arraySizeLimit) { + String reason = String.format( + "Size of array (%d) over limit at %d", + new Object[] { count, arraySizeLimit }); + throw new SizeLimitException(reason); + } + a.acceptArray(count); + stack.reduceCount(); + stack.pushArray(count); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return false; + } + case 0xde: // map 16 + { + int count = in.getShort() & 0xffff; + if (count >= mapSizeLimit) { + String reason = String.format( + "Size of map (%d) over limit at %d", + new Object[] { count, mapSizeLimit }); + throw new SizeLimitException(reason); + } + a.acceptMap(count); + stack.reduceCount(); + stack.pushMap(count); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return false; + } + case 0xdf: // map 32 + { + int count = in.getInt(); + if (count < 0 || count >= mapSizeLimit) { + String reason = String.format( + "Size of map (%d) over limit at %d", + new Object[] { count, mapSizeLimit }); + throw new SizeLimitException(reason); + } + a.acceptMap(count); + stack.reduceCount(); + stack.pushMap(count); + in.advance(); + headByte = REQUIRE_TO_READ_HEAD; + return false; + } + default: + // System.out.println("unknown b "+(b&0xff)); + // headByte = CS_INVALID + headByte = REQUIRE_TO_READ_HEAD; + throw new IOException("Invalid byte: " + b); // TODO error FormatException + } + } + + private boolean tryReferRawBody(BufferReferer referer, int size) throws IOException { + return in.tryRefer(referer, size); + } + + private void readRawBody(int size) throws IOException { + raw = new byte[size]; + rawFilled = 0; + readRawBodyCont(); + } + + private void readRawBodyCont() throws IOException { + int len = in.read(raw, rawFilled, raw.length - rawFilled); + rawFilled += len; + if (rawFilled < raw.length) { + throw new EOFException(); + } + } + + @Override + protected boolean tryReadNil() throws IOException { + stack.checkCount(); + int b = getHeadByte() & 0xff; + if (b == 0xc0) { + // nil is read + stack.reduceCount(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + // not nil + return false; + } + + @Override + public boolean trySkipNil() throws IOException { + if (stack.getDepth() > 0 && stack.getTopCount() <= 0) { + // end of array or map + return true; + } + + int b = getHeadByte() & 0xff; + if (b == 0xc0) { + // nil is skipped + stack.reduceCount(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + // not nil + return false; + } + + @Override + public void readNil() throws IOException { + // optimized not to allocate nilAccept + stack.checkCount(); + int b = getHeadByte() & 0xff; + if (b == 0xc0) { + stack.reduceCount(); + headByte = REQUIRE_TO_READ_HEAD; + return; + } + throw new MessageTypeException("Expected nil but got not nil value"); + } + + @Override + public boolean readBoolean() throws IOException { + // optimized not to allocate booleanAccept + stack.checkCount(); + int b = getHeadByte() & 0xff; + if (b == 0xc2) { + stack.reduceCount(); + headByte = REQUIRE_TO_READ_HEAD; + return false; + } else if (b == 0xc3) { + stack.reduceCount(); + headByte = REQUIRE_TO_READ_HEAD; + return true; + } + throw new MessageTypeException( + "Expected Boolean but got not boolean value"); + } + + @Override + public byte readByte() throws IOException { + // optimized not to allocate byteAccept + stack.checkCount(); + readOneWithoutStack(intAccept); + int value = intAccept.value; + if (value < (int) Byte.MIN_VALUE || value > (int) Byte.MAX_VALUE) { + throw new MessageTypeException(); // TODO message + } + stack.reduceCount(); + return (byte) value; + } + + @Override + public short readShort() throws IOException { + // optimized not to allocate shortAccept + stack.checkCount(); + readOneWithoutStack(intAccept); + int value = intAccept.value; + if (value < (int) Short.MIN_VALUE || value > (int) Short.MAX_VALUE) { + throw new MessageTypeException(); // TODO message + } + stack.reduceCount(); + return (short) value; + } + + @Override + public int readInt() throws IOException { + readOne(intAccept); + return intAccept.value; + } + + @Override + public long readLong() throws IOException { + readOne(longAccept); + return longAccept.value; + } + + @Override + public BigInteger readBigInteger() throws IOException { + readOne(bigIntegerAccept); + return bigIntegerAccept.value; + } + + @Override + public float readFloat() throws IOException { + readOne(doubleAccept); + return (float) doubleAccept.value; + } + + @Override + public double readDouble() throws IOException { + readOne(doubleAccept); + return doubleAccept.value; + } + + @Override + public byte[] readByteArray() throws IOException { + readOne(byteArrayAccept); + return byteArrayAccept.value; + } + + @Override + public String readString() throws IOException { + readOne(stringAccept); + return stringAccept.value; + } + + @Override + public int readArrayBegin() throws IOException { + readOne(arrayAccept); + return arrayAccept.size; + } + + @Override + public void readArrayEnd(boolean check) throws IOException { + if (!stack.topIsArray()) { + throw new MessageTypeException( + "readArrayEnd() is called but readArrayBegin() is not called"); + } + + int remain = stack.getTopCount(); + if (remain > 0) { + if (check) { + throw new MessageTypeException( + "readArrayEnd(check=true) is called but the array is not end"); + } + for (int i = 0; i < remain; i++) { + skip(); + } + } + stack.pop(); + } + + @Override + public int readMapBegin() throws IOException { + readOne(mapAccept); + return mapAccept.size; + } + + @Override + public void readMapEnd(boolean check) throws IOException { + if (!stack.topIsMap()) { + throw new MessageTypeException( + "readMapEnd() is called but readMapBegin() is not called"); + } + + int remain = stack.getTopCount(); + if (remain > 0) { + if (check) { + throw new MessageTypeException( + "readMapEnd(check=true) is called but the map is not end"); + } + for (int i = 0; i < remain; i++) { + skip(); + } + } + stack.pop(); + } + + @Override + protected void readValue(Unconverter uc) throws IOException { + if (uc.getResult() != null) { + uc.resetResult(); + } + valueAccept.setUnconverter(uc); + + stack.checkCount(); + if (readOneWithoutStack(valueAccept)) { + stack.reduceCount(); + if (uc.getResult() != null) { + return; + } + } + while (true) { + while (stack.getTopCount() == 0) { + if (stack.topIsArray()) { + uc.writeArrayEnd(true); + stack.pop(); + // stack.reduceCount(); + } else if (stack.topIsMap()) { + uc.writeMapEnd(true); + stack.pop(); + // stack.reduceCount(); + } else { + throw new RuntimeException("invalid stack"); // FIXME error? + } + if (uc.getResult() != null) { + return; + } + } + readOne(valueAccept); + } + } + + @Override + public void skip() throws IOException { + stack.checkCount(); + if (readOneWithoutStack(skipAccept)) { + stack.reduceCount(); + return; + } + int targetDepth = stack.getDepth() - 1; + while (true) { + while (stack.getTopCount() == 0) { + stack.pop(); + if (stack.getDepth() <= targetDepth) { + return; + } + } + readOne(skipAccept); + } + } + + public ValueType getNextType() throws IOException { + final int b = (int) getHeadByte(); + if ((b & 0x80) == 0) { // Positive Fixnum + return ValueType.INTEGER; + } + if ((b & 0xe0) == 0xe0) { // Negative Fixnum + return ValueType.INTEGER; + } + if ((b & 0xe0) == 0xa0) { // FixRaw + return ValueType.RAW; + } + if ((b & 0xf0) == 0x90) { // FixArray + return ValueType.ARRAY; + } + if ((b & 0xf0) == 0x80) { // FixMap + return ValueType.MAP; + } + switch (b & 0xff) { + case 0xc0: // nil + return ValueType.NIL; + case 0xc2: // false + case 0xc3: // true + return ValueType.BOOLEAN; + case 0xca: // float + case 0xcb: // double + return ValueType.FLOAT; + case 0xcc: // unsigned int 8 + case 0xcd: // unsigned int 16 + case 0xce: // unsigned int 32 + case 0xcf: // unsigned int 64 + case 0xd0: // signed int 8 + case 0xd1: // signed int 16 + case 0xd2: // signed int 32 + case 0xd3: // signed int 64 + return ValueType.INTEGER; + // The definition based on a minor upgrade guide. + // https://github.com/msgpack/msgpack/blob/master/spec.md#impl-upgrade + case 0xc4: // bin 8 + case 0xc5: // bin 16 + case 0xc6: // bin 32 + case 0xd9: // str8 + case 0xda: // raw 16 + case 0xdb: // raw 32 + return ValueType.RAW; + case 0xdc: // array 16 + case 0xdd: // array 32 + return ValueType.ARRAY; + case 0xde: // map 16 + case 0xdf: // map 32 + return ValueType.MAP; + default: + throw new IOException("Invalid byte: " + b); // TODO error FormatException + } + } + + public void reset() { + raw = null; + stack.clear(); + } + + public void close() throws IOException { + in.close(); + } + + @Override + public int getReadByteCount() { + return in.getReadByteCount(); + } + + @Override + public void resetReadByteCount() { + in.resetReadByteCount(); + } +}