From 979ff809827ab25005364dad41d2fd043b8eaa4d Mon Sep 17 00:00:00 2001 From: frsyuki Date: Thu, 20 May 2010 03:49:26 +0900 Subject: [PATCH] java: redesign --- cpp/msgpack/type/nil.hpp | 8 + .../org/msgpack/BufferedUnpackerImpl.java | 384 ++++++++++++++++++ ...Mergeable.java => MessageConvertable.java} | 4 +- .../org/msgpack/MessageTypeException.java | 4 +- .../java/org/msgpack/MessageUnpackable.java | 25 ++ java/src/main/java/org/msgpack/Packer.java | 2 +- .../java/org/msgpack/UnbufferedUnpacker.java | 82 ---- .../main/java/org/msgpack/UnpackCursor.java | 96 +++++ .../main/java/org/msgpack/UnpackIterator.java | 24 +- .../main/java/org/msgpack/UnpackResult.java | 42 ++ java/src/main/java/org/msgpack/Unpacker.java | 269 ++++++------ .../org/msgpack/{impl => }/UnpackerImpl.java | 26 +- .../org/msgpack/TestDirectConversion.java | 154 +++++++ .../test/java/org/msgpack/TestPackUnpack.java | 2 +- 14 files changed, 864 insertions(+), 258 deletions(-) create mode 100644 java/src/main/java/org/msgpack/BufferedUnpackerImpl.java rename java/src/main/java/org/msgpack/{MessageMergeable.java => MessageConvertable.java} (86%) create mode 100644 java/src/main/java/org/msgpack/MessageUnpackable.java delete mode 100644 java/src/main/java/org/msgpack/UnbufferedUnpacker.java create mode 100644 java/src/main/java/org/msgpack/UnpackCursor.java create mode 100644 java/src/main/java/org/msgpack/UnpackResult.java rename java/src/main/java/org/msgpack/{impl => }/UnpackerImpl.java (97%) create mode 100644 java/src/test/java/org/msgpack/TestDirectConversion.java diff --git a/cpp/msgpack/type/nil.hpp b/cpp/msgpack/type/nil.hpp index e58bc9d4..f44e45e4 100644 --- a/cpp/msgpack/type/nil.hpp +++ b/cpp/msgpack/type/nil.hpp @@ -51,6 +51,14 @@ inline void operator<< (object::with_zone& o, type::nil v) { static_cast(o) << v; } +template <> +inline void object::as() const +{ + msgpack::type::nil v; + convert(&v); +} + + } // namespace msgpack #endif /* msgpack/type/nil.hpp */ diff --git a/java/src/main/java/org/msgpack/BufferedUnpackerImpl.java b/java/src/main/java/org/msgpack/BufferedUnpackerImpl.java new file mode 100644 index 00000000..0ef9c12e --- /dev/null +++ b/java/src/main/java/org/msgpack/BufferedUnpackerImpl.java @@ -0,0 +1,384 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 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; + +import java.io.IOException; +import java.nio.ByteBuffer; +//import java.math.BigInteger; + +abstract class BufferedUnpackerImpl extends UnpackerImpl { + int filled = 0; + byte[] buffer = null; + private ByteBuffer castBuffer = ByteBuffer.allocate(8); + + abstract boolean fill() throws IOException; + + final int next(int offset, UnpackResult result) throws IOException, UnpackException { + if(filled == 0) { + if(!fill()) { + return offset; + } + } + + do { + int noffset = super.execute(buffer, offset, filled); + if(noffset <= offset) { + if(!fill()) { + return offset; + } + } + offset = noffset; + } while(!super.isFinished()); + + Object obj = super.getData(); + super.reset(); + result.done(obj); + + return offset; + } + + private final void more(int offset, int require) throws IOException, UnpackException { + while(filled - offset < require) { + if(!fill()) { + // FIXME + throw new UnpackException("insufficient buffer"); + } + } + } + + final byte unpackByte(UnpackCursor c, int offset) throws IOException, MessageTypeException { + int o = unpackInt(c, offset); + if(0x7f < o || o < -0x80) { + throw new MessageTypeException(); + } + return (byte)o; + } + + final short unpackShort(UnpackCursor c, int offset) throws IOException, MessageTypeException { + int o = unpackInt(c, offset); + if(0x7fff < o || o < -0x8000) { + throw new MessageTypeException(); + } + return (short)o; + } + + final int unpackInt(UnpackCursor c, int offset) throws IOException, MessageTypeException { + more(offset, 1); + int b = buffer[offset]; + if((b & 0x80) == 0 || (b & 0xe0) == 0xe0) { // Fixnum + return (int)b; + } + switch(b & 0xff) { + case 0xcc: // unsigned int 8 + more(offset, 2); + c.advance(2); + return (int)((short)buffer[offset+1] & 0xff); + case 0xcd: // unsigned int 16 + more(offset, 3); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 2); + c.advance(3); + return (int)((int)castBuffer.getShort(0) & 0xffff); + case 0xce: // unsigned int 32 + more(offset, 5); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 4); + { + int o = castBuffer.getInt(0); + if(o < 0) { + throw new MessageTypeException(); + } + c.advance(5); + return o; + } + case 0xcf: // unsigned int 64 + more(offset, 9); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 8); + { + long o = castBuffer.getLong(0); + if(o < 0 || o > 0x7fffffffL) { + throw new MessageTypeException(); + } + c.advance(9); + return (int)o; + } + case 0xd0: // signed int 8 + more(offset, 2); + c.advance(2); + return (int)buffer[offset+1]; + case 0xd1: // signed int 16 + more(offset, 3); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 2); + c.advance(3); + return (int)castBuffer.getShort(0); + case 0xd2: // signed int 32 + more(offset, 4); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 4); + c.advance(4); + return (int)castBuffer.getInt(0); + case 0xd3: // signed int 64 + more(offset, 9); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 8); + { + long o = castBuffer.getLong(0); + if(0x7fffffffL < o || o < -0x80000000L) { + throw new MessageTypeException(); + } + c.advance(9); + return (int)o; + } + default: + throw new MessageTypeException(); + } + } + + final long unpackLong(UnpackCursor c, int offset) throws IOException, MessageTypeException { + more(offset, 1); + int b = buffer[offset]; + if((b & 0x80) == 0 || (b & 0xe0) == 0xe0) { // Fixnum + return (long)b; + } + switch(b & 0xff) { + case 0xcc: // unsigned int 8 + more(offset, 2); + c.advance(2); + return (long)((short)buffer[offset+1] & 0xff); + case 0xcd: // unsigned int 16 + more(offset, 3); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 2); + c.advance(3); + return (long)((int)castBuffer.getShort(0) & 0xffff); + case 0xce: // unsigned int 32 + more(offset, 5); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 4); + c.advance(5); + return ((long)castBuffer.getInt(0) & 0xffffffffL); + case 0xcf: // unsigned int 64 + more(offset, 9); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 8); + { + long o = castBuffer.getLong(0); + if(o < 0) { + // FIXME + throw new MessageTypeException("uint 64 bigger than 0x7fffffff is not supported"); + } + c.advance(9); + return o; + } + case 0xd0: // signed int 8 + more(offset, 2); + c.advance(2); + return (long)buffer[offset+1]; + case 0xd1: // signed int 16 + more(offset, 3); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 2); + c.advance(3); + return (long)castBuffer.getShort(0); + case 0xd2: // signed int 32 + more(offset, 4); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 4); + c.advance(4); + return (long)castBuffer.getInt(0); + case 0xd3: // signed int 64 + more(offset, 9); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 8); + c.advance(9); + return (long)castBuffer.getLong(0); + default: + throw new MessageTypeException(); + } + } + + final float unpackFloat(UnpackCursor c, int offset) throws IOException, MessageTypeException { + more(offset, 1); + int b = buffer[offset]; + switch(b & 0xff) { + case 0xca: // float + more(offset, 5); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 4); + c.advance(5); + return castBuffer.getFloat(0); + case 0xcb: // double + more(offset, 9); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 8); + c.advance(9); + // FIXME overflow check + return (float)castBuffer.getDouble(0); + default: + throw new MessageTypeException(); + } + } + + final double unpackDouble(UnpackCursor c, int offset) throws IOException, MessageTypeException { + more(offset, 1); + int b = buffer[offset]; + switch(b & 0xff) { + case 0xca: // float + more(offset, 5); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 4); + c.advance(5); + return (double)castBuffer.getFloat(0); + case 0xcb: // double + more(offset, 9); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 8); + c.advance(9); + return castBuffer.getDouble(0); + default: + throw new MessageTypeException(); + } + } + + final Object unpackNull(UnpackCursor c, int offset) throws IOException, MessageTypeException { + more(offset, 1); + int b = buffer[offset] & 0xff; + if(b != 0xc0) { // nil + throw new MessageTypeException(); + } + c.advance(1); + return null; + } + + final boolean unpackBoolean(UnpackCursor c, int offset) throws IOException, MessageTypeException { + more(offset, 1); + int b = buffer[offset] & 0xff; + if(b == 0xc2) { // false + c.advance(1); + return false; + } else if(b == 0xc3) { // true + c.advance(1); + return true; + } else { + throw new MessageTypeException(); + } + } + + final int unpackArray(UnpackCursor c, int offset) throws IOException, MessageTypeException { + more(offset, 1); + int b = buffer[offset]; + if((b & 0xf0) == 0x90) { // FixArray + c.advance(1); + return (int)(b & 0x0f); + } + switch(b & 0xff) { + case 0xdc: // array 16 + more(offset, 3); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 2); + c.advance(3); + return (int)castBuffer.getShort(0) & 0xffff; + case 0xdd: // array 32 + more(offset, 5); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 4); + c.advance(5); + // FIXME overflow check + return castBuffer.getInt(0) & 0x7fffffff; + default: + throw new MessageTypeException(); + } + } + + final int unpackMap(UnpackCursor c, int offset) throws IOException, MessageTypeException { + more(offset, 1); + int b = buffer[offset]; + if((b & 0xf0) == 0x80) { // FixMap + c.advance(1); + return (int)(b & 0x0f); + } + switch(b & 0xff) { + case 0xde: // map 16 + more(offset, 3); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 2); + c.advance(3); + return (int)castBuffer.getShort(0) & 0xffff; + case 0xdf: // map 32 + more(offset, 5); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 4); + c.advance(5); + // FIXME overflow check + return castBuffer.getInt(0) & 0x7fffffff; + default: + throw new MessageTypeException(); + } + } + + final int unpackRaw(UnpackCursor c, int offset) throws IOException, MessageTypeException { + more(offset, 1); + int b = buffer[offset]; + if((b & 0xe0) == 0xa0) { // FixRaw + c.advance(1); + return (int)(b & 0x0f); + } + switch(b & 0xff) { + case 0xda: // raw 16 + more(offset, 3); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 2); + c.advance(3); + return (int)castBuffer.getShort(0) & 0xffff; + case 0xdb: // raw 32 + more(offset, 5); + castBuffer.rewind(); + castBuffer.put(buffer, offset+1, 4); + c.advance(5); + // FIXME overflow check + return castBuffer.getInt(0) & 0x7fffffff; + default: + throw new MessageTypeException(); + } + } + + final byte[] unpackRawBody(UnpackCursor c, int offset, int length) throws IOException, MessageTypeException { + more(offset, length); + byte[] bytes = new byte[length]; + System.arraycopy(buffer, offset, bytes, 0, length); + c.advance(length); + return bytes; + } + + final String unpackString(UnpackCursor c, int offset) throws IOException, MessageTypeException { + int length = unpackRaw(c, offset); + offset = c.getOffset(); + more(offset, length); + String s; + try { + s = new String(buffer, offset, length, "UTF-8"); + } catch (Exception e) { + throw new MessageTypeException(); + } + c.advance(length); + return s; + } +} + diff --git a/java/src/main/java/org/msgpack/MessageMergeable.java b/java/src/main/java/org/msgpack/MessageConvertable.java similarity index 86% rename from java/src/main/java/org/msgpack/MessageMergeable.java rename to java/src/main/java/org/msgpack/MessageConvertable.java index e5a5b451..da251dc1 100644 --- a/java/src/main/java/org/msgpack/MessageMergeable.java +++ b/java/src/main/java/org/msgpack/MessageConvertable.java @@ -17,7 +17,7 @@ // package org.msgpack; -public interface MessageMergeable { - public void messageMerge(Object obj) throws MessageTypeException; +public interface MessageConvertable { + public void messageConvert(Object obj) throws MessageTypeException; } diff --git a/java/src/main/java/org/msgpack/MessageTypeException.java b/java/src/main/java/org/msgpack/MessageTypeException.java index feb6c088..0fa37b76 100644 --- a/java/src/main/java/org/msgpack/MessageTypeException.java +++ b/java/src/main/java/org/msgpack/MessageTypeException.java @@ -17,9 +17,7 @@ // package org.msgpack; -import java.io.IOException; - -public class MessageTypeException extends IOException { +public class MessageTypeException extends RuntimeException { public MessageTypeException() { } public MessageTypeException(String s) { diff --git a/java/src/main/java/org/msgpack/MessageUnpackable.java b/java/src/main/java/org/msgpack/MessageUnpackable.java new file mode 100644 index 00000000..20e4d569 --- /dev/null +++ b/java/src/main/java/org/msgpack/MessageUnpackable.java @@ -0,0 +1,25 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 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; + +import java.io.IOException; + +public interface MessageUnpackable { + public void messageUnpack(Unpacker pk) throws IOException, MessageTypeException; +} + diff --git a/java/src/main/java/org/msgpack/Packer.java b/java/src/main/java/org/msgpack/Packer.java index 935728d2..6d79414c 100644 --- a/java/src/main/java/org/msgpack/Packer.java +++ b/java/src/main/java/org/msgpack/Packer.java @@ -401,7 +401,7 @@ public class Packer { } else if(o instanceof Double) { return packDouble((Double)o); } else { - throw new IOException("unknown object "+o+" ("+o.getClass()+")"); + throw new MessageTypeException("unknown object "+o+" ("+o.getClass()+")"); } } } diff --git a/java/src/main/java/org/msgpack/UnbufferedUnpacker.java b/java/src/main/java/org/msgpack/UnbufferedUnpacker.java deleted file mode 100644 index b4279730..00000000 --- a/java/src/main/java/org/msgpack/UnbufferedUnpacker.java +++ /dev/null @@ -1,82 +0,0 @@ -// -// MessagePack for Java -// -// Copyright (C) 2009-2010 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; - -import java.lang.Iterable; -import java.io.InputStream; -import java.io.IOException; -import java.util.Iterator; -import org.msgpack.impl.UnpackerImpl; - -public class UnbufferedUnpacker extends UnpackerImpl { - private int offset; - private boolean finished; - private Object data; - - public UnbufferedUnpacker() { - super(); - this.offset = 0; - this.finished = false; - } - - public UnbufferedUnpacker useSchema(Schema s) { - super.setSchema(s); - return this; - } - - public Object getData() { - return data; - } - - public boolean isFinished() { - return finished; - } - - public void reset() { - super.reset(); - this.offset = 0; - } - - int getOffset() { - return offset; - } - - void setOffset(int offset) { - this.offset = offset; - } - - public int execute(byte[] buffer) throws UnpackException { - return execute(buffer, 0, buffer.length); - } - - // FIXME - public int execute(byte[] buffer, int offset, int length) throws UnpackException - { - int noffset = super.execute(buffer, offset + this.offset, length); - this.offset = noffset - offset; - if(super.isFinished()) { - this.data = super.getData(); - this.finished = true; - super.reset(); - } else { - this.finished = false; - } - return noffset; - } -} - diff --git a/java/src/main/java/org/msgpack/UnpackCursor.java b/java/src/main/java/org/msgpack/UnpackCursor.java new file mode 100644 index 00000000..33f42588 --- /dev/null +++ b/java/src/main/java/org/msgpack/UnpackCursor.java @@ -0,0 +1,96 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 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; + +import java.io.IOException; + +public class UnpackCursor { + private Unpacker pac; + private int offset; + + UnpackCursor(Unpacker pac, int offset) + { + this.pac = pac; + this.offset = offset; + } + + final void advance(int length) { + offset += length; + } + + final int getOffset() { + return offset; + } + + public byte unpackByte() throws IOException, MessageTypeException { + return pac.impl.unpackByte(this, offset); + } + + public short unpackShort() throws IOException, MessageTypeException { + return pac.impl.unpackShort(this, offset); + } + + public int unpackInt() throws IOException, MessageTypeException { + return pac.impl.unpackInt(this, offset); + } + + public long unpackLong() throws IOException, MessageTypeException { + return pac.impl.unpackLong(this, offset); + } + + public float unpackFloat() throws IOException, MessageTypeException { + return pac.impl.unpackFloat(this, offset); + } + + public double unpackDouble() throws IOException, MessageTypeException { + return pac.impl.unpackDouble(this, offset); + } + + public Object unpackNull() throws IOException, MessageTypeException { + return pac.impl.unpackNull(this, offset); + } + + public boolean unpackBoolean() throws IOException, MessageTypeException { + return pac.impl.unpackBoolean(this, offset); + } + + public int unpackArray() throws IOException, MessageTypeException { + return pac.impl.unpackArray(this, offset); + } + + public int unpackMap() throws IOException, MessageTypeException { + return pac.impl.unpackMap(this, offset); + } + + public int unpackRaw() throws IOException, MessageTypeException { + return pac.impl.unpackRaw(this, offset); + } + + public byte[] unpackRawBody(int length) throws IOException, MessageTypeException { + return pac.impl.unpackRawBody(this, offset, length); + } + + public String unpackString() throws IOException, MessageTypeException { + return pac.impl.unpackString(this, offset); + } + + public void commit() { + pac.setOffset(offset); + } +} + diff --git a/java/src/main/java/org/msgpack/UnpackIterator.java b/java/src/main/java/org/msgpack/UnpackIterator.java index 0a78e83a..5cc994db 100644 --- a/java/src/main/java/org/msgpack/UnpackIterator.java +++ b/java/src/main/java/org/msgpack/UnpackIterator.java @@ -21,41 +21,27 @@ import java.io.IOException; import java.util.Iterator; import java.util.NoSuchElementException; -public class UnpackIterator implements Iterator { +public class UnpackIterator extends UnpackResult implements Iterator { private Unpacker pac; - private boolean have; - private Object data; UnpackIterator(Unpacker pac) { + super(); this.pac = pac; - this.have = false; } public boolean hasNext() { - if(have) { return true; } try { - while(true) { - if(pac.execute()) { - data = pac.getData(); - pac.reset(); - have = true; - return true; - } - - if(!pac.fill()) { - return false; - } - } + return pac.next(this); } catch (IOException e) { return false; } } public Object next() { - if(!have && !hasNext()) { + if(!finished && !hasNext()) { throw new NoSuchElementException(); } - have = false; + finished = false; return data; } diff --git a/java/src/main/java/org/msgpack/UnpackResult.java b/java/src/main/java/org/msgpack/UnpackResult.java new file mode 100644 index 00000000..cec18a16 --- /dev/null +++ b/java/src/main/java/org/msgpack/UnpackResult.java @@ -0,0 +1,42 @@ +// +// MessagePack for Java +// +// Copyright (C) 2009-2010 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; + +public class UnpackResult { + protected boolean finished = false; + protected Object data = null; + + public boolean isFinished() { + return finished; + } + + public Object getData() { + return data; + } + + public void reset() { + finished = false; + data = null; + } + + void done(Object obj) { + finished = true; + data = obj; + } +} + diff --git a/java/src/main/java/org/msgpack/Unpacker.java b/java/src/main/java/org/msgpack/Unpacker.java index c8a88234..20a8c4ac 100644 --- a/java/src/main/java/org/msgpack/Unpacker.java +++ b/java/src/main/java/org/msgpack/Unpacker.java @@ -22,18 +22,43 @@ import java.io.InputStream; import java.io.IOException; import java.util.Iterator; import java.nio.ByteBuffer; -import org.msgpack.impl.UnpackerImpl; -public class Unpacker extends UnpackerImpl implements Iterable { +public class Unpacker implements Iterable { - public static final int DEFAULT_BUFFER_SIZE = 32*1024; + // buffer: + // +---------------------------------------------+ + // | [object] | [obje| unparsed ... | unused ...| + // +---------------------------------------------+ + // ^ parsed + // ^ offset + // ^ filled + // ^ buffer.length + + private static final int DEFAULT_BUFFER_SIZE = 32*1024; + + protected int offset; + protected int parsed; + protected int bufferReserveSize; + protected InputStream stream; + + class BufferedUnpackerMixin extends BufferedUnpackerImpl { + boolean fill() throws IOException { + if(stream == null) { + return false; + } + reserveBuffer(bufferReserveSize); + int rl = stream.read(buffer, filled, buffer.length - filled); + // equals: stream.read(getBuffer(), getBufferOffset(), getBufferCapacity()); + if(rl <= 0) { + return false; + } + bufferConsumed(rl); + return true; + } + }; + + final BufferedUnpackerMixin impl = new BufferedUnpackerMixin(); - private int used; - private int offset; - private int parsed; - private byte[] buffer; - private int bufferReserveSize; - private InputStream stream; public Unpacker() { this(DEFAULT_BUFFER_SIZE); @@ -48,67 +73,31 @@ public class Unpacker extends UnpackerImpl implements Iterable { } public Unpacker(InputStream stream, int bufferReserveSize) { - super(); - this.used = 0; this.offset = 0; this.parsed = 0; - this.buffer = new byte[bufferReserveSize]; this.bufferReserveSize = bufferReserveSize/2; this.stream = stream; } public Unpacker useSchema(Schema s) { - super.setSchema(s); + impl.setSchema(s); return this; } - public void reserveBuffer(int size) { - if(buffer.length - used >= size) { - return; - } - /* - if(used == parsed && buffer.length >= size) { - // rewind buffer - used = 0; - offset = 0; - return; - } - */ - int nextSize = buffer.length * 2; - while(nextSize < size + used) { - nextSize *= 2; - } - - byte[] tmp = new byte[nextSize]; - System.arraycopy(buffer, offset, tmp, 0, used - offset); - - buffer = tmp; - used -= offset; - offset = 0; + public InputStream getStream() { + return this.stream; } - public byte[] getBuffer() { - return buffer; - } - - public int getBufferOffset() { - return used; - } - - public int getBufferCapacity() { - return buffer.length - used; - } - - public void bufferConsumed(int size) { - used += size; + public void setStream(InputStream stream) { + this.stream = stream; } public void feed(ByteBuffer buffer) { int length = buffer.remaining(); if (length == 0) return; reserveBuffer(length); - buffer.get(this.buffer, this.offset, length); + buffer.get(impl.buffer, this.offset, length); bufferConsumed(length); } @@ -118,48 +107,116 @@ public class Unpacker extends UnpackerImpl implements Iterable { public void feed(byte[] buffer, int offset, int length) { reserveBuffer(length); - System.arraycopy(buffer, offset, this.buffer, this.offset, length); + System.arraycopy(buffer, offset, impl.buffer, this.offset, length); bufferConsumed(length); } public boolean fill() throws IOException { - if(stream == null) { - return false; - } - reserveBuffer(bufferReserveSize); - int rl = stream.read(getBuffer(), getBufferOffset(), getBufferCapacity()); - if(rl <= 0) { - return false; - } - bufferConsumed(rl); - return true; + return impl.fill(); } public Iterator iterator() { return new UnpackIterator(this); } + public UnpackResult next() throws IOException, UnpackException { + UnpackResult result = new UnpackResult(); + this.offset = impl.next(this.offset, result); + return result; + } + + public boolean next(UnpackResult result) throws IOException, UnpackException { + this.offset = impl.next(this.offset, result); + return result.isFinished(); + } + + + public void reserveBuffer(int require) { + if(impl.buffer == null) { + int nextSize = (bufferReserveSize < require) ? require : bufferReserveSize; + impl.buffer = new byte[nextSize]; + return; + } + + if(impl.buffer.length - impl.filled >= require) { + return; + } + + int nextSize = impl.buffer.length * 2; + int notParsed = impl.filled - this.offset; + while(nextSize < require + notParsed) { + nextSize *= 2; + } + + byte[] tmp = new byte[nextSize]; + System.arraycopy(impl.buffer, this.offset, tmp, 0, impl.filled - this.offset); + + impl.buffer = tmp; + impl.filled = notParsed; + this.offset = 0; + } + + public byte[] getBuffer() { + return impl.buffer; + } + + public int getBufferOffset() { + return impl.filled; + } + + public int getBufferCapacity() { + return impl.buffer.length - impl.filled; + } + + public void bufferConsumed(int size) { + impl.filled += size; + } + public boolean execute() throws UnpackException { - int noffset = super.execute(buffer, offset, used); + int noffset = impl.execute(impl.buffer, offset, impl.filled); if(noffset <= offset) { return false; } parsed += noffset - offset; offset = noffset; - return super.isFinished(); + return impl.isFinished(); + } + + + public int execute(byte[] buffer) throws UnpackException { + return execute(buffer, 0, buffer.length); + } + + public int execute(byte[] buffer, int offset, int length) throws UnpackException { + int noffset = impl.execute(buffer, offset + this.offset, length); + this.offset = noffset - offset; + if(impl.isFinished()) { + impl.resetState(); + } + return noffset; + } + + public boolean isFinished() { + return impl.isFinished(); } public Object getData() { - return super.getData(); + return impl.getData(); } public void reset() { - super.reset(); - parsed = 0; + impl.reset(); } + + public UnpackCursor begin() + { + return new UnpackCursor(this, offset); + } + + public int getMessageSize() { - return parsed - offset + used; + return parsed - offset + impl.filled; } public int getParsedSize() { @@ -167,7 +224,7 @@ public class Unpacker extends UnpackerImpl implements Iterable { } public int getNonParsedSize() { - return used - offset; + return impl.filled - offset; } public void skipNonparsedBuffer(int size) { @@ -175,80 +232,14 @@ public class Unpacker extends UnpackerImpl implements Iterable { } public void removeNonparsedBuffer() { - used = offset; + impl.filled = offset; } - /* - public static class Context { - private boolean finished; - private Object data; - private int offset; - private UnpackerImpl impl; - public Context() - { - this.finished = false; - this.impl = new UnpackerImpl(); - } - - public boolean isFinished() - { - return finished; - } - - public Object getData() - { - return data; - } - - int getOffset() - { - return offset; - } - - void setFinished(boolean finished) - { - this.finished = finished; - } - - void setData(Object data) - { - this.data = data; - } - - void setOffset(int offset) - { - this.offset = offset; - } - - UnpackerImpl getImpl() - { - return impl; - } - } - - public static int unpack(Context ctx, byte[] buffer) throws UnpackException + void setOffset(int offset) { - return unpack(ctx, buffer, 0, buffer.length); + parsed += offset - this.offset; + this.offset = offset; } - - public static int unpack(Context ctx, byte[] buffer, int offset, int length) throws UnpackException - { - UnpackerImpl impl = ctx.getImpl(); - int noffset = impl.execute(buffer, offset + ctx.getOffset(), length); - ctx.setOffset(noffset - offset); - if(impl.isFinished()) { - ctx.setData(impl.getData()); - ctx.setFinished(false); - impl.reset(); - } else { - ctx.setData(null); - ctx.setFinished(true); - } - int parsed = noffset - offset; - ctx.setOffset(parsed); - return noffset; - } - */ } diff --git a/java/src/main/java/org/msgpack/impl/UnpackerImpl.java b/java/src/main/java/org/msgpack/UnpackerImpl.java similarity index 97% rename from java/src/main/java/org/msgpack/impl/UnpackerImpl.java rename to java/src/main/java/org/msgpack/UnpackerImpl.java index adc62b09..ae012898 100644 --- a/java/src/main/java/org/msgpack/impl/UnpackerImpl.java +++ b/java/src/main/java/org/msgpack/UnpackerImpl.java @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -package org.msgpack.impl; +package org.msgpack; import java.nio.ByteBuffer; //import java.math.BigInteger; @@ -47,7 +47,7 @@ public class UnpackerImpl { static final int CT_MAP_KEY = 0x01; static final int CT_MAP_VALUE = 0x02; - static final int MAX_STACK_SIZE = 16; + static final int MAX_STACK_SIZE = 32; private int cs; private int trail; @@ -67,41 +67,45 @@ public class UnpackerImpl { private static final Schema GENERIC_SCHEMA = new GenericSchema(); private Schema rootSchema; - protected UnpackerImpl() + public UnpackerImpl() { setSchema(GENERIC_SCHEMA); } - protected void setSchema(Schema schema) + public void setSchema(Schema schema) { this.rootSchema = schema; reset(); } - protected Object getData() + public final Object getData() { return data; } - protected boolean isFinished() + public final boolean isFinished() { return finished; } - protected void reset() - { + public final void resetState() { cs = CS_HEADER; top = -1; - finished = false; - data = null; top_ct = 0; top_count = 0; top_obj = null; top_schema = rootSchema; } + public final void reset() + { + resetState(); + finished = false; + data = null; + } + @SuppressWarnings("unchecked") - protected int execute(byte[] src, int off, int length) throws UnpackException + public final int execute(byte[] src, int off, int length) throws UnpackException { if(off >= length) { return off; } diff --git a/java/src/test/java/org/msgpack/TestDirectConversion.java b/java/src/test/java/org/msgpack/TestDirectConversion.java new file mode 100644 index 00000000..d77fe13d --- /dev/null +++ b/java/src/test/java/org/msgpack/TestDirectConversion.java @@ -0,0 +1,154 @@ +package org.msgpack; + +import org.msgpack.*; +import java.io.*; +import java.util.*; + +import org.junit.Test; +import static org.junit.Assert.*; + +public class TestDirectConversion { + private UnpackCursor prepareCursor(ByteArrayOutputStream out) { + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker upk = new Unpacker(in); + return upk.begin(); + } + + @Test + public void testInt() throws Exception { + testInt(0); + testInt(-1); + testInt(1); + testInt(Integer.MIN_VALUE); + testInt(Integer.MAX_VALUE); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testInt(rand.nextInt()); + } + public void testInt(int val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + UnpackCursor c = prepareCursor(out); + assertEquals(val, c.unpackInt()); + c.commit(); + } + + @Test + public void testFloat() throws Exception { + testFloat((float)0.0); + testFloat((float)-0.0); + testFloat((float)1.0); + testFloat((float)-1.0); + testFloat((float)Float.MAX_VALUE); + testFloat((float)Float.MIN_VALUE); + testFloat((float)Float.NaN); + testFloat((float)Float.NEGATIVE_INFINITY); + testFloat((float)Float.POSITIVE_INFINITY); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testFloat(rand.nextFloat()); + } + public void testFloat(float val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + UnpackCursor c = prepareCursor(out); + float f = c.unpackFloat(); + if(Float.isNaN(val)) { + assertTrue(Float.isNaN(f)); + } else { + assertEquals(val, f, 10e-10); + } + c.commit(); + } + + @Test + public void testDouble() throws Exception { + testDouble((double)0.0); + testDouble((double)-0.0); + testDouble((double)1.0); + testDouble((double)-1.0); + testDouble((double)Double.MAX_VALUE); + testDouble((double)Double.MIN_VALUE); + testDouble((double)Double.NaN); + testDouble((double)Double.NEGATIVE_INFINITY); + testDouble((double)Double.POSITIVE_INFINITY); + Random rand = new Random(); + for (int i = 0; i < 1000; i++) + testDouble(rand.nextDouble()); + } + public void testDouble(double val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + UnpackCursor c = prepareCursor(out); + double f = c.unpackDouble(); + if(Double.isNaN(val)) { + assertTrue(Double.isNaN(f)); + } else { + assertEquals(val, f, 10e-10); + } + c.commit(); + } + + @Test + public void testNil() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).packNil(); + UnpackCursor c = prepareCursor(out); + assertEquals(null, c.unpackNull()); + c.commit(); + } + + @Test + public void testBoolean() throws Exception { + testBoolean(false); + testBoolean(true); + } + public void testBoolean(boolean val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + UnpackCursor c = prepareCursor(out); + assertEquals(val, c.unpackBoolean()); + c.commit(); + } + + @Test + public void testString() throws Exception { + testString(""); + testString("a"); + testString("ab"); + testString("abc"); + // small size string + for (int i = 0; i < 100; i++) { + StringBuilder sb = new StringBuilder(); + int len = (int)Math.random() % 31 + 1; + for (int j = 0; j < len; j++) + sb.append('a' + ((int)Math.random()) & 26); + testString(sb.toString()); + } + // medium size string + for (int i = 0; i < 100; i++) { + StringBuilder sb = new StringBuilder(); + int len = (int)Math.random() % 100 + (1 << 15); + for (int j = 0; j < len; j++) + sb.append('a' + ((int)Math.random()) & 26); + testString(sb.toString()); + } + // large size string + for (int i = 0; i < 10; i++) { + StringBuilder sb = new StringBuilder(); + int len = (int)Math.random() % 100 + (1 << 31); + for (int j = 0; j < len; j++) + sb.append('a' + ((int)Math.random()) & 26); + testString(sb.toString()); + } + } + public void testString(String val) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(val); + UnpackCursor c = prepareCursor(out); + assertEquals(val, c.unpackString()); + c.commit(); + } + + // FIXME container types +}; diff --git a/java/src/test/java/org/msgpack/TestPackUnpack.java b/java/src/test/java/org/msgpack/TestPackUnpack.java index 68778539..a16b5b11 100644 --- a/java/src/test/java/org/msgpack/TestPackUnpack.java +++ b/java/src/test/java/org/msgpack/TestPackUnpack.java @@ -237,5 +237,5 @@ public class TestPackUnpack { System.out.println("Got unexpected class: " + obj.getClass()); assertTrue(false); } - } + } };