diff --git a/java/src/main/java/org/msgpack/CustomMessage.java b/java/src/main/java/org/msgpack/CustomMessage.java index 832aa599..40df8ef0 100644 --- a/java/src/main/java/org/msgpack/CustomMessage.java +++ b/java/src/main/java/org/msgpack/CustomMessage.java @@ -18,6 +18,7 @@ package org.msgpack; import java.lang.annotation.Annotation; +import org.msgpack.template.TemplateRegistry; public class CustomMessage { public static void registerPacker(Class target, MessagePacker packer) { @@ -33,6 +34,7 @@ public class CustomMessage { } public static void register(Class target, Template tmpl) { + TemplateRegistry.register(target, tmpl); CustomPacker.register(target, tmpl); CustomConverter.register(target, tmpl); CustomUnpacker.register(target, tmpl); diff --git a/java/src/main/java/org/msgpack/MessagePack.java b/java/src/main/java/org/msgpack/MessagePack.java index b7b176bc..0c8d6dad 100644 --- a/java/src/main/java/org/msgpack/MessagePack.java +++ b/java/src/main/java/org/msgpack/MessagePack.java @@ -27,6 +27,8 @@ import java.lang.reflect.Method; import org.msgpack.util.codegen.DynamicTemplate; import org.msgpack.util.codegen.DynamicOrdinalEnumTemplate; import org.msgpack.util.codegen.FieldList; +import org.msgpack.template.TemplateRegistry; +import org.msgpack.template.TemplateBuilder; public class MessagePack { public static byte[] pack(Object obj) { @@ -150,6 +152,8 @@ public class MessagePack { } public static void register(Class target) { // auto-detect + TemplateRegistry.register(target); + Template tmpl; if(target.isEnum()) { tmpl = DynamicOrdinalEnumTemplate.create(target); @@ -194,6 +198,7 @@ public class MessagePack { } public static void register(Class target, FieldList opts) { + TemplateRegistry.register(target); // FIXME FieldList Template tmpl = DynamicTemplate.create(target, opts); CustomPacker.register(target, tmpl); CustomConverter.register(target, tmpl); @@ -201,6 +206,7 @@ public class MessagePack { } public static void register(Class target, Template tmpl) { + TemplateRegistry.register(target, tmpl); CustomPacker.register(target, tmpl); CustomConverter.register(target, tmpl); CustomUnpacker.register(target, tmpl); diff --git a/java/src/main/java/org/msgpack/MessagePackObject.java b/java/src/main/java/org/msgpack/MessagePackObject.java index 07c5d300..59fab62e 100644 --- a/java/src/main/java/org/msgpack/MessagePackObject.java +++ b/java/src/main/java/org/msgpack/MessagePackObject.java @@ -21,14 +21,9 @@ import java.util.List; import java.util.Set; import java.util.Map; import java.math.BigInteger; -import org.msgpack.template.ClassTemplate; -import org.msgpack.template.NullableTemplate; +import org.msgpack.template.TemplateRegistry; public abstract class MessagePackObject implements Cloneable, MessagePackable { - static { - Templates.load(); - } - public boolean isNil() { return false; } @@ -155,9 +150,11 @@ public abstract class MessagePackObject implements Cloneable, MessagePackable { return convert((Class)to.getClass(), to); } - private T convert(Class klass, T to) throws MessageTypeException { - // FIXME nullable? - return (T)convert(new NullableTemplate(new ClassTemplate(klass)), to); + public T convert(Class klass, T to) throws MessageTypeException { + if(isNil()) { + return null; + } + return (T)convert(TemplateRegistry.lookup(klass), to); } } diff --git a/java/src/main/java/org/msgpack/MessageTypeException.java b/java/src/main/java/org/msgpack/MessageTypeException.java index 7a06a3e3..bd148447 100644 --- a/java/src/main/java/org/msgpack/MessageTypeException.java +++ b/java/src/main/java/org/msgpack/MessageTypeException.java @@ -27,5 +27,9 @@ public class MessageTypeException extends RuntimeException { public MessageTypeException(String s, Throwable t) { super(s, t); } + + public MessageTypeException(Throwable t) { + super(t); + } } diff --git a/java/src/main/java/org/msgpack/Packer.java b/java/src/main/java/org/msgpack/Packer.java index 8349a308..cd6bc75a 100644 --- a/java/src/main/java/org/msgpack/Packer.java +++ b/java/src/main/java/org/msgpack/Packer.java @@ -25,11 +25,7 @@ import java.util.Set; import java.util.Map; import java.util.Collection; import java.math.BigInteger; - -import org.msgpack.annotation.MessagePackDelegate; -import org.msgpack.annotation.MessagePackMessage; -import org.msgpack.annotation.MessagePackOrdinalEnum; -import org.msgpack.util.codegen.DynamicTemplate; +import org.msgpack.template.TemplateRegistry; /** * Packer enables you to serialize objects into OutputStream. @@ -48,14 +44,10 @@ import org.msgpack.util.codegen.DynamicTemplate; * You can serialize objects that implements {@link MessagePackable} interface. */ public class Packer { - static { - Templates.load(); - } - public static void load() { } protected byte[] castBytes = new byte[9]; - protected ByteBuffer castBuffer = ByteBuffer.wrap(castBytes); + //protected ByteBuffer castBuffer = ByteBuffer.wrap(castBytes); protected OutputStream out; public Packer(OutputStream out) { @@ -78,7 +70,9 @@ public class Packer { if(d < -(1<<7)) { // signed 16 castBytes[0] = (byte)0xd1; - castBuffer.putShort(1, d); +// castBuffer.putShort(1, d); + castBytes[1] = (byte)(d >> 8); + castBytes[2] = (byte)(d >> 0); out.write(castBytes, 0, 3); } else { // signed 8 @@ -98,7 +92,9 @@ public class Packer { } else { // unsigned 16 castBytes[0] = (byte)0xcd; - castBuffer.putShort(1, d); +// castBuffer.putShort(1, d); + castBytes[1] = (byte)(d >> 8); + castBytes[2] = (byte)(d >> 0); out.write(castBytes, 0, 3); } } @@ -110,12 +106,18 @@ public class Packer { if(d < -(1<<15)) { // signed 32 castBytes[0] = (byte)0xd2; - castBuffer.putInt(1, d); +// castBuffer.putInt(1, d); + castBytes[1] = (byte)(d >> 24); + castBytes[2] = (byte)(d >> 16); + castBytes[3] = (byte)(d >> 8); + castBytes[4] = (byte)(d >> 0); out.write(castBytes, 0, 5); } else if(d < -(1<<7)) { // signed 16 castBytes[0] = (byte)0xd1; - castBuffer.putShort(1, (short)d); +// castBuffer.putShort(1, (short)d); + castBytes[1] = (byte)(d >> 8); + castBytes[2] = (byte)(d >> 0); out.write(castBytes, 0, 3); } else { // signed 8 @@ -135,12 +137,18 @@ public class Packer { } else if(d < (1<<16)) { // unsigned 16 castBytes[0] = (byte)0xcd; - castBuffer.putShort(1, (short)d); +// castBuffer.putShort(1, (short)d); + castBytes[1] = (byte)(d >> 8); + castBytes[2] = (byte)(d >> 0); out.write(castBytes, 0, 3); } else { // unsigned 32 castBytes[0] = (byte)0xce; - castBuffer.putInt(1, d); +// castBuffer.putInt(1, d); + castBytes[1] = (byte)(d >> 24); + castBytes[2] = (byte)(d >> 16); + castBytes[3] = (byte)(d >> 8); + castBytes[4] = (byte)(d >> 0); out.write(castBytes, 0, 5); } } @@ -153,19 +161,33 @@ public class Packer { if(d < -(1L<<31)) { // signed 64 castBytes[0] = (byte)0xd3; - castBuffer.putLong(1, d); +// castBuffer.putLong(1, d); + castBytes[1] = (byte)(d >> 56); + castBytes[2] = (byte)(d >> 48); + castBytes[3] = (byte)(d >> 40); + castBytes[4] = (byte)(d >> 32); + castBytes[5] = (byte)(d >> 24); + castBytes[6] = (byte)(d >> 16); + castBytes[7] = (byte)(d >> 8); + castBytes[8] = (byte)(d >> 0); out.write(castBytes, 0, 9); } else { // signed 32 castBytes[0] = (byte)0xd2; - castBuffer.putInt(1, (int)d); +// castBuffer.putInt(1, (int)d); + castBytes[1] = (byte)(d >> 24); + castBytes[2] = (byte)(d >> 16); + castBytes[3] = (byte)(d >> 8); + castBytes[4] = (byte)(d >> 0); out.write(castBytes, 0, 5); } } else { if(d < -(1<<7)) { // signed 16 castBytes[0] = (byte)0xd1; - castBuffer.putShort(1, (short)d); +// castBuffer.putShort(1, (short)d); + castBytes[1] = (byte)(d >> 8); + castBytes[2] = (byte)(d >> 0); out.write(castBytes, 0, 3); } else { // signed 8 @@ -187,7 +209,9 @@ public class Packer { } else { // unsigned 16 castBytes[0] = (byte)0xcd; - castBuffer.putShort(1, (short)d); +// castBuffer.putShort(1, (short)d); + castBytes[1] = (byte)((d & 0x0000ff00) >> 8); + castBytes[2] = (byte)((d & 0x000000ff) >> 0); out.write(castBytes, 0, 3); //System.out.println("pack uint 16 "+(short)d); } @@ -195,12 +219,24 @@ public class Packer { if(d < (1L<<32)) { // unsigned 32 castBytes[0] = (byte)0xce; - castBuffer.putInt(1, (int)d); +// castBuffer.putInt(1, (int)d); + castBytes[1] = (byte)((d & 0xff000000) >> 24); + castBytes[2] = (byte)((d & 0x00ff0000) >> 16); + castBytes[3] = (byte)((d & 0x0000ff00) >> 8); + castBytes[4] = (byte)((d & 0x000000ff) >> 0); out.write(castBytes, 0, 5); } else { // unsigned 64 castBytes[0] = (byte)0xcf; - castBuffer.putLong(1, d); +// castBuffer.putLong(1, d); + castBytes[1] = (byte)(d >> 56); + castBytes[2] = (byte)(d >> 48); + castBytes[3] = (byte)(d >> 40); + castBytes[4] = (byte)(d >> 32); + castBytes[5] = (byte)(d >> 24); + castBytes[6] = (byte)(d >> 16); + castBytes[7] = (byte)(d >> 8); + castBytes[8] = (byte)(d >> 0); out.write(castBytes, 0, 9); } } @@ -222,7 +258,7 @@ public class Packer { castBytes[6] = barray[barray.length-3]; castBytes[7] = barray[barray.length-2]; castBytes[8] = barray[barray.length-1]; - out.write(castBytes); + out.write(castBytes, 0, 9); return this; } else { throw new MessageTypeException("can't pack BigInteger larger than 0xffffffffffffffff"); @@ -231,14 +267,28 @@ public class Packer { public Packer packFloat(float d) throws IOException { castBytes[0] = (byte)0xca; - castBuffer.putFloat(1, d); +// castBuffer.putFloat(1, d); + int f = Float.floatToRawIntBits(d); + castBytes[1] = (byte)(f >> 24); + castBytes[2] = (byte)(f >> 16); + castBytes[3] = (byte)(f >> 8); + castBytes[4] = (byte)(f >> 0); out.write(castBytes, 0, 5); return this; } public Packer packDouble(double d) throws IOException { castBytes[0] = (byte)0xcb; - castBuffer.putDouble(1, d); +// castBuffer.putDouble(1, d); + long f = Double.doubleToRawLongBits(d); + castBytes[1] = (byte)(f >> 56); + castBytes[2] = (byte)(f >> 48); + castBytes[3] = (byte)(f >> 40); + castBytes[4] = (byte)(f >> 32); + castBytes[5] = (byte)(f >> 24); + castBytes[6] = (byte)(f >> 16); + castBytes[7] = (byte)(f >> 8); + castBytes[8] = (byte)(f >> 0); out.write(castBytes, 0, 9); return this; } @@ -268,11 +318,17 @@ public class Packer { out.write((byte)d); } else if(n < 65536) { castBytes[0] = (byte)0xdc; - castBuffer.putShort(1, (short)n); +// castBuffer.putShort(1, (short)n); + castBytes[1] = (byte)(n >> 8); + castBytes[2] = (byte)(n >> 0); out.write(castBytes, 0, 3); } else { castBytes[0] = (byte)0xdd; - castBuffer.putInt(1, n); +// castBuffer.putInt(1, n); + castBytes[1] = (byte)(n >> 24); + castBytes[2] = (byte)(n >> 16); + castBytes[3] = (byte)(n >> 8); + castBytes[4] = (byte)(n >> 0); out.write(castBytes, 0, 5); } return this; @@ -284,11 +340,17 @@ public class Packer { out.write((byte)d); } else if(n < 65536) { castBytes[0] = (byte)0xde; - castBuffer.putShort(1, (short)n); +// castBuffer.putShort(1, (short)n); + castBytes[1] = (byte)(n >> 8); + castBytes[2] = (byte)(n >> 0); out.write(castBytes, 0, 3); } else { castBytes[0] = (byte)0xdf; - castBuffer.putInt(1, n); +// castBuffer.putInt(1, n); + castBytes[1] = (byte)(n >> 24); + castBytes[2] = (byte)(n >> 16); + castBytes[3] = (byte)(n >> 8); + castBytes[4] = (byte)(n >> 0); out.write(castBytes, 0, 5); } return this; @@ -300,11 +362,17 @@ public class Packer { out.write((byte)d); } else if(n < 65536) { castBytes[0] = (byte)0xda; - castBuffer.putShort(1, (short)n); +// castBuffer.putShort(1, (short)n); + castBytes[1] = (byte)(n >> 8); + castBytes[2] = (byte)(n >> 0); out.write(castBytes, 0, 3); } else { castBytes[0] = (byte)0xdb; - castBuffer.putInt(1, n); +// castBuffer.putInt(1, n); + castBytes[1] = (byte)(n >> 24); + castBytes[2] = (byte)(n >> 16); + castBytes[3] = (byte)(n >> 8); + castBytes[4] = (byte)(n >> 0); out.write(castBytes, 0, 5); } return this; @@ -449,7 +517,8 @@ public class Packer { } public Packer pack(Object o) throws IOException { - Templates.TAny.pack(this, o); + if(o == null) { return packNil(); } + TemplateRegistry.lookup(o.getClass()).pack(this, o); return this; } diff --git a/java/src/main/java/org/msgpack/Templates.java b/java/src/main/java/org/msgpack/Templates.java index 2c0457ef..4972670e 100644 --- a/java/src/main/java/org/msgpack/Templates.java +++ b/java/src/main/java/org/msgpack/Templates.java @@ -20,8 +20,6 @@ package org.msgpack; import org.msgpack.template.*; public class Templates { - public static void load() { } - public static Template tNullable(Template elementTemplate) { return new NullableTemplate(elementTemplate); } @@ -46,7 +44,11 @@ public class Templates { } public static Template tClass(Class target) { - return new ClassTemplate(target); + Template tmpl = TemplateRegistry.lookup(target); + if(tmpl == null) { + // FIXME + } + return tmpl; } public static final Template TByte = ByteTemplate.getInstance(); diff --git a/java/src/main/java/org/msgpack/Unpacker.java b/java/src/main/java/org/msgpack/Unpacker.java index 8c50b857..fdc213c5 100644 --- a/java/src/main/java/org/msgpack/Unpacker.java +++ b/java/src/main/java/org/msgpack/Unpacker.java @@ -23,8 +23,7 @@ import java.io.IOException; import java.util.Iterator; import java.nio.ByteBuffer; import java.math.BigInteger; -import org.msgpack.template.ClassTemplate; -import org.msgpack.template.NullableTemplate; +import org.msgpack.template.TemplateRegistry; /** * Unpacker enables you to deserialize objects from stream. @@ -105,10 +104,6 @@ import org.msgpack.template.NullableTemplate; * */ public class Unpacker implements Iterable { - static { - Templates.load(); - } - // buffer: // +---------------------------------------------+ // | [object] | [obje| unparsed ... | unused ...| @@ -578,6 +573,19 @@ public class Unpacker implements Iterable { // return unpackObject(); //} + final public T unpack(T to) throws IOException, MessageTypeException { + return unpack((Class)to.getClass(), to); + } + + final public T unpack(Class klass) throws IOException, MessageTypeException { + return unpack(klass, null); + } + + final public T unpack(Class klass, T to) throws IOException, MessageTypeException { + if(tryUnpackNull()) { return null; } + return (T)TemplateRegistry.lookup(klass).unpack(this, to); + } + final public Object unpack(Template tmpl) throws IOException, MessageTypeException { return unpack(tmpl, null); } @@ -585,18 +593,5 @@ public class Unpacker implements Iterable { final public T unpack(Template tmpl, T to) throws IOException, MessageTypeException { return (T)tmpl.unpack(this, to); } - - final public T unpack(Class klass) throws IOException, MessageTypeException { - return unpack(klass, null); - } - - final public T unpack(T to) throws IOException, MessageTypeException { - return unpack((Class)to.getClass(), to); - } - - final public T unpack(Class klass, T to) throws IOException, MessageTypeException { - // FIXME nullable? - return (T)unpack(new NullableTemplate(new ClassTemplate(klass)), to); - } } diff --git a/java/src/main/java/org/msgpack/annotation/Ignore.java b/java/src/main/java/org/msgpack/annotation/Ignore.java new file mode 100644 index 00000000..dfe9f7ed --- /dev/null +++ b/java/src/main/java/org/msgpack/annotation/Ignore.java @@ -0,0 +1,28 @@ +// +// 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.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Ignore { +} diff --git a/java/src/main/java/org/msgpack/annotation/Index.java b/java/src/main/java/org/msgpack/annotation/Index.java new file mode 100644 index 00000000..b5670120 --- /dev/null +++ b/java/src/main/java/org/msgpack/annotation/Index.java @@ -0,0 +1,29 @@ +// +// 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.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Index { + int value(); +} diff --git a/java/src/main/java/org/msgpack/annotation/MessagePackDelegate.java b/java/src/main/java/org/msgpack/annotation/MessagePackDelegate.java index 88c6f8cf..bb16e2ce 100644 --- a/java/src/main/java/org/msgpack/annotation/MessagePackDelegate.java +++ b/java/src/main/java/org/msgpack/annotation/MessagePackDelegate.java @@ -25,4 +25,5 @@ import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MessagePackDelegate { + String value(); } diff --git a/java/src/main/java/org/msgpack/annotation/MessagePackMessage.java b/java/src/main/java/org/msgpack/annotation/MessagePackMessage.java index 5f781e01..ab3ad0a1 100644 --- a/java/src/main/java/org/msgpack/annotation/MessagePackMessage.java +++ b/java/src/main/java/org/msgpack/annotation/MessagePackMessage.java @@ -21,8 +21,10 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.msgpack.template.FieldOption; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MessagePackMessage { + FieldOption value() default FieldOption.DEFAULT; } diff --git a/java/src/main/java/org/msgpack/annotation/Required.java b/java/src/main/java/org/msgpack/annotation/Required.java new file mode 100644 index 00000000..00d4ecbe --- /dev/null +++ b/java/src/main/java/org/msgpack/annotation/Required.java @@ -0,0 +1,28 @@ +// +// 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.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Required { +} diff --git a/java/src/main/java/org/msgpack/buffer/VectoredByteBuffer.java b/java/src/main/java/org/msgpack/buffer/VectoredByteBuffer.java new file mode 100644 index 00000000..df73bc08 --- /dev/null +++ b/java/src/main/java/org/msgpack/buffer/VectoredByteBuffer.java @@ -0,0 +1,462 @@ +// +// MessagePack for Java +// +// Copyright (C) 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.buffer; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.util.List; +import java.util.ArrayList; +import java.nio.ByteBuffer; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; + +public class VectoredByteBuffer implements GatheringByteChannel, ScatteringByteChannel { + private List vec = new ArrayList(); + private ByteBuffer internalBuffer; + private ByteBuffer lastInternalBuffer; + private int chunkSize; + private int referenceThreshold; + + public VectoredByteBuffer() { + this(32*1024); + } + + public VectoredByteBuffer(int chunkSize) { + this(chunkSize, 32); + } + + public VectoredByteBuffer(int chunkSize, int referenceThreshold) { + this.chunkSize = chunkSize; + this.referenceThreshold = referenceThreshold; + internalBuffer = ByteBuffer.allocateDirect(chunkSize); + } + + + public void setChunkSize(int chunkSize) { + this.chunkSize = chunkSize; + } + + public int getChunkSize(int chunkSize) { + return this.chunkSize; + } + + public void setReferenceThreshold(int referenceThreshold) { + this.referenceThreshold = referenceThreshold; + } + + public int getReferenceThreshold(int referenceThreshold) { + return this.referenceThreshold; + } + + + @Override + public void close() { + reset(); + } + + @Override + public boolean isOpen() { + return true; // FIXME? + } + + + public synchronized void reset() { + vec.clear(); + lastInternalBuffer = null; + } + + + public void write(byte[] b) { + write(b, 0, b.length); + } + + public void write(byte[] b, int off, int len) { + if(off < 0 || len < 0 || b.length < off+len) { + throw new IndexOutOfBoundsException(); + } + if(referenceThreshold >= 0 && len > referenceThreshold) { + writeReference(b, off, len); + } else { + writeCopy(b, off, len); + } + } + + public void write(int b) { + byte[] ba = new byte[1]; + ba[0] = (byte)b; + write(ba); + } + + @Override + public int write(ByteBuffer src) { + int slen = src.remaining(); + if(referenceThreshold >= 0 && slen > referenceThreshold) { + writeCopy(src); + } else { + writeReference(src); + } + return slen; + } + + @Override + public synchronized long write(ByteBuffer[] srcs) { + return write(srcs, 0, srcs.length); + } + + @Override + public synchronized long write(ByteBuffer[] srcs, int offset, int length) { + if(offset < 0 || length < 0 || srcs.length < offset+length) { + throw new IndexOutOfBoundsException(); + } + long total = 0; + for(int i=offset; offset < length; offset++) { + ByteBuffer src = srcs[i]; + total += write(src); + } + return total; + } + + private synchronized void writeCopy(byte[] b, int off, int len) { + int ipos = internalBuffer.position(); + if(internalBuffer.capacity() - ipos < len) { + // allocate new buffer + int nsize = chunkSize > len ? chunkSize : len; + internalBuffer = ByteBuffer.allocateDirect(nsize); + ipos = 0; + } else if(internalBuffer == lastInternalBuffer) { + // optimization: concatenates to the last buffer instead + // of adding new reference + ByteBuffer dup = vec.get(vec.size()-1); + internalBuffer.put(b, off, len); + dup.limit(ipos + len); + return; + } + internalBuffer.put(b, off, len); + ByteBuffer dup = internalBuffer.duplicate(); + dup.position(ipos); + dup.mark(); + dup.limit(ipos + len); + vec.add(dup); + lastInternalBuffer = internalBuffer; + } + + private synchronized void writeCopy(ByteBuffer src) { + int slen = src.remaining(); + int ipos = internalBuffer.position(); + if(internalBuffer.capacity() - ipos < slen) { + // allocate new buffer + int nsize = chunkSize > slen ? chunkSize : slen; + internalBuffer = ByteBuffer.allocateDirect(nsize); + ipos = 0; + } else if(internalBuffer == lastInternalBuffer) { + // optimization: concatenates to the last buffer instead + // of adding new reference + ByteBuffer dup = vec.get(vec.size()-1); + int dpos = dup.position(); + internalBuffer.put(src); + ByteBuffer dup2 = internalBuffer.duplicate(); + dup2.position(dpos); + dup2.limit(ipos + slen); + vec.set(vec.size()-1, dup2); + return; + } + internalBuffer.put(src); + ByteBuffer dup = internalBuffer.duplicate(); + dup.position(ipos); + dup.mark(); + dup.limit(ipos + slen); + vec.add(dup); + lastInternalBuffer = internalBuffer; + } + + private synchronized void writeReference(byte[] b, int off, int len) { + ByteBuffer buf = ByteBuffer.wrap(b, off, len); + vec.add(buf); + lastInternalBuffer = null; + } + + private synchronized void writeReference(ByteBuffer src) { + ByteBuffer buf = src.duplicate(); + vec.add(buf); + lastInternalBuffer = null; + } + + + public synchronized void writeTo(java.io.OutputStream out) throws IOException { + byte[] tmpbuf = null; + for(int i=0; i < vec.size(); i++) { + ByteBuffer r = vec.get(i); + int rpos = r.position(); + int rlen = r.limit() - rpos; + if(r.hasArray()) { + byte[] array = r.array(); + out.write(array, rpos, rlen); + } else { + if(tmpbuf == null) { + int max = rlen; + for(int j=i+1; j < vec.size(); j++) { + ByteBuffer c = vec.get(j); + int clen = c.remaining(); + if(max < clen) { + max = clen; + } + } + tmpbuf = new byte[max]; + } + r.get(tmpbuf, 0, rlen); + r.position(rpos); + out.write(tmpbuf, 0, rlen); + } + } + } + + public synchronized byte[] toByteArray() { + byte[] out = new byte[available()]; + int off = 0; + for(ByteBuffer r: vec) { + int rpos = r.position(); + int rlen = r.limit() - rpos; + r.get(out, off, rlen); + r.position(rpos); + off += rlen; + } + return out; + } + + + public synchronized int available() { + int total = 0; + for(ByteBuffer r : vec) { + total += r.remaining(); + } + return total; + } + + public synchronized int read(byte[] b) { + return read(b, 0, b.length); + } + + public synchronized int read(byte[] b, int off, int len) { + if(off < 0 || len < 0 || b.length < off+len) { + throw new IndexOutOfBoundsException(); + } + int start = len; + while(!vec.isEmpty()) { + ByteBuffer r = vec.get(0); + int rlen = r.remaining(); + if(rlen <= len) { + r.get(b, off, rlen); + vec.remove(0); + off += rlen; + len -= rlen; + } else { + r.get(b, off, len); + return start; + } + } + return start - len; + } + + public synchronized int read() { + byte[] ba = new byte[1]; + if(read(ba) >= 1) { + return ba[0]; + } else { + return -1; + } + } + + @Override + public synchronized int read(ByteBuffer dst) { + int len = dst.remaining(); + int start = len; + while(!vec.isEmpty()) { + ByteBuffer r = vec.get(0); + int rlen = r.remaining(); + if(rlen <= len) { + dst.put(r); + vec.remove(0); + len -= rlen; + } else { + int blim = r.limit(); + r.limit(len); + try { + dst.put(r); + } finally { + r.limit(blim); + } + return start; + } + } + return start - len; + } + + @Override + public synchronized long read(ByteBuffer[] dsts) { + return read(dsts, 0, dsts.length); + } + + @Override + public synchronized long read(ByteBuffer[] dsts, int offset, int length) { + if(offset < 0 || length < 0 || dsts.length < offset+length) { + throw new IndexOutOfBoundsException(); + } + long total = 0; + for(int i=offset; i < length; i++) { + ByteBuffer dst = dsts[i]; + int dlen = dst.remaining(); + int rlen = read(dsts[i]); + total += rlen; + if(rlen < dlen) { + return total; + } + } + return total; + } + + public synchronized long read(GatheringByteChannel to) throws IOException { + long total = to.write((ByteBuffer[])vec.toArray()); + while(!vec.isEmpty()) { + ByteBuffer r = vec.get(0); + if(r.remaining() == 0) { + vec.remove(0); + } else { + break; + } + } + return total; + } + + public synchronized long skip(long len) { + if(len <= 0) { + return 0; + } + long start = len; + while(!vec.isEmpty()) { + ByteBuffer r = vec.get(0); + int rlen = r.remaining(); + if(rlen <= len) { + r.position(r.position()+rlen); + vec.remove(0); + len -= rlen; + } else { + r.position((int)(r.position()+len)); + return start; + } + } + return start - len; + } + + + public final static class OutputStream extends java.io.OutputStream { + private VectoredByteBuffer vbb; + + OutputStream(VectoredByteBuffer vbb) { + this.vbb = vbb; + } + + @Override + public void write(byte[] b) { + vbb.write(b); + } + + @Override + public void write(byte[] b, int off, int len) { + vbb.write(b, off, len); + } + + @Override + public void write(int b) { + vbb.write(b); + } + + public int write(ByteBuffer src) { + return vbb.write(src); + } + + public long write(ByteBuffer[] srcs) { + return vbb.write(srcs); + } + + public long write(ByteBuffer[] srcs, int offset, int length) { + return vbb.write(srcs, offset, length); + } + + public void writeTo(OutputStream out) throws IOException { + vbb.writeTo(out); + } + + public byte[] toByteArray() { + return vbb.toByteArray(); + } + } + + public final static class InputStream extends java.io.InputStream { + private VectoredByteBuffer vbb; + + InputStream(VectoredByteBuffer vbb) { + this.vbb = vbb; + } + + @Override + public int available() { + return vbb.available(); + } + + @Override + public int read(byte[] b) { + return vbb.read(b); + } + + @Override + public int read(byte[] b, int off, int len) { + return vbb.read(b, off, len); + } + + @Override + public int read() { + return vbb.read(); + } + + public int read(ByteBuffer dst) { + return vbb.read(dst); + } + + public long read(ByteBuffer[] dsts, int offset, int length) { + return vbb.read(dsts, offset, length); + } + + public long read(GatheringByteChannel to) throws IOException { + return vbb.read(to); + } + + public long skip(long len) { + return vbb.skip(len); + } + } + + public OutputStream outputStream() { + return new OutputStream(this); + } + + public InputStream inputStream() { + return new InputStream(this); + } +} + diff --git a/java/src/main/java/org/msgpack/template/AnyTemplate.java b/java/src/main/java/org/msgpack/template/AnyTemplate.java index 69b2669b..36c34432 100644 --- a/java/src/main/java/org/msgpack/template/AnyTemplate.java +++ b/java/src/main/java/org/msgpack/template/AnyTemplate.java @@ -24,10 +24,13 @@ public class AnyTemplate implements Template { private AnyTemplate() { } public void pack(Packer pk, Object target) throws IOException { - if(target == null) { + if(target instanceof MessagePackObject) { + pk.pack((MessagePackObject)target); + } else if(target == null) { pk.packNil(); } else { - new ClassTemplate(target.getClass()).pack(pk, target); + TemplateRegistry.lookup(target.getClass()).pack(pk, target); + //new ClassTemplate(target.getClass()).pack(pk, target); } } @@ -47,6 +50,7 @@ public class AnyTemplate implements Template { static { CustomMessage.register(MessagePackObject.class, instance); + TemplateRegistry.register(MessagePackObject.class, instance); } } diff --git a/java/src/main/java/org/msgpack/template/BigIntegerTemplate.java b/java/src/main/java/org/msgpack/template/BigIntegerTemplate.java index f0a45fcc..c59260bc 100644 --- a/java/src/main/java/org/msgpack/template/BigIntegerTemplate.java +++ b/java/src/main/java/org/msgpack/template/BigIntegerTemplate.java @@ -44,6 +44,7 @@ public class BigIntegerTemplate implements Template { static { CustomMessage.register(BigInteger.class, instance); + TemplateRegistry.register(BigInteger.class, instance); } } diff --git a/java/src/main/java/org/msgpack/template/BooleanArrayTemplate.java b/java/src/main/java/org/msgpack/template/BooleanArrayTemplate.java new file mode 100644 index 00000000..9999cc2b --- /dev/null +++ b/java/src/main/java/org/msgpack/template/BooleanArrayTemplate.java @@ -0,0 +1,76 @@ +// +// 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.template; + +import java.io.IOException; +import org.msgpack.*; + +public class BooleanArrayTemplate implements Template { + private BooleanArrayTemplate() { } + + public void pack(Packer pk, Object target) throws IOException { + if(!(target instanceof boolean[])) { + throw new MessageTypeException(); + } + boolean[] array = (boolean[])target; + pk.packArray(array.length); + for(boolean a : array) { + pk.pack(a); + } + } + + public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException { + int length = pac.unpackArray(); + boolean[] array; + if(to != null && to instanceof boolean[] && ((boolean[])to).length == length) { + array = (boolean[])to; + } else { + array = new boolean[length]; + } + for(int i=0; i < length; i++) { + array[i] = pac.unpackBoolean(); + } + return array; + } + + public Object convert(MessagePackObject from, Object to) throws MessageTypeException { + MessagePackObject[] src = from.asArray(); + boolean[] array; + if(to != null && to instanceof boolean[] && ((boolean[])to).length == src.length) { + array = (boolean[])to; + } else { + array = new boolean[src.length]; + } + for(int i=0; i < src.length; i++) { + MessagePackObject s = src[i]; + array[i] = s.asBoolean(); + } + return array; + } + + static public BooleanArrayTemplate getInstance() { + return instance; + } + + static final BooleanArrayTemplate instance = new BooleanArrayTemplate(); + + static { + TemplateRegistry.register(boolean[].class, instance); + } +} + diff --git a/java/src/main/java/org/msgpack/template/BooleanTemplate.java b/java/src/main/java/org/msgpack/template/BooleanTemplate.java index a3143dbb..e37431c1 100644 --- a/java/src/main/java/org/msgpack/template/BooleanTemplate.java +++ b/java/src/main/java/org/msgpack/template/BooleanTemplate.java @@ -43,6 +43,8 @@ public class BooleanTemplate implements Template { static { CustomMessage.register(Boolean.class, instance); + TemplateRegistry.register(Boolean.class, instance); + TemplateRegistry.register(boolean.class, instance); } } diff --git a/java/src/main/java/org/msgpack/template/BuiltInTemplateLoader.java b/java/src/main/java/org/msgpack/template/BuiltInTemplateLoader.java new file mode 100644 index 00000000..82c17d5a --- /dev/null +++ b/java/src/main/java/org/msgpack/template/BuiltInTemplateLoader.java @@ -0,0 +1,47 @@ +// +// 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.template; + +public class BuiltInTemplateLoader { + public static void load() { + AnyTemplate.getInstance(); + BigIntegerTemplate.getInstance(); + BooleanArrayTemplate.getInstance(); + BooleanTemplate.getInstance(); + ByteArrayTemplate.getInstance(); + ByteTemplate.getInstance(); + DoubleArrayTemplate.getInstance(); + DoubleTemplate.getInstance(); + FloatArrayTemplate.getInstance(); + FloatTemplate.getInstance(); + IntArrayTemplate.getInstance(); + IntegerTemplate.getInstance(); + LongArrayTemplate.getInstance(); + LongTemplate.getInstance(); + ShortArrayTemplate.getInstance(); + ShortTemplate.getInstance(); + StringTemplate.getInstance(); + + CollectionTemplate.load(); + ListTemplate.load(); + MapTemplate.load(); + NullableTemplate.load(); + ObjectArrayTemplate.load(); + } +} + diff --git a/java/src/main/java/org/msgpack/template/ByteArrayTemplate.java b/java/src/main/java/org/msgpack/template/ByteArrayTemplate.java index 2922828c..ba1a1fd1 100644 --- a/java/src/main/java/org/msgpack/template/ByteArrayTemplate.java +++ b/java/src/main/java/org/msgpack/template/ByteArrayTemplate.java @@ -43,6 +43,7 @@ public class ByteArrayTemplate implements Template { static { CustomMessage.register(byte[].class, instance); + TemplateRegistry.register(byte[].class, instance); } } diff --git a/java/src/main/java/org/msgpack/template/ByteBufferTemplate.java b/java/src/main/java/org/msgpack/template/ByteBufferTemplate.java index c0cb99c6..8ea1a4df 100644 --- a/java/src/main/java/org/msgpack/template/ByteBufferTemplate.java +++ b/java/src/main/java/org/msgpack/template/ByteBufferTemplate.java @@ -56,5 +56,9 @@ public class ByteBufferTemplate implements Template { } static final ByteBufferTemplate instance = new ByteBufferTemplate(); + + static { + TemplateRegistry.register(ByteBuffer.class, instance); + } } diff --git a/java/src/main/java/org/msgpack/template/ByteTemplate.java b/java/src/main/java/org/msgpack/template/ByteTemplate.java index c079e455..121d599a 100644 --- a/java/src/main/java/org/msgpack/template/ByteTemplate.java +++ b/java/src/main/java/org/msgpack/template/ByteTemplate.java @@ -43,6 +43,8 @@ public class ByteTemplate implements Template { static { CustomMessage.register(Byte.class, instance); + TemplateRegistry.register(Byte.class, instance); + TemplateRegistry.register(byte.class, instance); } } diff --git a/java/src/main/java/org/msgpack/template/ClassTemplate.java b/java/src/main/java/org/msgpack/template/ClassTemplate.java index bf8a5be9..68a89e2e 100644 --- a/java/src/main/java/org/msgpack/template/ClassTemplate.java +++ b/java/src/main/java/org/msgpack/template/ClassTemplate.java @@ -18,6 +18,8 @@ package org.msgpack.template; import java.io.IOException; +import java.lang.reflect.AccessibleObject; +import java.lang.annotation.Annotation; import org.msgpack.*; import org.msgpack.annotation.MessagePackDelegate; import org.msgpack.annotation.MessagePackMessage; @@ -30,10 +32,6 @@ import java.math.BigInteger; import java.nio.ByteBuffer; public class ClassTemplate implements Template { - static { - Templates.load(); - } - private Class klass; public ClassTemplate(Class klass) { @@ -119,14 +117,14 @@ public class ClassTemplate implements Template { // pk.packDouble((Double)o); // return; //} - if(o instanceof BigInteger) { - pk.packBigInteger((BigInteger)o); - return; - } - if (o instanceof ByteBuffer) { // FIXME - Templates.tByteBuffer().pack(pk, o); - return; - } + //if(o instanceof BigInteger) { + // pk.packBigInteger((BigInteger)o); + // return; + //} + //if (o instanceof ByteBuffer) { + // Templates.tByteBuffer().pack(pk, o); + // return; + //} MessagePacker packer = CustomPacker.get(klass); if(packer != null) { @@ -134,15 +132,15 @@ public class ClassTemplate implements Template { return; } - if (CustomMessage.isAnnotated(klass, MessagePackMessage.class)) { + if (isAnnotated(klass, MessagePackMessage.class)) { Template tmpl = DynamicTemplate.create(klass); CustomMessage.register(klass, tmpl); tmpl.pack(pk, o); return; - } else if (CustomMessage.isAnnotated(klass, MessagePackDelegate.class)) { + } else if (isAnnotated(klass, MessagePackDelegate.class)) { // FIXME DelegatePacker throw new UnsupportedOperationException("not supported yet. : " + klass.getName()); - } else if (CustomMessage.isAnnotated(klass, MessagePackOrdinalEnum.class)) { + } else if (isAnnotated(klass, MessagePackOrdinalEnum.class)) { Template tmpl = DynamicOrdinalEnumTemplate.create(klass); CustomMessage.register(klass, tmpl); tmpl.pack(pk, o); @@ -171,14 +169,14 @@ public class ClassTemplate implements Template { return obj; } - if (CustomMessage.isAnnotated(klass, MessagePackMessage.class)) { + if (isAnnotated(klass, MessagePackMessage.class)) { Template tmpl = DynamicTemplate.create(klass); CustomMessage.register(klass, tmpl); return tmpl.unpack(pac, to); - } else if (CustomMessage.isAnnotated(klass, MessagePackDelegate.class)) { + } else if (isAnnotated(klass, MessagePackDelegate.class)) { // TODO DelegateUnpacker throw new UnsupportedOperationException("not supported yet. : " + klass.getName()); - } else if (CustomMessage.isAnnotated(klass, MessagePackOrdinalEnum.class)) { + } else if (isAnnotated(klass, MessagePackOrdinalEnum.class)) { Template tmpl = DynamicOrdinalEnumTemplate.create(klass); CustomMessage.register(klass, tmpl); return tmpl.unpack(pac, to); @@ -187,20 +185,20 @@ public class ClassTemplate implements Template { // fallback MessageConverter converter = null; - if (CustomMessage.isAnnotated(klass, MessagePackMessage.class)) { + if (isAnnotated(klass, MessagePackMessage.class)) { Template tmpl = DynamicTemplate.create(klass); CustomMessage.register(klass, tmpl); return tmpl.convert(pac.unpackObject(), to); - } else if (CustomMessage.isAnnotated(klass, MessagePackDelegate.class)) { + } else if (isAnnotated(klass, MessagePackDelegate.class)) { // TODO DelegateConverter throw new UnsupportedOperationException("not supported yet. : " + klass.getName()); - } else if (CustomMessage.isAnnotated(klass, MessagePackOrdinalEnum.class)) { + } else if (isAnnotated(klass, MessagePackOrdinalEnum.class)) { Template tmpl = DynamicOrdinalEnumTemplate.create(klass); CustomMessage.register(klass, tmpl); return tmpl.convert(pac.unpackObject(), to); } - throw new MessageTypeException(); + throw new MessageTypeException("unknown type: "+klass); } catch (IllegalAccessException e) { throw new MessageTypeException(e.getMessage()); // FIXME @@ -228,14 +226,14 @@ public class ClassTemplate implements Template { return obj; } - if (CustomMessage.isAnnotated(klass, MessagePackMessage.class)) { + if (isAnnotated(klass, MessagePackMessage.class)) { Template tmpl = DynamicTemplate.create(klass); CustomMessage.register(klass, tmpl); return tmpl.convert(from, to); - } else if (CustomMessage.isAnnotated(klass, MessagePackDelegate.class)) { + } else if (isAnnotated(klass, MessagePackDelegate.class)) { // TODO DelegateConverter throw new UnsupportedOperationException("not supported yet. : " + klass.getName()); - } else if (CustomMessage.isAnnotated(klass, MessagePackOrdinalEnum.class)) { + } else if (isAnnotated(klass, MessagePackOrdinalEnum.class)) { Template tmpl = DynamicOrdinalEnumTemplate.create(klass); CustomMessage.register(klass, tmpl); return tmpl.convert(from, to); @@ -249,5 +247,9 @@ public class ClassTemplate implements Template { throw new MessageTypeException(e.getMessage()); // FIXME } } + + private boolean isAnnotated(Class ao, Class with) { + return ao.getAnnotation(with) != null; + } } diff --git a/java/src/main/java/org/msgpack/template/CollectionTemplate.java b/java/src/main/java/org/msgpack/template/CollectionTemplate.java index 1621636e..b88500c3 100644 --- a/java/src/main/java/org/msgpack/template/CollectionTemplate.java +++ b/java/src/main/java/org/msgpack/template/CollectionTemplate.java @@ -24,6 +24,8 @@ import java.io.IOException; import org.msgpack.*; public class CollectionTemplate implements Template { + public static void load() { } + private Template elementTemplate; public CollectionTemplate(Template elementTemplate) { @@ -72,5 +74,10 @@ public class CollectionTemplate implements Template { } return list; } + + static { + TemplateRegistry.registerGeneric(Collection.class, new GenericTemplate1(CollectionTemplate.class)); + TemplateRegistry.register(Collection.class, new CollectionTemplate(AnyTemplate.getInstance())); + } } diff --git a/java/src/main/java/org/msgpack/template/DefaultTemplate.java b/java/src/main/java/org/msgpack/template/DefaultTemplate.java new file mode 100644 index 00000000..d9e118d5 --- /dev/null +++ b/java/src/main/java/org/msgpack/template/DefaultTemplate.java @@ -0,0 +1,93 @@ +// +// 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.template; + +import java.io.IOException; +import java.lang.reflect.Type; +import org.msgpack.*; + +public class DefaultTemplate implements Template { + private Class targetClass; + private Type lookupType; + private boolean messagePackable; + private boolean messageUnpackable; + private boolean messageConvertable; + + public DefaultTemplate(Class targetClass) { + this(targetClass, (Type)targetClass); + } + + public DefaultTemplate(Class targetClass, Type lookupType) { + this.targetClass = targetClass; + this.lookupType = lookupType; + this.messagePackable = MessagePackable.class.isAssignableFrom(targetClass); + this.messageUnpackable = MessageUnpackable.class.isAssignableFrom(targetClass); + this.messageConvertable = MessageConvertable.class.isAssignableFrom(targetClass); + } + + public void pack(Packer pk, Object target) throws IOException { + if(messagePackable) { + ((MessagePackable)target).messagePack(pk); + return; + } + Template tmpl = TemplateRegistry.tryLookup(lookupType); + if(tmpl == this || tmpl == null) { + throw new MessageTypeException(); + } + tmpl.pack(pk, target); + } + + public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException { + if(messageUnpackable) { + if(to == null) { + try { + to = targetClass.newInstance(); + } catch (Exception e) { + throw new MessageTypeException(e); + } + } + ((MessageUnpackable)to).messageUnpack(pac); + return to; + } + Template tmpl = TemplateRegistry.tryLookup(lookupType); + if(tmpl == this || tmpl == null) { + throw new MessageTypeException(); + } + return tmpl.unpack(pac, to); + } + + public Object convert(MessagePackObject from, Object to) throws MessageTypeException { + if(messageConvertable) { + if(to == null) { + try { + to = targetClass.newInstance(); + } catch (Exception e) { + throw new MessageTypeException(e); + } + } + ((MessageConvertable)to).messageConvert(from); + return from; + } + Template tmpl = TemplateRegistry.tryLookup(lookupType); + if(tmpl == this || tmpl == null) { + throw new MessageTypeException(); + } + return tmpl.convert(from, to); + } +} + diff --git a/java/src/main/java/org/msgpack/template/DoubleArrayTemplate.java b/java/src/main/java/org/msgpack/template/DoubleArrayTemplate.java new file mode 100644 index 00000000..8d36acb9 --- /dev/null +++ b/java/src/main/java/org/msgpack/template/DoubleArrayTemplate.java @@ -0,0 +1,76 @@ +// +// 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.template; + +import java.io.IOException; +import org.msgpack.*; + +public class DoubleArrayTemplate implements Template { + private DoubleArrayTemplate() { } + + public void pack(Packer pk, Object target) throws IOException { + if(!(target instanceof double[])) { + throw new MessageTypeException(); + } + double[] array = (double[])target; + pk.packArray(array.length); + for(double a : array) { + pk.pack(a); + } + } + + public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException { + int length = pac.unpackArray(); + double[] array; + if(to != null && to instanceof double[] && ((double[])to).length == length) { + array = (double[])to; + } else { + array = new double[length]; + } + for(int i=0; i < length; i++) { + array[i] = pac.unpackDouble(); + } + return array; + } + + public Object convert(MessagePackObject from, Object to) throws MessageTypeException { + MessagePackObject[] src = from.asArray(); + double[] array; + if(to != null && to instanceof double[] && ((double[])to).length == src.length) { + array = (double[])to; + } else { + array = new double[src.length]; + } + for(int i=0; i < src.length; i++) { + MessagePackObject s = src[i]; + array[i] = s.asDouble(); + } + return array; + } + + static public DoubleArrayTemplate getInstance() { + return instance; + } + + static final DoubleArrayTemplate instance = new DoubleArrayTemplate(); + + static { + TemplateRegistry.register(double[].class, instance); + } +} + diff --git a/java/src/main/java/org/msgpack/template/DoubleTemplate.java b/java/src/main/java/org/msgpack/template/DoubleTemplate.java index 7bbf3f81..602e69b5 100644 --- a/java/src/main/java/org/msgpack/template/DoubleTemplate.java +++ b/java/src/main/java/org/msgpack/template/DoubleTemplate.java @@ -43,6 +43,8 @@ public class DoubleTemplate implements Template { static { CustomMessage.register(Double.class, instance); + TemplateRegistry.register(Double.class, instance); + TemplateRegistry.register(double.class, instance); } } diff --git a/java/src/main/java/org/msgpack/template/FieldList.java b/java/src/main/java/org/msgpack/template/FieldList.java new file mode 100644 index 00000000..8d6f28ff --- /dev/null +++ b/java/src/main/java/org/msgpack/template/FieldList.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.template; + +import java.util.List; +import java.util.ArrayList; + +public class FieldList { + public static class Entry { + public Entry() { + this.name = null; + this.option = null; + } + + public Entry(String name, FieldOption option) { + this.name = name; + this.option = option; + } + + private String name; + private FieldOption option; + + public String getName() { + return name; + } + + public FieldOption getOption() { + return option; + } + + boolean isAvailable() { + return this.name != null; + } + + boolean isRequired() { + return this.option == FieldOption.REQUIRED; + } + + boolean isOptional() { + return this.option == FieldOption.OPTIONAL; + } + + boolean isNullable() { + return this.option == FieldOption.NULLABLE; + } + } + + private ArrayList list; + + public FieldList() { + list = new ArrayList(); + } + + public void add(final String name) { + add(name, FieldOption.REQUIRED); + } + + public void add(final String name, final FieldOption option) { + list.add(new Entry(name, option)); + } + + public void put(int index, final String name) { + put(index, name, FieldOption.REQUIRED); + } + + public void put(int index, final String name, final FieldOption option) { + if(list.size() < index) { + do { + list.add(new Entry()); + } while(list.size() < index); + list.add(new Entry(name, option)); + } else { + list.set(index, new Entry(name, option)); + } + } + + List getList() { + return list; + } +} + diff --git a/java/src/main/java/org/msgpack/template/FieldOption.java b/java/src/main/java/org/msgpack/template/FieldOption.java new file mode 100644 index 00000000..41f152ff --- /dev/null +++ b/java/src/main/java/org/msgpack/template/FieldOption.java @@ -0,0 +1,27 @@ +// +// 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.template; + +public enum FieldOption { + IGNORE, + REQUIRED, + OPTIONAL, + NULLABLE, + DEFAULT; +} + diff --git a/java/src/main/java/org/msgpack/template/FloatArrayTemplate.java b/java/src/main/java/org/msgpack/template/FloatArrayTemplate.java new file mode 100644 index 00000000..7c2685ba --- /dev/null +++ b/java/src/main/java/org/msgpack/template/FloatArrayTemplate.java @@ -0,0 +1,76 @@ +// +// 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.template; + +import java.io.IOException; +import org.msgpack.*; + +public class FloatArrayTemplate implements Template { + private FloatArrayTemplate() { } + + public void pack(Packer pk, Object target) throws IOException { + if(!(target instanceof float[])) { + throw new MessageTypeException(); + } + float[] array = (float[])target; + pk.packArray(array.length); + for(float a : array) { + pk.pack(a); + } + } + + public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException { + int length = pac.unpackArray(); + float[] array; + if(to != null && to instanceof float[] && ((float[])to).length == length) { + array = (float[])to; + } else { + array = new float[length]; + } + for(int i=0; i < length; i++) { + array[i] = pac.unpackFloat(); + } + return array; + } + + public Object convert(MessagePackObject from, Object to) throws MessageTypeException { + MessagePackObject[] src = from.asArray(); + float[] array; + if(to != null && to instanceof float[] && ((float[])to).length == src.length) { + array = (float[])to; + } else { + array = new float[src.length]; + } + for(int i=0; i < src.length; i++) { + MessagePackObject s = src[i]; + array[i] = s.asFloat(); + } + return array; + } + + static public FloatArrayTemplate getInstance() { + return instance; + } + + static final FloatArrayTemplate instance = new FloatArrayTemplate(); + + static { + TemplateRegistry.register(float[].class, instance); + } +} + diff --git a/java/src/main/java/org/msgpack/template/FloatTemplate.java b/java/src/main/java/org/msgpack/template/FloatTemplate.java index 205c0b3e..19718857 100644 --- a/java/src/main/java/org/msgpack/template/FloatTemplate.java +++ b/java/src/main/java/org/msgpack/template/FloatTemplate.java @@ -43,6 +43,8 @@ public class FloatTemplate implements Template { static { CustomMessage.register(Float.class, instance); + TemplateRegistry.register(Float.class, instance); + TemplateRegistry.register(float.class, instance); } } diff --git a/java/src/main/java/org/msgpack/template/GenericTemplate.java b/java/src/main/java/org/msgpack/template/GenericTemplate.java new file mode 100644 index 00000000..3fa204c9 --- /dev/null +++ b/java/src/main/java/org/msgpack/template/GenericTemplate.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.template; + +import org.msgpack.Template; + +public interface GenericTemplate { + public Template build(Template[] params); +} + diff --git a/java/src/main/java/org/msgpack/template/GenericTemplate1.java b/java/src/main/java/org/msgpack/template/GenericTemplate1.java new file mode 100644 index 00000000..ca89b655 --- /dev/null +++ b/java/src/main/java/org/msgpack/template/GenericTemplate1.java @@ -0,0 +1,54 @@ +// +// 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.template; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import org.msgpack.Template; + +public class GenericTemplate1 implements GenericTemplate { + Constructor constructor; + + public GenericTemplate1(Class tmpl) { + try { + this.constructor = tmpl.getConstructor(new Class[]{Template.class}); + constructor.newInstance(new Object[]{AnyTemplate.getInstance()}); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException(e); + } catch (InvocationTargetException e) { + throw new IllegalArgumentException(e); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException(e); + } catch (InstantiationException e) { + throw new IllegalArgumentException(e); + } + } + + public Template build(Template[] params) { + try { + return constructor.newInstance(params); + } catch (InvocationTargetException e) { + throw new IllegalArgumentException(e); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException(e); + } catch (InstantiationException e) { + throw new IllegalArgumentException(e); + } + } +} + diff --git a/java/src/main/java/org/msgpack/template/GenericTemplate2.java b/java/src/main/java/org/msgpack/template/GenericTemplate2.java new file mode 100644 index 00000000..5f938775 --- /dev/null +++ b/java/src/main/java/org/msgpack/template/GenericTemplate2.java @@ -0,0 +1,54 @@ +// +// 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.template; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import org.msgpack.Template; + +public class GenericTemplate2 implements GenericTemplate { + Constructor constructor; + + public GenericTemplate2(Class tmpl) { + try { + this.constructor = tmpl.getConstructor(new Class[]{Template.class, Template.class}); + constructor.newInstance(new Object[]{AnyTemplate.getInstance(), AnyTemplate.getInstance()}); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException(e); + } catch (InvocationTargetException e) { + throw new IllegalArgumentException(e); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException(e); + } catch (InstantiationException e) { + throw new IllegalArgumentException(e); + } + } + + public Template build(Template[] params) { + try { + return constructor.newInstance(params); + } catch (InvocationTargetException e) { + throw new IllegalArgumentException(e); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException(e); + } catch (InstantiationException e) { + throw new IllegalArgumentException(e); + } + } +} + diff --git a/java/src/main/java/org/msgpack/template/IntArrayTemplate.java b/java/src/main/java/org/msgpack/template/IntArrayTemplate.java new file mode 100644 index 00000000..0f7b9bff --- /dev/null +++ b/java/src/main/java/org/msgpack/template/IntArrayTemplate.java @@ -0,0 +1,76 @@ +// +// 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.template; + +import java.io.IOException; +import org.msgpack.*; + +public class IntArrayTemplate implements Template { + private IntArrayTemplate() { } + + public void pack(Packer pk, Object target) throws IOException { + if(!(target instanceof int[])) { + throw new MessageTypeException(); + } + int[] array = (int[])target; + pk.packArray(array.length); + for(int a : array) { + pk.pack(a); + } + } + + public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException { + int length = pac.unpackArray(); + int[] array; + if(to != null && to instanceof int[] && ((int[])to).length == length) { + array = (int[])to; + } else { + array = new int[length]; + } + for(int i=0; i < length; i++) { + array[i] = pac.unpackInt(); + } + return array; + } + + public Object convert(MessagePackObject from, Object to) throws MessageTypeException { + MessagePackObject[] src = from.asArray(); + int[] array; + if(to != null && to instanceof int[] && ((int[])to).length == src.length) { + array = (int[])to; + } else { + array = new int[src.length]; + } + for(int i=0; i < src.length; i++) { + MessagePackObject s = src[i]; + array[i] = s.asInt(); + } + return array; + } + + static public IntArrayTemplate getInstance() { + return instance; + } + + static final IntArrayTemplate instance = new IntArrayTemplate(); + + static { + TemplateRegistry.register(int[].class, instance); + } +} + diff --git a/java/src/main/java/org/msgpack/template/IntegerTemplate.java b/java/src/main/java/org/msgpack/template/IntegerTemplate.java index 630aa79a..6b0d9f70 100644 --- a/java/src/main/java/org/msgpack/template/IntegerTemplate.java +++ b/java/src/main/java/org/msgpack/template/IntegerTemplate.java @@ -43,6 +43,8 @@ public class IntegerTemplate implements Template { static { CustomMessage.register(Integer.class, instance); + TemplateRegistry.register(Integer.class, instance); + TemplateRegistry.register(int.class, instance); } } diff --git a/java/src/main/java/org/msgpack/template/JavassistTemplateBuilder.java b/java/src/main/java/org/msgpack/template/JavassistTemplateBuilder.java new file mode 100644 index 00000000..cac6bd17 --- /dev/null +++ b/java/src/main/java/org/msgpack/template/JavassistTemplateBuilder.java @@ -0,0 +1,523 @@ +// +// 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.template; + +import java.io.IOException; +import java.lang.reflect.*; +import java.util.Map; +import java.util.HashMap; +import org.msgpack.*; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtField; +import javassist.CtMethod; +import javassist.CtNewConstructor; +import javassist.CtNewMethod; +import javassist.LoaderClassPath; +import javassist.NotFoundException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JavassistTemplateBuilder extends TemplateBuilder { + private static JavassistTemplateBuilder instance; + public synchronized static JavassistTemplateBuilder getInstance() { + if(instance == null) { + instance = new JavassistTemplateBuilder(); + } + return instance; + } + + private JavassistTemplateBuilder() { + this.pool = ClassPool.getDefault(); + } + + private static Logger LOG = LoggerFactory + .getLogger(JavassistTemplateBuilder.class); + + protected ClassPool pool; + private int seqId = 0; + + CtClass makeCtClass(String className) { + return pool.makeClass(className); + } + + CtClass getCtClass(String className) throws NotFoundException { + return pool.get(className); + } + + int nextSeqId() { + return seqId++; + } + + public static abstract class JavassistTemplate extends AbstractTemplate { + public Class targetClass; + public Template[] templates; + + public JavassistTemplate(Class targetClass, Template[] templates) { + this.targetClass = targetClass; + this.templates = templates; + } + } + + private static abstract class BuildContextBase { + protected JavassistTemplateBuilder director; + protected Class origClass; + protected String origName; + protected String tmplName; + protected CtClass tmplCtClass; + + protected abstract void setSuperClass() throws CannotCompileException, NotFoundException; + protected abstract void buildConstructor() throws CannotCompileException, NotFoundException; + protected void buildMethodInit() { } + protected abstract String buildPackMethodBody(); + protected abstract String buildUnpackMethodBody(); + protected abstract String buildConvertMethodBody(); + protected abstract Template buildInstance(Class c) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException; + + public BuildContextBase(JavassistTemplateBuilder director) { + this.director = director; + } + + protected Template build(Class targetClass) { + try { + reset(targetClass); + buildClass(); + buildConstructor(); + buildMethodInit(); + buildPackMethod(); + buildUnpackMethod(); + buildConvertMethod(); + return buildInstance(createClass()); + } catch (Exception e) { + if(this.stringBuilder != null) { + LOG.error("builder: "+this.stringBuilder.toString()); + } + throw new MessageTypeException(e); + } + } + + protected void reset(Class targetClass) { + this.origClass = targetClass; + this.origName = this.origClass.getName(); + this.tmplName = this.origName + "_$$_Template" + director.nextSeqId(); + this.tmplCtClass = director.makeCtClass(this.tmplName); + } + + protected void buildClass() throws CannotCompileException, NotFoundException { + setSuperClass(); + this.tmplCtClass.addInterface( + director.getCtClass(Template.class.getName())); + } + + protected void buildPackMethod() throws CannotCompileException, NotFoundException { + String mbody = buildPackMethodBody(); + int mod = javassist.Modifier.PUBLIC; + CtClass returnType = CtClass.voidType; + String mname = "pack"; + CtClass[] paramTypes = new CtClass[] { + director.getCtClass(Packer.class.getName()), + director.getCtClass(Object.class.getName()) + }; + CtClass[] exceptTypes = new CtClass[] { + director.getCtClass(IOException.class.getName()) + }; + CtMethod newCtMethod = CtNewMethod.make( + mod, returnType, mname, + paramTypes, exceptTypes, mbody, + this.tmplCtClass); + this.tmplCtClass.addMethod(newCtMethod); + } + + protected void buildUnpackMethod() throws CannotCompileException, NotFoundException { + String mbody = buildUnpackMethodBody(); + int mod = javassist.Modifier.PUBLIC; + CtClass returnType = director.getCtClass(Object.class.getName()); + String mname = "unpack"; + CtClass[] paramTypes = new CtClass[] { + director.getCtClass(Unpacker.class.getName()), + director.getCtClass(Object.class.getName()) + }; + CtClass[] exceptTypes = new CtClass[] { + director.getCtClass(MessageTypeException.class.getName()) + }; + CtMethod newCtMethod = CtNewMethod.make( + mod, returnType, mname, + paramTypes, exceptTypes, mbody, + this.tmplCtClass); + this.tmplCtClass.addMethod(newCtMethod); + } + + protected void buildConvertMethod() throws CannotCompileException, NotFoundException { + String mbody = buildConvertMethodBody(); + int mod = javassist.Modifier.PUBLIC; + CtClass returnType = director.getCtClass(Object.class.getName()); + String mname = "convert"; + CtClass[] paramTypes = new CtClass[] { + director.getCtClass(MessagePackObject.class.getName()), + director.getCtClass(Object.class.getName()) + }; + CtClass[] exceptTypes = new CtClass[] { + director.getCtClass(MessageTypeException.class.getName()) + }; + CtMethod newCtMethod = CtNewMethod.make( + mod, returnType, mname, + paramTypes, exceptTypes, mbody, + this.tmplCtClass); + this.tmplCtClass.addMethod(newCtMethod); + } + + protected Class createClass() throws CannotCompileException { + return (Class)this.tmplCtClass.toClass(null, null); + } + + protected StringBuilder stringBuilder = null; + + protected void resetStringBuilder() { + this.stringBuilder = new StringBuilder(); + } + + protected void buildString(String str) { + this.stringBuilder.append(str); + } + + protected void buildString(String format, Object... args) { + this.stringBuilder.append(String.format(format, args)); + } + + protected String getBuiltString() { + return this.stringBuilder.toString(); + } + } + + private static class BuildContext extends BuildContextBase { + protected FieldEntry[] entries; + protected Template[] templates; + protected int minimumArrayLength; + + public BuildContext(JavassistTemplateBuilder director) { + super(director); + } + + public Template buildTemplate(Class targetClass, FieldEntry[] entries, Template[] templates) { + this.entries = entries; + this.templates = templates; + return build(targetClass); + } + + protected void setSuperClass() throws CannotCompileException, NotFoundException { + this.tmplCtClass.setSuperclass( + director.getCtClass(JavassistTemplate.class.getName())); + } + + protected void buildConstructor() throws CannotCompileException, NotFoundException { + // Constructor(Class targetClass, Template[] templates) + CtConstructor newCtCons = CtNewConstructor.make( + new CtClass[] { + director.getCtClass(Class.class.getName()), + director.getCtClass(Template.class.getName()+"[]") + }, + new CtClass[0], + this.tmplCtClass); + this.tmplCtClass.addConstructor(newCtCons); + } + + protected Template buildInstance(Class c) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { + Constructor cons = c.getConstructor(new Class[] { + Class.class, + Template[].class + }); + Object tmpl = cons.newInstance(new Object[] { + this.origClass, + this.templates + }); + return (Template)tmpl; + } + + protected void buildMethodInit() { + this.minimumArrayLength = 0; + for(int i=0; i < entries.length; i++) { + FieldEntry e = entries[i]; + if(e.isRequired() || e.isNullable()) { + this.minimumArrayLength = i+1; + } + } + } + + protected String buildPackMethodBody() { + resetStringBuilder(); + buildString("{"); + + buildString("%s _$$_t = (%s)$2;", this.origName, this.origName); + buildString("$1.packArray(%d);", entries.length); + for(int i=0; i < entries.length; i++) { + FieldEntry e = entries[i]; + if(!e.isAvailable()) { + buildString("$1.packNil();"); + continue; + } + Class type = e.getType(); + if(type.isPrimitive()) { + buildString("$1.%s(_$$_t.%s);", primitivePackName(type), e.getName()); + } else { + buildString("if(_$$_t.%s == null) {", e.getName()); + if(!e.isNullable() && !e.isOptional()) { + buildString("throw new %s();", MessageTypeException.class.getName()); + } else { + buildString("$1.packNil();"); + } + buildString("} else {"); + buildString(" this.templates[%d].pack($1, _$$_t.%s);", i, e.getName()); + buildString("}"); + } + } + + buildString("}"); + return getBuiltString(); + } + + protected String buildUnpackMethodBody() { + resetStringBuilder(); + buildString("{ "); + + buildString("%s _$$_t;", this.origName); + buildString("if($2 == null) {"); + buildString(" _$$_t = (%s)this.targetClass.newInstance();", this.origName); + buildString("} else {"); + buildString(" _$$_t = (%s)$2;", this.origName); + buildString("}"); + + buildString("int length = $1.unpackArray();"); + buildString("if(length < %d) {", this.minimumArrayLength); + buildString(" throw new %s();", MessageTypeException.class.getName()); + buildString("}"); + + int i; + for(i=0; i < this.minimumArrayLength; i++) { + FieldEntry e = entries[i]; + if(!e.isAvailable()) { + buildString("$1.unpackObject();"); + continue; + } + + buildString("if($1.tryUnpackNull()) {"); + if(e.isRequired()) { + // Requred + nil => exception + buildString("throw new %s();", MessageTypeException.class.getName()); + } else if(e.isOptional()) { + // Optional + nil => keep default value + } else { // Nullable + // Nullable + nil => set null + buildString("_$$_t.%s = null;", e.getName()); + } + buildString("} else {"); + Class type = e.getType(); + if(type.isPrimitive()) { + buildString("_$$_t.%s = $1.%s();", e.getName(), primitiveUnpackName(type)); + } else { + buildString("_$$_t.%s = (%s)this.templates[%d].unpack($1, _$$_t.%s);", e.getName(), e.getJavaTypeName(), i, e.getName()); + } + buildString("}"); + } + + for(; i < entries.length; i++) { + buildString("if(length <= %d) { return _$$_t; }", i); + + FieldEntry e = entries[i]; + if(!e.isAvailable()) { + buildString("$1.unpackObject();"); + continue; + } + + buildString("if($1.tryUnpackNull()) {"); + buildString("} else {"); + Class type = e.getType(); + if(type.isPrimitive()) { + buildString("_$$_t.%s = $1.%s();", e.getName(), primitiveUnpackName(type)); + } else { + buildString("_$$_t.%s = (%s)this.templates[%d].unpack($1, _$$_t.%s);", e.getName(), e.getJavaTypeName(), i, e.getName()); + } + buildString("}"); + } + + buildString("for(int i=%d; i < length; i++) {", i); + buildString(" $1.unpackObject();"); + buildString("}"); + + buildString("return _$$_t;"); + + buildString("}"); + return getBuiltString(); + } + + protected String buildConvertMethodBody() { + resetStringBuilder(); + buildString("{ "); + + buildString("%s _$$_t;", this.origName); + buildString("if($2 == null) {"); + buildString(" _$$_t = (%s)this.targetClass.newInstance();", this.origName); + buildString("} else {"); + buildString(" _$$_t = (%s)$2;", this.origName); + buildString("}"); + + buildString("%s[] array = $1.asArray();", MessagePackObject.class.getName()); + buildString("int length = array.length;"); + buildString("if(length < %d) {", this.minimumArrayLength); + buildString(" throw new %s();", MessageTypeException.class.getName()); + buildString("}"); + + int i; + for(i=0; i < this.minimumArrayLength; i++) { + FieldEntry e = entries[i]; + if(!e.isAvailable()) { + continue; + } + + buildString("%s obj = array[%d];", MessagePackObject.class.getName(), i); + buildString("if(obj.isNil()) {"); + if(e.isRequired()) { + // Requred + nil => exception + buildString("throw new %s();", MessageTypeException.class.getName()); + } else if(e.isOptional()) { + // Optional + nil => keep default value + } else { // Nullable + // Nullable + nil => set null + buildString("_$$_t.%s = null;", e.getName()); + } + buildString("} else {"); + Class type = e.getType(); + if(type.isPrimitive()) { + buildString("_$$_t.%s = obj.%s();", e.getName(), primitiveConvertName(type)); + } else { + buildString("_$$_t.%s = (%s)this.templates[%d].convert(obj, _$$_t.%s);", e.getName(), e.getJavaTypeName(), i, e.getName()); + } + buildString("}"); + } + + for(; i < entries.length; i++) { + buildString("if(length <= %d) { return _$$_t; }", i); + + FieldEntry e = entries[i]; + if(!e.isAvailable()) { + continue; + } + + buildString("%s obj = array[%d];", MessagePackObject.class.getName(), i); + buildString("if(obj.isNil()) {"); + buildString("} else {"); + Class type = e.getType(); + if(type.isPrimitive()) { + buildString("_$$_t.%s = $1.%s();", e.getName(), primitiveConvertName(type)); + } else { + buildString("_$$_t.%s = (%s)this.templates[%d].convert(obj, _$$_t.%s);", e.getName(), e.getJavaTypeName(), i, e.getName()); + } + buildString("}"); + } + + buildString("return _$$_t;"); + + buildString("}"); + return getBuiltString(); + } + + + protected String primitivePackName(Class type) { + if(type == boolean.class) { + return "packBoolean"; + } else if(type == byte.class) { + return "packByte"; + } else if(type == short.class) { + return "packShort"; + } else if(type == int.class) { + return "packInt"; + } else if(type == long.class) { + return "packLong"; + } else if(type == float.class) { + return "packFloat"; + } else if(type == double.class) { + return "packDouble"; + } + return null; + } + + protected String primitiveUnpackName(Class type) { + if(type == boolean.class) { + return "unpackBoolean"; + } else if(type == byte.class) { + return "unpackByte"; + } else if(type == short.class) { + return "unpackShort"; + } else if(type == int.class) { + return "unpackInt"; + } else if(type == long.class) { + return "unpackLong"; + } else if(type == float.class) { + return "unpackFloat"; + } else if(type == double.class) { + return "unpackDouble"; + } + return null; + } + + protected String primitiveConvertName(Class type) { + if(type == boolean.class) { + return "asBoolean"; + } else if(type == byte.class) { + return "asByte"; + } else if(type == short.class) { + return "asShort"; + } else if(type == int.class) { + return "asInt"; + } else if(type == long.class) { + return "asLong"; + } else if(type == float.class) { + return "asFloat"; + } else if(type == double.class) { + return "asDouble"; + } + return null; + } + } + + public Template buildTemplate(Class targetClass, FieldEntry[] entries) { + Template[] tmpls = new Template[entries.length]; + for(int i=0; i < entries.length; i++) { + FieldEntry e = entries[i]; + Template tmpl = TemplateRegistry.lookup(e.getGenericType(), true); + tmpls[i] = tmpl; + } + + BuildContext bc = new BuildContext(this); + return bc.buildTemplate(targetClass, entries, tmpls); + } + + static class JavassistOrdinalEnumTemplate extends ReflectionTemplateBuilder.ReflectionOrdinalEnumTemplate { + JavassistOrdinalEnumTemplate(Enum[] entries) { + super(entries); + } + } + + public Template buildOrdinalEnumTemplate(Class targetClass, Enum[] entries) { + return new JavassistOrdinalEnumTemplate(entries); + } +} + diff --git a/java/src/main/java/org/msgpack/template/ListTemplate.java b/java/src/main/java/org/msgpack/template/ListTemplate.java index f3bd2738..b0a24f3e 100644 --- a/java/src/main/java/org/msgpack/template/ListTemplate.java +++ b/java/src/main/java/org/msgpack/template/ListTemplate.java @@ -23,6 +23,8 @@ import java.io.IOException; import org.msgpack.*; public class ListTemplate implements Template { + static void load() { } + private Template elementTemplate; public ListTemplate(Template elementTemplate) { @@ -75,5 +77,10 @@ public class ListTemplate implements Template { } return list; } + + static { + TemplateRegistry.registerGeneric(List.class, new GenericTemplate1(ListTemplate.class)); + TemplateRegistry.register(List.class, new ListTemplate(AnyTemplate.getInstance())); + } } diff --git a/java/src/main/java/org/msgpack/template/LongArrayTemplate.java b/java/src/main/java/org/msgpack/template/LongArrayTemplate.java new file mode 100644 index 00000000..c216143a --- /dev/null +++ b/java/src/main/java/org/msgpack/template/LongArrayTemplate.java @@ -0,0 +1,76 @@ +// +// 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.template; + +import java.io.IOException; +import org.msgpack.*; + +public class LongArrayTemplate implements Template { + private LongArrayTemplate() { } + + public void pack(Packer pk, Object target) throws IOException { + if(!(target instanceof long[])) { + throw new MessageTypeException(); + } + long[] array = (long[])target; + pk.packArray(array.length); + for(long a : array) { + pk.pack(a); + } + } + + public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException { + int length = pac.unpackArray(); + long[] array; + if(to != null && to instanceof long[] && ((long[])to).length == length) { + array = (long[])to; + } else { + array = new long[length]; + } + for(int i=0; i < length; i++) { + array[i] = pac.unpackLong(); + } + return array; + } + + public Object convert(MessagePackObject from, Object to) throws MessageTypeException { + MessagePackObject[] src = from.asArray(); + long[] array; + if(to != null && to instanceof long[] && ((long[])to).length == src.length) { + array = (long[])to; + } else { + array = new long[src.length]; + } + for(int i=0; i < src.length; i++) { + MessagePackObject s = src[i]; + array[i] = s.asLong(); + } + return array; + } + + static public LongArrayTemplate getInstance() { + return instance; + } + + static final LongArrayTemplate instance = new LongArrayTemplate(); + + static { + TemplateRegistry.register(long[].class, instance); + } +} + diff --git a/java/src/main/java/org/msgpack/template/LongTemplate.java b/java/src/main/java/org/msgpack/template/LongTemplate.java index c5c8865d..08ce8ede 100644 --- a/java/src/main/java/org/msgpack/template/LongTemplate.java +++ b/java/src/main/java/org/msgpack/template/LongTemplate.java @@ -43,6 +43,8 @@ public class LongTemplate implements Template { static { CustomMessage.register(Long.class, instance); + TemplateRegistry.register(Long.class, instance); + TemplateRegistry.register(long.class, instance); } } diff --git a/java/src/main/java/org/msgpack/template/MapTemplate.java b/java/src/main/java/org/msgpack/template/MapTemplate.java index eeccd48b..9815e3b6 100644 --- a/java/src/main/java/org/msgpack/template/MapTemplate.java +++ b/java/src/main/java/org/msgpack/template/MapTemplate.java @@ -23,6 +23,8 @@ import java.io.IOException; import org.msgpack.*; public class MapTemplate implements Template { + static void load() { } + private Template keyTemplate; private Template valueTemplate; @@ -86,5 +88,10 @@ public class MapTemplate implements Template { } return map; } + + static { + TemplateRegistry.registerGeneric(Map.class, new GenericTemplate2(MapTemplate.class)); + TemplateRegistry.register(Map.class, new MapTemplate(AnyTemplate.getInstance(), AnyTemplate.getInstance())); + } } diff --git a/java/src/main/java/org/msgpack/template/NullableTemplate.java b/java/src/main/java/org/msgpack/template/NullableTemplate.java index 0efc0cf3..431da596 100644 --- a/java/src/main/java/org/msgpack/template/NullableTemplate.java +++ b/java/src/main/java/org/msgpack/template/NullableTemplate.java @@ -21,6 +21,8 @@ import java.io.IOException; import org.msgpack.*; public class NullableTemplate implements Template { + static void load() { } + private Template elementTemplate; public NullableTemplate(Template elementTemplate) { diff --git a/java/src/main/java/org/msgpack/template/ObjectArrayTemplate.java b/java/src/main/java/org/msgpack/template/ObjectArrayTemplate.java new file mode 100644 index 00000000..93b94d47 --- /dev/null +++ b/java/src/main/java/org/msgpack/template/ObjectArrayTemplate.java @@ -0,0 +1,79 @@ +// +// 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.template; + +import java.util.List; +import java.util.ArrayList; +import java.io.IOException; +import org.msgpack.*; + +public class ObjectArrayTemplate implements Template { + static void load() { } + + private Template componentTemplate; + + public ObjectArrayTemplate(Template componentTemplate) { + this.componentTemplate = componentTemplate; + } + + public Template getcomponentTemplate() { + return componentTemplate; + } + + @SuppressWarnings("unchecked") + public void pack(Packer pk, Object target) throws IOException { + if(!(target instanceof Object[])) { + throw new MessageTypeException(); + } + Object[] array = (Object[])target; + pk.packArray(array.length); + for(Object a : array) { + componentTemplate.pack(pk, a); + } + } + + public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException { + int length = pac.unpackArray(); + Object[] array; + if(to != null && to instanceof Object[] && ((Object[])to).length == length) { + array = (Object[])to; + } else { + array = new Object[length]; + } + for(int i=0; i < length; i++) { + array[i] = componentTemplate.unpack(pac, null); + } + return array; + } + + public Object convert(MessagePackObject from, Object to) throws MessageTypeException { + MessagePackObject[] src = from.asArray(); + Object[] array; + if(to != null && to instanceof Object[] && ((Object[])to).length == src.length) { + array = (Object[])to; + } else { + array = new Object[src.length]; + } + for(int i=0; i < src.length; i++) { + MessagePackObject s = src[i]; + array[i] = componentTemplate.convert(s, array[i]); + } + return array; + } +} + diff --git a/java/src/main/java/org/msgpack/template/ReflectionTemplateBuilder.java b/java/src/main/java/org/msgpack/template/ReflectionTemplateBuilder.java new file mode 100644 index 00000000..aecde46e --- /dev/null +++ b/java/src/main/java/org/msgpack/template/ReflectionTemplateBuilder.java @@ -0,0 +1,444 @@ +// +// 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.template; + +import java.io.IOException; +import java.lang.reflect.*; +import java.util.Map; +import java.util.HashMap; +import org.msgpack.*; + +public class ReflectionTemplateBuilder extends TemplateBuilder { + private static ReflectionTemplateBuilder instance; + public synchronized static ReflectionTemplateBuilder getInstance() { + if(instance == null) { + instance = new ReflectionTemplateBuilder(); + } + return instance; + } + + private ReflectionTemplateBuilder() { + } + + static abstract class ReflectionFieldEntry extends FieldEntry { + public ReflectionFieldEntry(FieldEntry e) { + super(e.getField(), e.getOption()); + } + + public abstract void pack(Object target, Packer pac) throws IOException; + + public abstract void convert(Object target, MessagePackObject obj) throws MessageTypeException, IllegalAccessException; + + public abstract void unpack(Object target, Unpacker pac) throws IOException, MessageTypeException, IllegalAccessException; + + public void setNull(Object target) throws IllegalAccessException { + getField().set(target, null); + } + } + + static class ObjectFieldEntry extends ReflectionFieldEntry { + private Template template; + + ObjectFieldEntry(FieldEntry e, Template template) { + super(e); + this.template = template; + } + + public void pack(Object target, Packer pac) throws IOException { + template.pack(pac, target); + } + + public void convert(Object target, MessagePackObject obj) throws MessageTypeException, IllegalAccessException { + Field f = getField(); + Class type = (Class)f.getType(); + Object fieldReference = f.get(target); + Object valueReference = template.convert(obj, fieldReference); + if(valueReference != fieldReference) { + f.set(target, valueReference); + } + } + + public void unpack(Object target, Unpacker pac) throws IOException, MessageTypeException, IllegalAccessException { + Field f = getField(); + Class type = (Class)f.getType(); + Object fieldReference = f.get(target); + Object valueReference = template.unpack(pac, fieldReference); + if(valueReference != fieldReference) { + f.set(target, valueReference); + } + } + } + + static class BooleanFieldEntry extends ReflectionFieldEntry { + BooleanFieldEntry(FieldEntry e) { + super(e); + } + public void pack(Object target, Packer pac) throws IOException { + pac.pack((boolean)(Boolean)target); + } + public void convert(Object target, MessagePackObject obj) throws MessageTypeException, IllegalAccessException { + getField().setBoolean(target, obj.asBoolean()); + } + public void unpack(Object target, Unpacker pac) throws IOException, MessageTypeException, IllegalAccessException { + getField().setBoolean(target, pac.unpackBoolean()); + } + } + + static class ByteFieldEntry extends ReflectionFieldEntry { + ByteFieldEntry(FieldEntry e) { + super(e); + } + public void pack(Object target, Packer pac) throws IOException { + pac.pack((byte)(Byte)target); + } + public void convert(Object target, MessagePackObject obj) throws MessageTypeException, IllegalAccessException { + getField().setByte(target, obj.asByte()); + } + public void unpack(Object target, Unpacker pac) throws IOException, MessageTypeException, IllegalAccessException { + getField().setByte(target, pac.unpackByte()); + } + } + + static class ShortFieldEntry extends ReflectionFieldEntry { + ShortFieldEntry(FieldEntry e) { + super(e); + } + public void pack(Object target, Packer pac) throws IOException { + pac.pack((short)(Short)target); + } + public void convert(Object target, MessagePackObject obj) throws MessageTypeException, IllegalAccessException { + getField().setShort(target, obj.asShort()); + } + public void unpack(Object target, Unpacker pac) throws IOException, MessageTypeException, IllegalAccessException { + getField().setShort(target, pac.unpackShort()); + } + } + + static class IntFieldEntry extends ReflectionFieldEntry { + IntFieldEntry(FieldEntry e) { + super(e); + } + public void pack(Object target, Packer pac) throws IOException { + pac.pack((int)(Integer)target); + } + public void convert(Object target, MessagePackObject obj) throws MessageTypeException, IllegalAccessException { + getField().setInt(target, obj.asInt()); + } + public void unpack(Object target, Unpacker pac) throws IOException, MessageTypeException, IllegalAccessException { + getField().setInt(target, pac.unpackInt()); + } + } + + static class LongFieldEntry extends ReflectionFieldEntry { + LongFieldEntry(FieldEntry e) { + super(e); + } + public void pack(Object target, Packer pac) throws IOException { + pac.pack((long)(Long)target); + } + public void convert(Object target, MessagePackObject obj) throws MessageTypeException, IllegalAccessException { + getField().setLong(target, obj.asLong()); + } + public void unpack(Object target, Unpacker pac) throws IOException, MessageTypeException, IllegalAccessException { + getField().setLong(target, pac.unpackLong()); + } + } + + static class FloatFieldEntry extends ReflectionFieldEntry { + FloatFieldEntry(FieldEntry e) { + super(e); + } + public void pack(Object target, Packer pac) throws IOException { + pac.pack((float)(Float)target); + } + public void convert(Object target, MessagePackObject obj) throws MessageTypeException, IllegalAccessException { + getField().setFloat(target, obj.asFloat()); + } + public void unpack(Object target, Unpacker pac) throws IOException, MessageTypeException, IllegalAccessException { + getField().setFloat(target, pac.unpackFloat()); + } + } + + static class DoubleFieldEntry extends ReflectionFieldEntry { + DoubleFieldEntry(FieldEntry e) { + super(e); + } + public void pack(Object target, Packer pac) throws IOException { + pac.pack((double)(Double)target); + } + public void convert(Object target, MessagePackObject obj) throws MessageTypeException, IllegalAccessException { + getField().setDouble(target, obj.asDouble()); + } + public void unpack(Object target, Unpacker pac) throws IOException, MessageTypeException, IllegalAccessException { + getField().setDouble(target, pac.unpackDouble()); + } + } + + static class ReflectionTemplate extends AbstractTemplate { + protected Class targetClass; + protected ReflectionFieldEntry[] entries; + protected int minimumArrayLength; + + ReflectionTemplate(Class targetClass, ReflectionFieldEntry[] entries) { + this.targetClass = targetClass; + this.entries = entries; + this.minimumArrayLength = 0; + for(int i=0; i < entries.length; i++) { + ReflectionFieldEntry e = entries[i]; + if(e.isRequired() || e.isNullable()) { + this.minimumArrayLength = i+1; + } + } + } + + public void pack(Packer pk, Object target) throws IOException { + try { + pk.packArray(entries.length); + for(ReflectionFieldEntry e : entries) { + if(!e.isAvailable()) { + pk.packNil(); + continue; + } + Object obj = e.getField().get(target); + if(obj == null) { + if(!e.isNullable() && !e.isOptional()) { + throw new MessageTypeException(); + } + pk.packNil(); + } else { + e.pack(obj, pk); + } + } + + } catch (MessageTypeException e) { + throw e; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new MessageTypeException(e); + } + } + + public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException { + try { + if(to == null) { + to = targetClass.newInstance(); + } + + int length = pac.unpackArray(); + if(length < minimumArrayLength) { + throw new MessageTypeException(); + } + + int i; + for(i=0; i < minimumArrayLength; i++) { + ReflectionFieldEntry e = entries[i]; + if(!e.isAvailable()) { + pac.unpackObject(); + continue; + } + + if(pac.tryUnpackNull()) { + if(e.isRequired()) { + // Requred + nil => exception + throw new MessageTypeException(); + } else if(e.isOptional()) { + // Optional + nil => keep default value + } else { // Nullable + // Nullable + nil => set null + e.setNull(to); + } + } else { + e.unpack(to, pac); + } + } + + int max = length < entries.length ? length : entries.length; + for(; i < max; i++) { + ReflectionFieldEntry e = entries[i]; + if(!e.isAvailable()) { + pac.unpackObject(); + continue; + } + + if(pac.tryUnpackNull()) { + // this is Optional field becaue i >= minimumArrayLength + // Optional + nil => keep default value + } else { + e.unpack(to, pac); + } + } + + // latter entries are all Optional + nil => keep default value + + for(; i < length; i++) { + pac.unpackObject(); + } + + return to; + + } catch (MessageTypeException e) { + throw e; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new MessageTypeException(e); + } + } + + public Object convert(MessagePackObject from, Object to) throws MessageTypeException { + try { + if(to == null) { + to = targetClass.newInstance(); + } + + MessagePackObject[] array = from.asArray(); + int length = array.length; + if(length < minimumArrayLength) { + throw new MessageTypeException(); + } + + int i; + for(i=0; i < minimumArrayLength; i++) { + ReflectionFieldEntry e = entries[i]; + if(!e.isAvailable()) { + continue; + } + + MessagePackObject obj = array[i]; + if(obj.isNil()) { + if(e.isRequired()) { + // Requred + nil => exception + throw new MessageTypeException(); + } else if(e.isOptional()) { + // Optional + nil => keep default value + } else { // Nullable + // Nullable + nil => set null + e.setNull(to); + } + } else { + e.convert(to, obj); + } + } + + int max = length < entries.length ? length : entries.length; + for(; i < max; i++) { + ReflectionFieldEntry e = entries[i]; + if(!e.isAvailable()) { + continue; + } + + MessagePackObject obj = array[i]; + if(obj.isNil()) { + // this is Optional field becaue i >= minimumArrayLength + // Optional + nil => keep default value + } else { + e.convert(to, obj); + } + } + + // latter entries are all Optional + nil => keep default value + + return to; + + } catch (MessageTypeException e) { + throw e; + } catch (Exception e) { + throw new MessageTypeException(e); + } + } + } + + public Template buildTemplate(Class targetClass, FieldEntry[] entries) { + for(FieldEntry e : entries) { + Field f = e.getField(); + int mod = f.getModifiers(); + if(!Modifier.isPublic(mod)) { + f.setAccessible(true); + } + } + + ReflectionFieldEntry[] res = new ReflectionFieldEntry[entries.length]; + for(int i=0; i < entries.length; i++) { + FieldEntry e = entries[i]; + Class type = e.getType(); + if(type.equals(boolean.class)) { + res[i] = new BooleanFieldEntry(e); + } else if(type.equals(byte.class)) { + res[i] = new ByteFieldEntry(e); + } else if(type.equals(short.class)) { + res[i] = new ShortFieldEntry(e); + } else if(type.equals(int.class)) { + res[i] = new IntFieldEntry(e); + } else if(type.equals(long.class)) { + res[i] = new LongFieldEntry(e); + } else if(type.equals(float.class)) { + res[i] = new FloatFieldEntry(e); + } else if(type.equals(double.class)) { + res[i] = new DoubleFieldEntry(e); + } else { + Template tmpl = TemplateRegistry.lookup(e.getGenericType(), true); + res[i] = new ObjectFieldEntry(e, tmpl); + } + } + + return new ReflectionTemplate(targetClass, res); + } + + static class ReflectionOrdinalEnumTemplate extends AbstractTemplate { + protected Enum[] entries; + protected Map, Integer> reverse; + + ReflectionOrdinalEnumTemplate(Enum[] entries) { + this.entries = entries; + this.reverse = new HashMap, Integer>(); + for(int i=0; i < entries.length; i++) { + this.reverse.put(entries[i], i); + } + } + + public void pack(Packer pk, Object target) throws IOException { + Integer ord = reverse.get(target); + if(ord == null) { + throw new MessageTypeException(); + } + pk.pack((int)ord); + } + + public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException { + int ord = pac.unpackInt(); + if(entries.length <= ord) { + throw new MessageTypeException(); + } + return entries[ord]; + } + + public Object convert(MessagePackObject from, Object to) throws MessageTypeException { + int ord = from.asInt(); + if(entries.length <= ord) { + throw new MessageTypeException(); + } + return entries[ord]; + } + } + + public Template buildOrdinalEnumTemplate(Class targetClass, Enum[] entries) { + return new ReflectionOrdinalEnumTemplate(entries); + } +} + diff --git a/java/src/main/java/org/msgpack/template/ShortArrayTemplate.java b/java/src/main/java/org/msgpack/template/ShortArrayTemplate.java new file mode 100644 index 00000000..82fc4b6e --- /dev/null +++ b/java/src/main/java/org/msgpack/template/ShortArrayTemplate.java @@ -0,0 +1,76 @@ +// +// 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.template; + +import java.io.IOException; +import org.msgpack.*; + +public class ShortArrayTemplate implements Template { + private ShortArrayTemplate() { } + + public void pack(Packer pk, Object target) throws IOException { + if(!(target instanceof short[])) { + throw new MessageTypeException(); + } + short[] array = (short[])target; + pk.packArray(array.length); + for(short a : array) { + pk.pack(a); + } + } + + public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException { + int length = pac.unpackArray(); + short[] array; + if(to != null && to instanceof short[] && ((short[])to).length == length) { + array = (short[])to; + } else { + array = new short[length]; + } + for(int i=0; i < length; i++) { + array[i] = pac.unpackShort(); + } + return array; + } + + public Object convert(MessagePackObject from, Object to) throws MessageTypeException { + MessagePackObject[] src = from.asArray(); + short[] array; + if(to != null && to instanceof short[] && ((short[])to).length == src.length) { + array = (short[])to; + } else { + array = new short[src.length]; + } + for(int i=0; i < src.length; i++) { + MessagePackObject s = src[i]; + array[i] = s.asShort(); + } + return array; + } + + static public ShortArrayTemplate getInstance() { + return instance; + } + + static final ShortArrayTemplate instance = new ShortArrayTemplate(); + + static { + TemplateRegistry.register(short[].class, instance); + } +} + diff --git a/java/src/main/java/org/msgpack/template/ShortTemplate.java b/java/src/main/java/org/msgpack/template/ShortTemplate.java index 970ed78f..a65af081 100644 --- a/java/src/main/java/org/msgpack/template/ShortTemplate.java +++ b/java/src/main/java/org/msgpack/template/ShortTemplate.java @@ -43,6 +43,8 @@ public class ShortTemplate implements Template { static { CustomMessage.register(Short.class, instance); + TemplateRegistry.register(Short.class, instance); + TemplateRegistry.register(short.class, instance); } } diff --git a/java/src/main/java/org/msgpack/template/StringTemplate.java b/java/src/main/java/org/msgpack/template/StringTemplate.java index de5f7ee4..538fc4e2 100644 --- a/java/src/main/java/org/msgpack/template/StringTemplate.java +++ b/java/src/main/java/org/msgpack/template/StringTemplate.java @@ -43,6 +43,7 @@ public class StringTemplate implements Template { static { CustomMessage.register(String.class, instance); + TemplateRegistry.register(String.class, instance); } } diff --git a/java/src/main/java/org/msgpack/template/TemplateBuilder.java b/java/src/main/java/org/msgpack/template/TemplateBuilder.java new file mode 100644 index 00000000..47942678 --- /dev/null +++ b/java/src/main/java/org/msgpack/template/TemplateBuilder.java @@ -0,0 +1,307 @@ +// +// 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.template; + +import java.io.IOException; +import java.lang.reflect.*; +import java.lang.annotation.*; +import java.util.List; +import java.util.ArrayList; +import java.util.EnumSet; +import org.msgpack.*; +import org.msgpack.annotation.*; + +public abstract class TemplateBuilder { + public static class FieldEntry { + private Field field = null; + private FieldOption option = FieldOption.IGNORE; + + public FieldEntry() { + } + + public FieldEntry(Field field, FieldOption option) { + this.field = field; + this.option = option; + } + + public Field getField() { + return field; + } + + public String getName() { + return field.getName(); + } + + public Class getType() { + return field.getType(); + } + + public String getJavaTypeName() { + Class type = field.getType(); + if(type.isArray()) { + return arrayTypeToString(type); + } else { + return type.getName(); + } + } + + public Type getGenericType() { + return field.getGenericType(); + } + + public FieldOption getOption() { + return option; + } + + public boolean isAvailable() { + return option != FieldOption.IGNORE; + } + + public boolean isRequired() { + return option == FieldOption.REQUIRED; + } + + public boolean isOptional() { + return option == FieldOption.OPTIONAL; + } + + public boolean isNullable() { + return option == FieldOption.NULLABLE; + } + + public boolean isAnnotated(Class with) { + return field.getAnnotation(with) != null; + } + + static String arrayTypeToString(Class type) { + int dim = 1; + Class baseType = type.getComponentType(); + while(baseType.isArray()) { + baseType = baseType.getComponentType(); + dim += 1; + } + StringBuilder sb = new StringBuilder(); + sb.append(baseType.getName()); + for (int i = 0; i < dim; ++i) { + sb.append("[]"); + } + return sb.toString(); + } + } + + // Override this method + public abstract Template buildTemplate(Class targetClass, FieldEntry[] entries); + + // Override this method + public abstract Template buildOrdinalEnumTemplate(Class targetClass, Enum[] entries); + + + public Template buildTemplate(Class targetClass) { + return buildTemplate(targetClass, readFieldEntries(targetClass)); + } + + public Template buildTemplate(Class targetClass, FieldOption implicitOption) { + return buildTemplate(targetClass, readFieldEntries(targetClass, implicitOption)); + } + + public Template buildTemplate(Class targetClass, FieldList flist) throws NoSuchFieldException { + return buildTemplate(targetClass, convertFieldEntries(targetClass, flist)); + } + + public Template buildOrdinalEnumTemplate(Class targetClass) { + Enum[] entries = (Enum[])targetClass.getEnumConstants(); + return buildOrdinalEnumTemplate(targetClass, entries); + } + + + private static TemplateBuilder instance; + static { + // FIXME + instance = JavassistTemplateBuilder.getInstance(); + } + + public synchronized static void setTemplateBuilder(TemplateBuilder builder) { + instance = builder; + } + + public static Template build(Class targetClass) { + return instance.buildTemplate(targetClass); + } + + public static Template build(Class targetClass, FieldOption implicitOption) { + return instance.buildTemplate(targetClass, implicitOption); + } + + public static Template build(Class targetClass, FieldList flist) throws NoSuchFieldException { + return instance.buildTemplate(targetClass, flist); + } + + public static Template buildOrdinalEnum(Class targetClass) { + return instance.buildOrdinalEnumTemplate(targetClass); + } + + + protected FieldEntry[] convertFieldEntries(Class targetClass, FieldList flist) throws NoSuchFieldException { + List src = flist.getList(); + FieldEntry[] result = new FieldEntry[src.size()]; + for(int i=0; i < src.size(); i++) { + FieldList.Entry s = src.get(i); + if(s.isAvailable()) { + result[i] = new FieldEntry(targetClass.getDeclaredField(s.getName()), s.getOption()); + } else { + result[i] = new FieldEntry(); + } + } + return result; + } + + protected FieldEntry[] readFieldEntries(Class targetClass) { + FieldOption implicitOption = readImplicitFieldOption(targetClass); + return readFieldEntries(targetClass, implicitOption); + } + + protected FieldEntry[] readFieldEntries(Class targetClass, FieldOption implicitOption) { + Field[] allFields = readAllFields(targetClass); + + /* index: + * @Index(0) int a; // 0 + * int b; // 1 + * @Index(3) int c; // 3 + * int e; // 4 + * @Index(2) int d; // 2 + * int e; // 5 + */ + List indexed = new ArrayList(); + int maxIndex = -1; + for(Field f : allFields) { + FieldOption opt = readFieldOption(f, implicitOption); + if(opt == FieldOption.IGNORE) { + // skip + continue; + } + + int index = readFieldIndex(f, maxIndex); + + if(indexed.size() > index && indexed.get(index) != null) { + throw new RuntimeException("duplicated index: "+index); // FIXME exception + } + if(index < 0) { + throw new RuntimeException("invalid index: "+index); // FIXME exception + } + + while(indexed.size() <= index) { + indexed.add(null); + } + indexed.set(index, new FieldEntry(f, opt)); + + if(maxIndex < index) { + maxIndex = index; + } + } + + FieldEntry[] result = new FieldEntry[maxIndex+1]; + for(int i=0; i < indexed.size(); i++) { + FieldEntry e = indexed.get(i); + if(e == null) { + result[i] = new FieldEntry(); + } else { + result[i] = new FieldEntry(e.getField(), e.getOption()); + } + } + + return result; + } + + private Field[] readAllFields(Class targetClass) { + // order: [fields of super class, ..., fields of this class] + List succ = new ArrayList(); + int total = 0; + for(Class c = targetClass; c != Object.class; c = c.getSuperclass()) { + Field[] fields = c.getDeclaredFields(); + total += fields.length; + succ.add(fields); + } + Field[] result = new Field[total]; + int off = 0; + for(int i=succ.size()-1; i >= 0; i--) { + Field[] fields = succ.get(i); + System.arraycopy(fields, 0, result, off, fields.length); + off += fields.length; + } + return result; + } + + private FieldOption readImplicitFieldOption(Class targetClass) { + MessagePackMessage a = targetClass.getAnnotation(MessagePackMessage.class); + if(a == null) { + return FieldOption.DEFAULT; + } + return a.value(); + } + + private FieldOption readFieldOption(Field field, FieldOption implicitOption) { + int mod = field.getModifiers(); + if(Modifier.isStatic(mod) || Modifier.isFinal(mod)) { + return FieldOption.IGNORE; + } + + if(isAnnotated(field, Ignore.class)) { + return FieldOption.IGNORE; + } else if(isAnnotated(field, Required.class)) { + return FieldOption.REQUIRED; + } else if(isAnnotated(field, Optional.class)) { + return FieldOption.OPTIONAL; + } else if(isAnnotated(field, Nullable.class)) { + if(field.getDeclaringClass().isPrimitive()) { + return FieldOption.REQUIRED; + } else { + return FieldOption.NULLABLE; + } + } + + if(implicitOption != FieldOption.DEFAULT) { + return implicitOption; + } + + // default mode: + // transient : Ignore + // public : Required + // others : Ignore + if(Modifier.isTransient(mod)) { + return FieldOption.IGNORE; + } else if(Modifier.isPublic(mod)) { + return FieldOption.REQUIRED; + } else { + return FieldOption.IGNORE; + } + } + + private int readFieldIndex(Field field, int maxIndex) { + Index a = field.getAnnotation(Index.class); + if(a == null) { + return maxIndex + 1; + } else { + return a.value(); + } + } + + protected boolean isAnnotated(AccessibleObject ao, Class with) { + return ao.getAnnotation(with) != null; + } +} + diff --git a/java/src/main/java/org/msgpack/template/TemplateRegistry.java b/java/src/main/java/org/msgpack/template/TemplateRegistry.java new file mode 100644 index 00000000..cee8de05 --- /dev/null +++ b/java/src/main/java/org/msgpack/template/TemplateRegistry.java @@ -0,0 +1,212 @@ +// +// 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.template; + +import java.util.Map; +import java.util.HashMap; +import java.lang.reflect.Type; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.annotation.Annotation; +import org.msgpack.annotation.MessagePackMessage; +import org.msgpack.annotation.MessagePackDelegate; +import org.msgpack.annotation.MessagePackOrdinalEnum; +import org.msgpack.Template; +import org.msgpack.Templates; + +public class TemplateRegistry { + private static Map map; + private static Map genericMap; + + static { + map = new HashMap(); + genericMap = new HashMap(); + BuiltInTemplateLoader.load(); + } + + public static void register(Class target) { // auto-detect + if(target.isEnum()) { + register(target, TemplateBuilder.buildOrdinalEnum(target)); + } else { + register(target, TemplateBuilder.build(target)); + } + } + + public static void register(Class target, FieldOption implicitOption) { + register(target, TemplateBuilder.build(target, implicitOption)); + } + + public static void register(Class target, FieldList flist) throws NoSuchFieldException { + register(target, TemplateBuilder.build(target, flist)); + } + + public static synchronized void register(Type rawType, Template tmpl) { + if(rawType instanceof ParameterizedType) { + rawType = ((ParameterizedType)rawType).getRawType(); + } + map.put(rawType, tmpl); + } + + public static synchronized void registerGeneric(Type rawType, GenericTemplate gtmpl) { + if(rawType instanceof ParameterizedType) { + rawType = ((ParameterizedType)rawType).getRawType(); + } + genericMap.put(rawType, gtmpl); + } + + public static synchronized Template lookup(Type targetType) { + return lookupImpl(targetType, false, true); + } + + public static synchronized Template lookup(Type targetType, boolean forceBuild) { + return lookupImpl(targetType, forceBuild, true); + } + + public static synchronized Template tryLookup(Type targetType) { + return lookupImpl(targetType, false, false); + } + + public static synchronized Template tryLookup(Type targetType, boolean forceBuild) { + return lookupImpl(targetType, forceBuild, false); + } + + private static synchronized Template lookupImpl(Type targetType, boolean forceBuild, boolean fallbackDefault) { + Template tmpl; + Class target; + + // TODO + //if((Type)target instanceof GenericArrayType) { + // return lookupArrayImpl((GenericArrayType)(Type)target); + //} + + if(targetType instanceof ParameterizedType) { + tmpl = lookupGenericImpl((ParameterizedType)targetType); + if(tmpl != null) { + return tmpl; + } + target = (Class)((ParameterizedType)targetType).getRawType(); + } else { + target = (Class)targetType; + } + + tmpl = map.get(target); + if(tmpl != null) { + return tmpl; + } + + if(isAnnotated(target, MessagePackMessage.class)) { + tmpl = TemplateBuilder.build(target); + register(target, tmpl); + return tmpl; + } else if(isAnnotated(target, MessagePackDelegate.class)) { + // TODO DelegateTemplate + throw new UnsupportedOperationException("not supported yet. : " + target.getName()); + } else if(isAnnotated(target, MessagePackOrdinalEnum.class)) { + tmpl = TemplateBuilder.buildOrdinalEnum(target); + register(target, tmpl); + return tmpl; + } + + for(Class i : target.getInterfaces()) { + tmpl = map.get(i); + if(tmpl != null) { + map.put(target, tmpl); + return tmpl; + } + } + + Class c = target.getSuperclass(); + if(c != null) { + for(; c != Object.class; c = c.getSuperclass()) { + tmpl = map.get(c); + if(tmpl != null) { + map.put(target, tmpl); + return tmpl; + } + } + + if(forceBuild) { + tmpl = TemplateBuilder.build(target); + register(target, tmpl); + return tmpl; + } + } + + if(fallbackDefault) { + tmpl = new DefaultTemplate((Class)target); + register(target, tmpl); + return tmpl; + } else { + return null; + } + } + + public static synchronized Template lookupGeneric(Type targetType) { + if(targetType instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType)targetType; + Template tmpl = lookupGenericImpl(parameterizedType); + if(tmpl != null) { + return tmpl; + } + return new DefaultTemplate((Class)parameterizedType.getRawType(), parameterizedType); + } else { + throw new IllegalArgumentException("actual types of the generic type are erased: "+targetType); + } + } + + private static synchronized Template lookupGenericImpl(ParameterizedType type) { + Type rawType = type.getRawType(); + GenericTemplate gtmpl = genericMap.get(rawType); + if(gtmpl == null) { + return null; + } + + Type[] types = type.getActualTypeArguments(); + Template[] tmpls = new Template[types.length]; + for(int i=0; i < types.length; i++) { + tmpls[i] = lookup(types[i]); + } + + return gtmpl.build(tmpls); + } + + public static synchronized Template lookupArray(Type targetType) { + if(targetType instanceof GenericArrayType) { + GenericArrayType arrayType = (GenericArrayType)targetType; + return lookupArrayImpl(arrayType); + } else { + throw new IllegalArgumentException("actual type of the array type is erased: "+targetType); + } + } + + private static synchronized Template lookupArrayImpl(GenericArrayType arrayType) { + Template tmpl = map.get(arrayType); + if(tmpl != null) { + // TODO primitive types are included? + return tmpl; + } + Type component = arrayType.getGenericComponentType(); + Template componentTemplate = lookup(component); + return new ObjectArrayTemplate(componentTemplate); + } + + private static boolean isAnnotated(Class ao, Class with) { + return ao.getAnnotation(with) != null; + } +} + diff --git a/java/src/main/java/org/msgpack/type/Raw.java b/java/src/main/java/org/msgpack/type/Raw.java new file mode 100644 index 00000000..cdd0c220 --- /dev/null +++ b/java/src/main/java/org/msgpack/type/Raw.java @@ -0,0 +1,69 @@ +// +// 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.type; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import org.msgpack.*; + +public final class Raw { + private byte[] bytes; + private String string; + + public Raw(byte[] bytes) { + this.bytes = bytes; + this.string = null; + } + + public Raw(String string) { + this.bytes = null; + this.string = string; + } + + public String toString() { + if(string == null) { + try { + string = new String(bytes, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + return string; + } + + public byte[] toByteArray() { + if(bytes == null) { + try { + bytes = string.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + return bytes; + } + + public ByteBuffer toByteBuffer() { + return ByteBuffer.wrap(toByteArray()); + } + + static { + RawTemplate.load(); + } +} + diff --git a/java/src/main/java/org/msgpack/type/RawTemplate.java b/java/src/main/java/org/msgpack/type/RawTemplate.java new file mode 100644 index 00000000..85fb5e46 --- /dev/null +++ b/java/src/main/java/org/msgpack/type/RawTemplate.java @@ -0,0 +1,51 @@ +// +// 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.type; + +import java.io.IOException; +import org.msgpack.*; +import org.msgpack.template.TemplateRegistry; + +public class RawTemplate implements Template { + static void load() { } + + private RawTemplate() { } + + public void pack(Packer pk, Object target) throws IOException { + pk.packByteArray(((Raw)target).toByteArray()); + } + + public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException { + return new Raw(pac.unpackByteArray()); + } + + public Object convert(MessagePackObject from, Object to) throws MessageTypeException { + return new Raw(from.asByteArray()); + } + + static public RawTemplate getInstance() { + return instance; + } + + static final RawTemplate instance = new RawTemplate(); + + static { + TemplateRegistry.register(Raw.class, instance); + } +} + diff --git a/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenBase.java b/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenBase.java index ac5fb50d..f2088ffa 100644 --- a/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenBase.java +++ b/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenBase.java @@ -333,6 +333,8 @@ public class DynamicCodeGenBase implements Constants { return Templates.tString(); } else if (c.equals(BigInteger.class)) { return Templates.tBigInteger(); + } else if (c.equals(byte[].class)) { + return Templates.tByteArray(); } else if (c.equals(ByteBuffer.class)) { return Templates.tByteBuffer(); } else if (CustomConverter.isRegistered(c)) {// FIXME diff --git a/java/src/test/java/org/msgpack/buffer/VectoredByteBufferTest.java b/java/src/test/java/org/msgpack/buffer/VectoredByteBufferTest.java new file mode 100644 index 00000000..5c9c8228 --- /dev/null +++ b/java/src/test/java/org/msgpack/buffer/VectoredByteBufferTest.java @@ -0,0 +1,45 @@ +package org.msgpack.buffer; + +import org.msgpack.*; +import org.msgpack.object.*; +import java.io.*; +import java.util.*; +import java.util.concurrent.*; +import java.net.*; +import junit.framework.*; +import org.junit.Test; + +public class VectoredByteBufferTest extends TestCase { + public VectoredByteBufferTest() { + } + + @Test + public void testIO() throws Exception { + VectoredByteBuffer v = new VectoredByteBuffer(); + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + byte[] ref = new byte[40]; + byte[] copy = new byte[3]; + ref[0] = 10; + ref[1] = 20; + ref[2] = 30; + copy[0] = 40; + copy[1] = 50; + copy[2] = 60; + + byte[][] src = new byte[][] { + copy, ref, ref, copy, ref, copy, copy, ref + }; + + for(byte[] s : src) { + bo.write(s); + v.write(s); + } + + ByteArrayOutputStream check = new ByteArrayOutputStream(); + v.writeTo(check); + + assertEquals(bo.size(), check.size()); + assertTrue(Arrays.equals(bo.toByteArray(), check.toByteArray())); + } +} + diff --git a/java/src/test/java/org/msgpack/template/TestTemplateBuilder.java b/java/src/test/java/org/msgpack/template/TestTemplateBuilder.java new file mode 100644 index 00000000..70e9bda5 --- /dev/null +++ b/java/src/test/java/org/msgpack/template/TestTemplateBuilder.java @@ -0,0 +1,218 @@ +package org.msgpack.template; + +import java.util.*; +import java.io.*; +import java.math.*; +import org.msgpack.*; +import org.msgpack.annotation.*; + +import org.junit.Test; +import junit.framework.TestCase; + +public class TestTemplateBuilder extends TestCase { + public static class PrimitiveTypeFieldsClass { + public byte f0; + public short f1; + public int f2; + public long f3; + public float f4; + public double f5; + public boolean f6; + + public PrimitiveTypeFieldsClass() { + } + } + + public static class GeneralReferenceTypeFieldsClass { + public Byte f0; + public Short f1; + public Integer f2; + public Long f3; + public Float f4; + public Double f5; + public Boolean f6; + public BigInteger f7; + public String f8; + public byte[] f9; + + public GeneralReferenceTypeFieldsClass() { + } + } + + public static class SampleListTypes { + public List f0; + public List f1; + public List f2; + public List> f3; + public List f4; + + public SampleListTypes() { + } + } + + @MessagePackMessage + public static class SampleListNestedType { + public byte[] f0; + public String f1; + + public SampleListNestedType() { + } + } + + public static class SampleMapTypes { + public Map f0; + public Map f1; + public Map f2; + + public SampleMapTypes() { + } + } + + static void buildAndRegisterTemplate(Class targetClass) { + MessagePack.register(targetClass, + TemplateBuilder.build(targetClass)); + } + + static { + buildAndRegisterTemplate(PrimitiveTypeFieldsClass.class); + buildAndRegisterTemplate(GeneralReferenceTypeFieldsClass.class); + buildAndRegisterTemplate(SampleListNestedType.class); + buildAndRegisterTemplate(SampleListTypes.class); + buildAndRegisterTemplate(SampleMapTypes.class); + } + + @Test + public void testPrimitiveTypeFieldsClass00() throws Exception { + PrimitiveTypeFieldsClass src = new PrimitiveTypeFieldsClass(); + src.f0 = (byte) 0; + src.f1 = 1; + src.f2 = 2; + src.f3 = 3; + src.f4 = 4; + src.f5 = 5; + src.f6 = false; + + byte[] raw = MessagePack.pack(src); + + PrimitiveTypeFieldsClass dstu = + MessagePack.unpack(raw, PrimitiveTypeFieldsClass.class); + assertEquals(src.f0, dstu.f0); + assertEquals(src.f1, dstu.f1); + assertEquals(src.f2, dstu.f2); + assertEquals(src.f3, dstu.f3); + assertEquals(src.f4, dstu.f4); + assertEquals(src.f5, dstu.f5); + assertEquals(src.f6, dstu.f6); + + MessagePackObject o = MessagePack.unpack(raw); + PrimitiveTypeFieldsClass dsto = + o.convert(PrimitiveTypeFieldsClass.class); + + assertEquals(src.f0, dsto.f0); + assertEquals(src.f1, dsto.f1); + assertEquals(src.f2, dsto.f2); + assertEquals(src.f3, dsto.f3); + assertEquals(src.f4, dsto.f4); + assertEquals(src.f5, dsto.f5); + assertEquals(src.f6, dsto.f6); + } + + public void testGeneralReferenceTypeFieldsClass() throws Exception { + GeneralReferenceTypeFieldsClass src = new GeneralReferenceTypeFieldsClass(); + src.f0 = 0; + src.f1 = 1; + src.f2 = 2; + src.f3 = (long) 3; + src.f4 = (float) 4; + src.f5 = (double) 5; + src.f6 = false; + src.f7 = new BigInteger("7"); + src.f8 = "8"; + src.f9 = new byte[] { 0x01, 0x02 }; + + byte[] raw = MessagePack.pack(src); + + GeneralReferenceTypeFieldsClass dstu = + MessagePack.unpack(raw, GeneralReferenceTypeFieldsClass.class); + assertEquals(src.f0, dstu.f0); + assertEquals(src.f1, dstu.f1); + assertEquals(src.f2, dstu.f2); + assertEquals(src.f3, dstu.f3); + assertEquals(src.f4, dstu.f4); + assertEquals(src.f5, dstu.f5); + assertEquals(src.f6, dstu.f6); + assertEquals(src.f7, dstu.f7); + assertEquals(src.f8, dstu.f8); + assertEquals(src.f9[0], dstu.f9[0]); + assertEquals(src.f9[1], dstu.f9[1]); + + MessagePackObject o = MessagePack.unpack(raw); + GeneralReferenceTypeFieldsClass dsto = + o.convert(GeneralReferenceTypeFieldsClass.class); + assertEquals(src.f0, dsto.f0); + assertEquals(src.f1, dsto.f1); + assertEquals(src.f2, dsto.f2); + assertEquals(src.f3, dsto.f3); + assertEquals(src.f4, dsto.f4); + assertEquals(src.f5, dsto.f5); + assertEquals(src.f6, dsto.f6); + assertEquals(src.f7, dsto.f7); + assertEquals(src.f8, dsto.f8); + assertEquals(src.f9[0], dsto.f9[0]); + assertEquals(src.f9[1], dsto.f9[1]); + } + + @Test + public void testListTypes() throws Exception { + SampleListTypes src = new SampleListTypes(); + src.f0 = new ArrayList(); + src.f1 = new ArrayList(); + src.f1.add(1); + src.f1.add(2); + src.f1.add(3); + src.f2 = new ArrayList(); + src.f2.add("e1"); + src.f2.add("e2"); + src.f2.add("e3"); + src.f3 = new ArrayList>(); + src.f3.add(src.f2); + src.f4 = new ArrayList(); + SampleListNestedType slnt = new SampleListNestedType(); + slnt.f0 = new byte[] { 0x01, 0x02 }; + slnt.f1 = "muga"; + src.f4.add(slnt); + + byte[] raw = MessagePack.pack(src); + + SampleListTypes dstu = + MessagePack.unpack(raw, SampleListTypes.class); + assertEquals(src.f0.size(), dstu.f0.size()); + assertEquals(src.f1.size(), dstu.f1.size()); + for (int i = 0; i < src.f1.size(); ++i) { + assertEquals(src.f1.get(i), dstu.f1.get(i)); + } + assertEquals(src.f2.size(), dstu.f2.size()); + for (int i = 0; i < src.f2.size(); ++i) { + assertEquals(src.f2.get(i), dstu.f2.get(i)); + } + assertEquals(src.f3.size(), dstu.f3.size()); + for (int i = 0; i < src.f3.size(); ++i) { + List srclist = src.f3.get(i); + List dstlist = dstu.f3.get(i); + assertEquals(srclist.size(), dstlist.size()); + for (int j = 0; j < srclist.size(); ++j) { + assertEquals(srclist.get(j), dstlist.get(j)); + } + } + assertEquals(src.f4.size(), dstu.f4.size()); + for (int i = 0; i < src.f4.size(); ++i) { + SampleListNestedType s = src.f4.get(i); + SampleListNestedType d = dstu.f4.get(i); + assertEquals(s.f0[0], d.f0[0]); + assertEquals(s.f0[1], d.f0[1]); + assertEquals(s.f1, d.f1); + } + } + +} + diff --git a/msgpack/sysdep.h b/msgpack/sysdep.h index 2bc01c9f..8b4ead7a 100644 --- a/msgpack/sysdep.h +++ b/msgpack/sysdep.h @@ -18,8 +18,9 @@ #ifndef MSGPACK_SYSDEP_H__ #define MSGPACK_SYSDEP_H__ - -#ifdef _MSC_VER +#include +#include +#if defined(_MSC_VER) && _MSC_VER < 1600 typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; @@ -28,8 +29,9 @@ typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; +#elif defined(_MSC_VER) // && _MSC_VER >= 1600 +#include #else -#include #include #include #endif @@ -76,7 +78,7 @@ typedef unsigned int _msgpack_atomic_counter_t; #define _msgpack_be16(x) ntohs(x) #define _msgpack_be32(x) ntohl(x) -#if defined(_byteswap_uint64) +#if defined(_byteswap_uint64) || _MSC_VER >= 1400 # define _msgpack_be64(x) (_byteswap_uint64(x)) #elif defined(bswap_64) # define _msgpack_be64(x) bswap_64(x) diff --git a/ruby/makegem.sh b/ruby/makegem.sh index bf30cd47..6a99cfe9 100755 --- a/ruby/makegem.sh +++ b/ruby/makegem.sh @@ -8,6 +8,7 @@ cp pack.h ext/ cp rbinit.c ext/ cp unpack.c ext/ cp unpack.h ext/ +cp compat.h ext/ cp version.rb ext/ cp ../msgpack/pack_define.h msgpack/ cp ../msgpack/pack_template.h msgpack/ diff --git a/ruby/test/test_pack_unpack.rb b/ruby/test/test_pack_unpack.rb index f378c3c7..8bdb3628 100644 --- a/ruby/test/test_pack_unpack.rb +++ b/ruby/test/test_pack_unpack.rb @@ -227,8 +227,9 @@ class MessagePackTestPackUnpack < Test::Unit::TestCase assert(fe.length > 0) off += fe.length - pac.feed fe - pac.each {|obj| + #pac.feed fe + #pac.each {|obj| + pac.feed_each(fe) {|obj| assert(!parsed) assert_equal(obj, str) parsed = true @@ -245,8 +246,8 @@ class MessagePackTestPackUnpack < Test::Unit::TestCase pac = MessagePack::Unpacker.new parsed = 0 raw.split(//).each do |b| - pac.feed(b) - pac.each {|o| + #pac.feed(b) + pac.feed_each(b) {|o| GC.start assert_equal(obj, o) parsed += 1 diff --git a/ruby/unpack.c b/ruby/unpack.c index 2d10e75d..f61fe6db 100644 --- a/ruby/unpack.c +++ b/ruby/unpack.c @@ -24,7 +24,7 @@ static ID s_sysread; static ID s_readpartial; struct unpack_buffer { - size_t size; + size_t used; size_t free; char* ptr; }; @@ -37,6 +37,7 @@ typedef struct { VALUE stream; VALUE streambuf; ID stream_append_method; + size_t buffer_free_size; } unpack_user; @@ -241,6 +242,13 @@ static VALUE eUnpackError; #define MSGPACK_UNPACKER_BUFFER_RESERVE_SIZE (8*1024) #endif +/* +#ifndef MSGPACK_BUFFER_FREE_SIZE +#define MSGPACK_BUFFER_FREE_SIZE (1024*1024) +#endif +*/ +#define MSGPACK_BUFFER_FREE_SIZE 0 + static void MessagePack_Unpacker_free(void* data) { if(data) { @@ -273,7 +281,7 @@ static VALUE MessagePack_Unpacker_alloc(VALUE klass) mp->user.finished = 0; mp->user.offset = 0; - mp->user.buffer.size = 0; + mp->user.buffer.used = 0; mp->user.buffer.free = 0; mp->user.buffer.ptr = NULL; @@ -326,6 +334,7 @@ static VALUE MessagePack_Unpacker_initialize(int argc, VALUE *argv, VALUE self) mp->user.stream = stream; mp->user.streambuf = rb_str_buf_new(MSGPACK_UNPACKER_BUFFER_RESERVE_SIZE); mp->user.stream_append_method = append_method_of(stream); + mp->user.buffer_free_size = MSGPACK_BUFFER_FREE_SIZE; return self; } @@ -366,22 +375,27 @@ static void reserve_buffer(msgpack_unpack_t* mp, size_t require) { struct unpack_buffer* buffer = &mp->user.buffer; - if(buffer->size == 0) { - size_t nsize = MSGPACK_UNPACKER_BUFFER_INIT_SIZE; + if(buffer->used == 0) { + if(require <= buffer->free) { + /* enough free space */ + return; + } + /* no used buffer: realloc only */ + size_t nsize = buffer->free == 0 ? + MSGPACK_UNPACKER_BUFFER_INIT_SIZE : buffer->free*2; while(nsize < require) { nsize *= 2; } - char* tmp = ALLOC_N(char, nsize); - buffer->ptr = tmp; + char* tmp = REALLOC_N(buffer->ptr, char, nsize); buffer->free = nsize; - buffer->size = 0; + buffer->ptr = tmp; return; } - if(buffer->size <= mp->user.offset) { + if(buffer->used <= mp->user.offset) { /* clear buffer and rewind offset */ - buffer->free += buffer->size; - buffer->size = 0; + buffer->free += buffer->used; + buffer->used = 0; mp->user.offset = 0; } @@ -390,41 +404,91 @@ static void reserve_buffer(msgpack_unpack_t* mp, size_t require) return; } - size_t nsize = (buffer->size + buffer->free) * 2; + size_t nsize = (buffer->used + buffer->free) * 2; - if(mp->user.offset <= buffer->size / 2) { + if(mp->user.offset <= buffer->used / 2) { /* parsed less than half: realloc only */ - while(nsize < buffer->size + require) { + while(nsize < buffer->used + require) { nsize *= 2; } char* tmp = REALLOC_N(buffer->ptr, char, nsize); - buffer->free = nsize - buffer->size; + buffer->free = nsize - buffer->used; buffer->ptr = tmp; } else { /* parsed more than half: realloc and move */ - size_t not_parsed = buffer->size - mp->user.offset; + size_t not_parsed = buffer->used - mp->user.offset; while(nsize < not_parsed + require) { nsize *= 2; } char* tmp = REALLOC_N(buffer->ptr, char, nsize); memcpy(tmp, tmp + mp->user.offset, not_parsed); - buffer->free = nsize - buffer->size; - buffer->size = not_parsed; + buffer->free = nsize - not_parsed; + buffer->used = not_parsed; buffer->ptr = tmp; mp->user.offset = 0; } } -static inline void feed_buffer(msgpack_unpack_t* mp, const char* ptr, size_t len) +static inline void try_free_buffer(msgpack_unpack_t* mp, size_t require) +{ + if(mp->user.buffer_free_size == 0) { + return; + } + + struct unpack_buffer* buffer = &mp->user.buffer; + size_t csize = buffer->used + buffer->free; + + if(csize <= mp->user.buffer_free_size) { + return; + } + + if(mp->user.offset <= buffer->used / 2) { + /* parsed less than half: do nothing */ + + } else if(mp->user.offset < buffer->used) { + /* parsed more than half but not all: realloc and move */ + size_t nsize = MSGPACK_UNPACKER_BUFFER_INIT_SIZE; + size_t not_parsed = buffer->used - mp->user.offset; + while(nsize < not_parsed + require) { + nsize *= 2; + } + + if(nsize >= csize) { + return; + } + + char* tmp; + if(mp->user.offset == 0) { + tmp = ALLOC_N(char, nsize); + memcpy(tmp, buffer->ptr + mp->user.offset, not_parsed); + free(buffer->ptr); + } else { + tmp = REALLOC_N(buffer->ptr, char, nsize); + } + buffer->free = nsize - not_parsed; + buffer->used = not_parsed; + buffer->ptr = tmp; + mp->user.offset = 0; + + } else { + /* all parsed: free all */ + free(buffer->ptr); + buffer->free = 0; + buffer->used = 0; + buffer->ptr = NULL; + mp->user.offset = 0; + } +} + +static void feed_buffer(msgpack_unpack_t* mp, const char* ptr, size_t len) { struct unpack_buffer* buffer = &mp->user.buffer; - if(buffer->free < len) { - reserve_buffer(mp, len); - } - memcpy(buffer->ptr + buffer->size, ptr, len); - buffer->size += len; + reserve_buffer(mp, len); + + memcpy(buffer->ptr + buffer->used, ptr, len); + buffer->used += len; buffer->free -= len; } @@ -498,7 +562,7 @@ static VALUE MessagePack_Unpacker_each(VALUE self) #endif while(1) { - if(mp->user.buffer.size <= mp->user.offset) { + if(mp->user.buffer.used <= mp->user.offset) { do_fill: { VALUE len = MessagePack_Unpacker_fill(self); @@ -509,7 +573,7 @@ static VALUE MessagePack_Unpacker_each(VALUE self) } ret = template_execute_wrap_each(mp, - mp->user.buffer.ptr, mp->user.buffer.size, + mp->user.buffer.ptr, mp->user.buffer.used, &mp->user.offset); if(ret < 0) { @@ -525,9 +589,131 @@ static VALUE MessagePack_Unpacker_each(VALUE self) } } + try_free_buffer(mp, 0); + return Qnil; } +static VALUE feed_each_impl(VALUE args) +{ + VALUE self = ((VALUE*)args)[0]; + VALUE data = ((VALUE*)args)[1]; + size_t* pconsumed = (size_t*)((VALUE*)args)[2]; + + UNPACKER(self, mp); + int ret; + const char* ptr = RSTRING_PTR(data); + size_t len = RSTRING_LEN(data); + + if(mp->user.buffer.used > 0) { + while(1) { + ret = template_execute_wrap_each(mp, + mp->user.buffer.ptr, mp->user.buffer.used, + &mp->user.offset); + + if(ret < 0) { + rb_raise(eUnpackError, "parse error."); + + } else if(ret > 0) { + VALUE data = template_data(mp); + template_init(mp); + rb_yield(data); + + } else { + break; + } + } + } + + if(len <= 0) { + return Qnil; + } + + if(mp->user.buffer.used <= mp->user.offset) { + // wrap & execute & feed + while(1) { + ret = template_execute_wrap_each(mp, + ptr, len, pconsumed); + + if(ret < 0) { + rb_raise(eUnpackError, "parse error."); + + } else if(ret > 0) { + VALUE data = template_data(mp); + template_init(mp); + rb_yield(data); + + } else { + break; + } + } + + } else { + // feed & execute + feed_buffer(mp, ptr, len); + *pconsumed = len; + + while(1) { + ret = template_execute_wrap_each(mp, + mp->user.buffer.ptr, mp->user.buffer.used, + &mp->user.offset); + + if(ret < 0) { + rb_raise(eUnpackError, "parse error."); + + } else if(ret > 0) { + VALUE data = template_data(mp); + template_init(mp); + rb_yield(data); + + } else { + break; + } + } + } + + return Qnil; +} + +static VALUE feed_each_ensure(VALUE args) { + VALUE self = ((VALUE*)args)[0]; + VALUE data = ((VALUE*)args)[1]; + size_t* pconsumed = (size_t*)((VALUE*)args)[2]; + + const char* dptr = RSTRING_PTR(data) + *pconsumed; + size_t dlen = RSTRING_LEN(data) - *pconsumed; + + if(dlen > 0) { + UNPACKER(self, mp); + try_free_buffer(mp, dlen); + feed_buffer(mp, dptr, dlen); + } + + return Qnil; +} + +/** + * Document-method: MessagePack::Unpacker#feed_each + * + * call-seq: + * unpacker.feed_each(data) {|object| } + * + * Same as feed(data) + each {|object| }, but tries to avoid copying of the buffer. + */ +static VALUE MessagePack_Unpacker_feed_each(VALUE self, VALUE data) +{ + size_t consumed = 0; + StringValue(data); + + VALUE args[3]; + args[0] = self; + args[1] = data; + args[2] = (VALUE)&consumed; + + return rb_ensure(feed_each_impl, (VALUE)args, + feed_each_ensure, (VALUE)args); +} + static inline VALUE MessagePack_unpack_impl(VALUE self, VALUE data, unsigned long dlen) { @@ -620,7 +806,7 @@ static VALUE MessagePack_Unpacker_execute_impl(VALUE self, VALUE data, * * This method doesn't use the internal buffer. * - * Call *reset()* method before calling this method again. + * Call *reset* method before calling this method again. * * UnpackError is throw when parse error is occured. */ @@ -642,7 +828,7 @@ static VALUE MessagePack_Unpacker_execute_limit(VALUE self, VALUE data, * * This method doesn't use the internal buffer. * - * Call *reset()* method before calling this method again. + * Call *reset* method before calling this method again. * * This returns offset that was parsed to. * Use *finished?* method to check an object is deserialized and call *data* @@ -705,6 +891,7 @@ static VALUE MessagePack_Unpacker_reset(VALUE self) UNPACKER(self, mp); template_init(mp); mp->user.finished = 0; + try_free_buffer(mp, 0); return self; } @@ -726,6 +913,7 @@ void Init_msgpack_unpack(VALUE mMessagePack) rb_define_method(cUnpacker, "each", MessagePack_Unpacker_each, 0); rb_define_method(cUnpacker, "stream", MessagePack_Unpacker_stream_get, 0); rb_define_method(cUnpacker, "stream=", MessagePack_Unpacker_stream_set, 1); + rb_define_method(cUnpacker, "feed_each", MessagePack_Unpacker_feed_each, 1); /* Unbuffered API */ rb_define_method(cUnpacker, "execute", MessagePack_Unpacker_execute, 2); diff --git a/ruby/version.rb b/ruby/version.rb index b1566203..4fca81ff 100644 --- a/ruby/version.rb +++ b/ruby/version.rb @@ -1,3 +1,3 @@ module MessagePack - VERSION = "0.4.3" + VERSION = "0.4.4" end