Mercurial > hg > Members > sugi > MessagePack-java
view src/main/java/org/msgpack/unpacker/MessagePackUnpacker.java @ 5:efe621b1dba9
bug fix
author | sugi |
---|---|
date | Fri, 09 Jan 2015 14:19:23 +0900 |
parents | d5cc3aef5b74 |
children | 8d782f988d92 |
line wrap: on
line source
// // 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(); } public byte[] getSerializedByteArray(int len) throws IOException { return ((StreamInput)in).getRawByteArray(len); } }