From f55e1d1f4890742caddd23166b51779360459ddc Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Mon, 7 Sep 2015 21:20:51 +0900 Subject: [PATCH 01/39] Adding full spec ext type implementation for JRuby --- ext/java/org/msgpack/jruby/Decoder.java | 37 ++++- ext/java/org/msgpack/jruby/Encoder.java | 37 ++++- ext/java/org/msgpack/jruby/Factory.java | 126 ++++++++++++++++++ .../org/msgpack/jruby/MessagePackLibrary.java | 13 +- ext/java/org/msgpack/jruby/Packer.java | 74 ++++++++++ ext/java/org/msgpack/jruby/Unpacker.java | 63 ++++++++- 6 files changed, 333 insertions(+), 17 deletions(-) create mode 100644 ext/java/org/msgpack/jruby/Factory.java diff --git a/ext/java/org/msgpack/jruby/Decoder.java b/ext/java/org/msgpack/jruby/Decoder.java index 8cb625b4..6d7a2720 100644 --- a/ext/java/org/msgpack/jruby/Decoder.java +++ b/ext/java/org/msgpack/jruby/Decoder.java @@ -12,6 +12,7 @@ import org.jruby.RubyClass; import org.jruby.RubyBignum; import org.jruby.RubyString; +import org.jruby.RubyArray; import org.jruby.RubyHash; import org.jruby.exceptions.RaiseException; import org.jruby.runtime.builtin.IRubyObject; @@ -31,19 +32,30 @@ public class Decoder implements Iterator { private final RubyClass underflowErrorClass; private final RubyClass unexpectedTypeErrorClass; + private Unpacker.ExtRegistry registry; private ByteBuffer buffer; private boolean symbolizeKeys; + private boolean allowUnknownExt; public Decoder(Ruby runtime) { - this(runtime, new byte[] {}, 0, 0); + this(runtime, null, new byte[] {}, 0, 0); + } + + public Decoder(Ruby runtime, Unpacker.ExtRegistry registry) { + this(runtime, registry, new byte[] {}, 0, 0); } public Decoder(Ruby runtime, byte[] bytes) { - this(runtime, bytes, 0, bytes.length); + this(runtime, null, bytes, 0, bytes.length); + } + + public Decoder(Ruby runtime, Unpacker.ExtRegistry registry, byte[] bytes) { + this(runtime, registry, bytes, 0, bytes.length); } - public Decoder(Ruby runtime, byte[] bytes, int offset, int length) { + public Decoder(Ruby runtime, Unpacker.ExtRegistry registry, byte[] bytes, int offset, int length) { this.runtime = runtime; + this.registry = registry; this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding(); this.utf8Encoding = UTF8Encoding.INSTANCE; this.unpackErrorClass = runtime.getModule("MessagePack").getClass("UnpackError"); @@ -56,6 +68,10 @@ public void symbolizeKeys(boolean symbolize) { this.symbolizeKeys = symbolize; } + public void allowUnknownExt(boolean allow) { + this.allowUnknownExt = allow; + } + public void feed(byte[] bytes) { feed(bytes, 0, bytes.length); } @@ -118,7 +134,20 @@ private IRubyObject consumeHash(int size) { private IRubyObject consumeExtension(int size) { int type = buffer.get(); byte[] payload = readBytes(size); - return ExtensionValue.newExtensionValue(runtime, type, payload); + + if (this.registry != null) { + IRubyObject proc = this.registry.lookup(type); + if (proc != null) { + ByteList byteList = new ByteList(payload, runtime.getEncodingService().getAscii8bitEncoding()); + return proc.callMethod(runtime.getCurrentContext(), "call", runtime.newString(byteList)); + } + } + + if (this.allowUnknownExt) { + return ExtensionValue.newExtensionValue(runtime, type, payload); + } + + throw runtime.newRaiseException(unexpectedTypeErrorClass, "unexpected type"); } private byte[] readBytes(int size) { diff --git a/ext/java/org/msgpack/jruby/Encoder.java b/ext/java/org/msgpack/jruby/Encoder.java index 0df9673c..a8c47cc2 100644 --- a/ext/java/org/msgpack/jruby/Encoder.java +++ b/ext/java/org/msgpack/jruby/Encoder.java @@ -35,6 +35,7 @@ public class Encoder { private final Encoding utf8Encoding; private final boolean compatibilityMode; + private Packer.ExtRegistry registry; private ByteBuffer buffer; public Encoder(Ruby runtime, boolean compatibilityMode) { @@ -45,6 +46,10 @@ public Encoder(Ruby runtime, boolean compatibilityMode) { this.compatibilityMode = compatibilityMode; } + public void setRegistry(Packer.ExtRegistry registry) { + this.registry = registry; + } + private void ensureRemainingCapacity(int c) { if (buffer.remaining() < c) { int newLength = Math.max(buffer.capacity() + (buffer.capacity() >> 1), buffer.capacity() + c); @@ -107,7 +112,7 @@ private void appendObject(IRubyObject object, IRubyObject destination) { } else if (object instanceof ExtensionValue) { appendExtensionValue((ExtensionValue) object); } else { - appendCustom(object, destination); + appendOther(object, destination); } } @@ -295,12 +300,7 @@ public void visit(IRubyObject key, IRubyObject value) { } } - private void appendExtensionValue(ExtensionValue object) { - long type = ((RubyFixnum)object.get_type()).getLongValue(); - if (type < -128 || type > 127) { - throw object.getRuntime().newRangeError(String.format("integer %d too big to convert to `signed char'", type)); - } - ByteList payloadBytes = ((RubyString)object.payload()).getByteList(); + private void appendExt(int type, ByteList payloadBytes) { int payloadSize = payloadBytes.length(); int outputSize = 0; boolean fixSize = payloadSize == 1 || payloadSize == 2 || payloadSize == 4 || payloadSize == 8 || payloadSize == 16; @@ -338,6 +338,29 @@ private void appendExtensionValue(ExtensionValue object) { buffer.put(payloadBytes.unsafeBytes(), payloadBytes.begin(), payloadSize); } + private void appendExtensionValue(ExtensionValue object) { + int type = (int) ((RubyFixnum)object.get_type()).getLongValue(); + if (type < -128 || type > 127) { + throw object.getRuntime().newRangeError(String.format("integer %d too big to convert to `signed char'", type)); + } + ByteList payloadBytes = ((RubyString)object.payload()).getByteList(); + appendExt(type, payloadBytes); + } + + private void appendOther(IRubyObject object, IRubyObject destination) { + if (this.registry != null) { + IRubyObject[] pair = this.registry.lookup(object.getType()); + if (pair != null) { + RubyString bytes = pair[0].callMethod(runtime.getCurrentContext(), "call", object).asString(); + int type = (int) ((RubyFixnum) pair[1]).getLongValue(); + appendExt(type, bytes.getByteList()); + return; + } + } + // registry is null or type is not registered + appendCustom(object, destination); + } + private void appendCustom(IRubyObject object, IRubyObject destination) { if (destination == null) { IRubyObject result = object.callMethod(runtime.getCurrentContext(), "to_msgpack"); diff --git a/ext/java/org/msgpack/jruby/Factory.java b/ext/java/org/msgpack/jruby/Factory.java new file mode 100644 index 00000000..259fec2e --- /dev/null +++ b/ext/java/org/msgpack/jruby/Factory.java @@ -0,0 +1,126 @@ +package org.msgpack.jruby; + + +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyObject; +import org.jruby.RubyArray; +import org.jruby.RubyHash; +import org.jruby.RubyIO; +import org.jruby.RubyInteger; +import org.jruby.RubyFixnum; +import org.jruby.RubyString; +import org.jruby.RubySymbol; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.util.ByteList; + +import static org.jruby.runtime.Visibility.PRIVATE; + +@JRubyClass(name="MessagePack::Factory") +public class Factory extends RubyObject { + private Ruby runtime; + private Packer.ExtRegistry packerExtRegistry; + private Unpacker.ExtRegistry unpackerExtRegistry; + + public Factory(Ruby runtime, RubyClass type) { + super(runtime, type); + this.runtime = runtime; + } + + static class FactoryAllocator implements ObjectAllocator { + public IRubyObject allocate(Ruby runtime, RubyClass type) { + return new Factory(runtime, type); + } + } + + @JRubyMethod(name = "initialize") + public IRubyObject initialize(ThreadContext ctx) { + this.packerExtRegistry = new Packer.ExtRegistry(ctx.getRuntime()); + this.unpackerExtRegistry = new Unpacker.ExtRegistry(ctx.getRuntime()); + return this; + } + + public Packer.ExtRegistry packerRegistry() { + return this.packerExtRegistry.dup(); + } + + public Unpacker.ExtRegistry unpackerRegistry() { + return this.unpackerExtRegistry.dup(); + } + + @JRubyMethod(name = "packer", optional = 1) + public Packer packer(ThreadContext ctx, IRubyObject[] args) { + return Packer.newPacker(ctx, packerRegistry(), args); + } + + @JRubyMethod(name = "unpacker", optional = 1) + public Unpacker unpacker(ThreadContext ctx, IRubyObject[] args) { + return Unpacker.newUnpacker(ctx, unpackerRegistry(), args); + } + + @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE) + public IRubyObject registeredTypesInternal(ThreadContext ctx) { + //TODO: unpackerExtRegistry + // IRubyObject[] ary = { packerRegistry().hash, unpackerRegistry().array }; + IRubyObject[] ary = new IRubyObject[] {}; + return RubyArray.newArray(ctx.getRuntime(), ary); + } + + @JRubyMethod(name = "register_type", required = 2, optional = 1) + public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args) { + // register_type(type, Class) + // register_type(type, Class, packer: proc-like, unpacker: proc-like) + Ruby runtime = ctx.getRuntime(); + IRubyObject type = args[0]; + IRubyObject klass = args[1]; + + IRubyObject packerArg; + IRubyObject unpackerArg; + if (args.length == 2) { + packerArg = runtime.newSymbol("to_msgpack_ext"); + unpackerArg = runtime.newSymbol("from_msgpack_ext"); + } else if (args.length == 3) { + if (args[args.length - 1] instanceof RubyHash) { + RubyHash options = (RubyHash) args[args.length - 1]; + packerArg = options.fastARef(runtime.newSymbol("packer")); + unpackerArg = options.fastARef(runtime.newSymbol("unpacker")); + } else { + throw runtime.newArgumentError(String.format("expected Hash but found %s.", args[args.length - 1].getType().getName())); + } + } else { + throw runtime.newArgumentError(String.format("wrong number of arguments (%d for 2..3)", 2 + args.length)); + } + + int typeId = (int) ((RubyFixnum) type).getLongValue(); + if (typeId < -128 || typeId > 127) { + throw runtime.newRangeError(String.format("integer %d too big to convert to `signed char'", typeId)); + } + + if (!(klass instanceof RubyClass)) { + throw runtime.newArgumentError(String.format("expected Class but found %s.", klass.getType().getName())); + } + RubyClass extClass = (RubyClass) klass; + + IRubyObject packerProc = runtime.getNil(); + IRubyObject unpackerProc = runtime.getNil(); + if (packerArg != runtime.getNil()) { + packerProc = packerArg.callMethod(ctx, "to_proc"); + } + if (unpackerArg != runtime.getNil()) { + if (unpackerArg instanceof RubyString || unpackerArg instanceof RubySymbol) { + unpackerProc = extClass.method(unpackerArg.callMethod(ctx, "to_sym")); + } else { + unpackerProc = unpackerArg.callMethod(ctx, "method", runtime.newSymbol("call")); + } + } + + this.packerExtRegistry.put(extClass, typeId, packerProc, packerArg); + this.unpackerExtRegistry.put(extClass, typeId, unpackerProc, unpackerArg); + + return runtime.getNil(); + } +} diff --git a/ext/java/org/msgpack/jruby/MessagePackLibrary.java b/ext/java/org/msgpack/jruby/MessagePackLibrary.java index d3e266ca..b252875a 100644 --- a/ext/java/org/msgpack/jruby/MessagePackLibrary.java +++ b/ext/java/org/msgpack/jruby/MessagePackLibrary.java @@ -20,6 +20,8 @@ public class MessagePackLibrary implements Library { + public static Factory defaultFactory; + public void load(Ruby runtime, boolean wrap) { RubyModule msgpackModule = runtime.defineModule("MessagePack"); msgpackModule.defineAnnotatedMethods(MessagePackModule.class); @@ -37,6 +39,11 @@ public void load(Ruby runtime, boolean wrap) { unpackerClass.defineAnnotatedMethods(Unpacker.class); RubyClass bufferClass = msgpackModule.defineClassUnder("Buffer", runtime.getObject(), new Buffer.BufferAllocator()); bufferClass.defineAnnotatedMethods(Buffer.class); + RubyClass factoryClass = msgpackModule.defineClassUnder("Factory", runtime.getObject(), new Factory.FactoryAllocator()); + factoryClass.defineAnnotatedMethods(Factory.class); + defaultFactory = new Factory(runtime, factoryClass); + defaultFactory.initialize(runtime.getCurrentContext()); + msgpackModule.defineConstant("DefaultFactory", defaultFactory); installCoreExtensions(runtime); } @@ -100,15 +107,15 @@ public static IRubyObject pack(ThreadContext ctx, IRubyObject recv, IRubyObject[ extraArgs = new IRubyObject[args.length - 1]; System.arraycopy(args, 1, extraArgs, 0, args.length - 1); } - Packer packer = new Packer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Packer")); - packer.initialize(ctx, extraArgs); + Packer packer = MessagePackLibrary.defaultFactory.packer(ctx, extraArgs); packer.write(ctx, args[0]); return packer.toS(ctx); } @JRubyMethod(module = true, required = 1, optional = 1, alias = {"load"}) public static IRubyObject unpack(ThreadContext ctx, IRubyObject recv, IRubyObject[] args) { - Decoder decoder = new Decoder(ctx.getRuntime(), args[0].asString().getBytes()); + Unpacker.ExtRegistry registry = MessagePackLibrary.defaultFactory.unpackerRegistry(); + Decoder decoder = new Decoder(ctx.getRuntime(), registry, args[0].asString().getBytes()); if (args.length > 1 && !args[args.length - 1].isNil()) { RubyHash hash = args[args.length - 1].convertToHash(); IRubyObject symbolizeKeys = hash.fastARef(ctx.getRuntime().newSymbol("symbolize_keys")); diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index fd326477..a972b2ef 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -4,9 +4,11 @@ import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyObject; +import org.jruby.RubyArray; import org.jruby.RubyHash; import org.jruby.RubyIO; import org.jruby.RubyInteger; +import org.jruby.RubyFixnum; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.anno.JRubyClass; import org.jruby.anno.JRubyMethod; @@ -17,6 +19,7 @@ @JRubyClass(name="MessagePack::Packer") public class Packer extends RubyObject { + public ExtRegistry extRegistry; private Buffer buffer; private Encoder encoder; @@ -30,6 +33,62 @@ public IRubyObject allocate(Ruby runtime, RubyClass type) { } } + static class ExtRegistry { + private Ruby runtime; + public RubyHash hash; + public RubyHash cache; + + public ExtRegistry(Ruby runtime) { + this.runtime = runtime; + hash = RubyHash.newHash(runtime); + cache = RubyHash.newHash(runtime); + } + + public ExtRegistry dup() { + ExtRegistry copy = new ExtRegistry(this.runtime); + copy.hash = (RubyHash) this.hash.dup(runtime.getCurrentContext()); + copy.cache = RubyHash.newHash(runtime); + return copy; + } + + public void put(RubyClass klass, int typeId, IRubyObject proc, IRubyObject arg) { + RubyArray e = RubyArray.newArray(runtime, new IRubyObject[] { RubyFixnum.newFixnum(runtime, typeId), proc, arg }); + cache.rb_clear(); + hash.fastASet(klass, e); + } + + // proc, typeId(Fixnum) + public IRubyObject[] lookup(RubyClass klass) { + RubyArray e = (RubyArray) hash.fastARef(klass); + if (e == null) { + e = (RubyArray) cache.fastARef(klass); + } + if (e != null) { + IRubyObject[] pair = new IRubyObject[] {}; + pair[0] = e.entry(1); + pair[1] = e.entry(0); + } + + // check all keys whether it's super class of klass, or not + /* + for (IRubyObject keyValue : hash.keys()) { + RubyClass key = (RubyClass) keyValue; + // TODO: are there any way to check `key` is a superclass of `klass`? + if (false) { + IRubyObject hit = RubyArray.newArray(2); // TODO: value + cache.fastASet(klass, hit); + IRubyObject[] pair = new IRubyObject[] {}; + pair[0] = hit.entry(1); + pair[1] = hit.entry(0); + return pair; + } + } + */ + + return null; + } + } + @JRubyMethod(name = "initialize", optional = 2) public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { boolean compatibilityMode = false; @@ -43,6 +102,21 @@ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { return this; } + public void setExtRegistry(ExtRegistry registry) { + this.extRegistry = registry; + this.encoder.setRegistry(registry); + } + + public static Packer newPacker(ThreadContext ctx, ExtRegistry extRegistry, IRubyObject[] args) { + Packer packer = new Packer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Packer")); + packer.initialize(ctx, args); + packer.setExtRegistry(extRegistry); + return packer; + } + + //TODO: registered_types_internal + //TODO: register_type + @JRubyMethod(name = "write") public IRubyObject write(ThreadContext ctx, IRubyObject obj) { buffer.write(ctx, encoder.encode(obj, this)); diff --git a/ext/java/org/msgpack/jruby/Unpacker.java b/ext/java/org/msgpack/jruby/Unpacker.java index 4ecb6ec6..964e26ed 100644 --- a/ext/java/org/msgpack/jruby/Unpacker.java +++ b/ext/java/org/msgpack/jruby/Unpacker.java @@ -1,12 +1,15 @@ package org.msgpack.jruby; +import java.util.Arrays; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyString; import org.jruby.RubyObject; +import org.jruby.RubyArray; import org.jruby.RubyHash; import org.jruby.RubyNumeric; +import org.jruby.RubyFixnum; import org.jruby.RubyIO; import org.jruby.exceptions.RaiseException; import org.jruby.runtime.builtin.IRubyObject; @@ -23,11 +26,13 @@ @JRubyClass(name="MessagePack::Unpacker") public class Unpacker extends RubyObject { + public ExtRegistry registry; private IRubyObject stream; private IRubyObject data; private Decoder decoder; private final RubyClass underflowErrorClass; private boolean symbolizeKeys; + private boolean allowUnknownExt; public Unpacker(Ruby runtime, RubyClass type) { super(runtime, type); @@ -40,9 +45,40 @@ public IRubyObject allocate(Ruby runtime, RubyClass klass) { } } + static class ExtRegistry { + private Ruby runtime; + public RubyArray[] array; + + public ExtRegistry(Ruby runtime) { + this.runtime = runtime; + this.array = new RubyArray[256]; + } + + public ExtRegistry dup() { + ExtRegistry copy = new ExtRegistry(this.runtime); + copy.array = Arrays.copyOf(this.array, 256); + return copy; + } + + public void put(RubyClass klass, int typeId, IRubyObject proc, IRubyObject arg) { + RubyArray e = RubyArray.newArray(runtime, new IRubyObject[] { RubyFixnum.newFixnum(runtime, typeId), proc, arg }); + array[typeId + 128] = e; + } + + // proc, typeId(Fixnum) + public IRubyObject lookup(int type) { + RubyArray e = array[type + 128]; + if (e == null) { + return null; + } + return e.entry(1); + } + } + @JRubyMethod(name = "initialize", optional = 1, visibility = PRIVATE) public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { symbolizeKeys = false; + allowUnknownExt = false; if (args.length > 0) { if (args[args.length - 1] instanceof RubyHash) { RubyHash options = (RubyHash) args[args.length - 1]; @@ -50,6 +86,10 @@ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { if (sk != null) { symbolizeKeys = sk.isTrue(); } + IRubyObject au = options.fastARef(ctx.getRuntime().newSymbol("allow_unknown_ext")); + if (au != null) { + allowUnknownExt = au.isTrue(); + } } else if (!(args[0] instanceof RubyHash)) { setStream(ctx, args[0]); } @@ -57,6 +97,20 @@ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { return this; } + public void setExtRegistry(ExtRegistry registry) { + this.registry = registry; + } + + public static Unpacker newUnpacker(ThreadContext ctx, ExtRegistry extRegistry, IRubyObject[] args) { + Unpacker unpacker = new Unpacker(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Unpacker")); + unpacker.initialize(ctx, args); + unpacker.setExtRegistry(extRegistry); + return unpacker; + } + + //TODO: registered_types_internal + //TODO: register_type + @JRubyMethod(required = 2) public IRubyObject execute(ThreadContext ctx, IRubyObject data, IRubyObject offset) { return executeLimit(ctx, data, offset, null); @@ -71,8 +125,9 @@ public IRubyObject executeLimit(ThreadContext ctx, IRubyObject str, IRubyObject if (limit == -1) { limit = byteList.length() - offset; } - Decoder decoder = new Decoder(ctx.getRuntime(), byteList.unsafeBytes(), byteList.begin() + offset, limit); + Decoder decoder = new Decoder(ctx.getRuntime(), this.registry, byteList.unsafeBytes(), byteList.begin() + offset, limit); decoder.symbolizeKeys(symbolizeKeys); + decoder.allowUnknownExt(allowUnknownExt); try { this.data = null; this.data = decoder.next(); @@ -102,8 +157,9 @@ public IRubyObject finished_p(ThreadContext ctx) { public IRubyObject feed(ThreadContext ctx, IRubyObject data) { ByteList byteList = data.asString().getByteList(); if (decoder == null) { - decoder = new Decoder(ctx.getRuntime(), byteList.unsafeBytes(), byteList.begin(), byteList.length()); + decoder = new Decoder(ctx.getRuntime(), this.registry, byteList.unsafeBytes(), byteList.begin(), byteList.length()); decoder.symbolizeKeys(symbolizeKeys); + decoder.allowUnknownExt(allowUnknownExt); } else { decoder.feed(byteList.unsafeBytes(), byteList.begin(), byteList.length()); } @@ -220,8 +276,9 @@ public IRubyObject setStream(ThreadContext ctx, IRubyObject stream) { ByteList byteList = str.getByteList(); this.stream = stream; this.decoder = null; - this.decoder = new Decoder(ctx.getRuntime(), byteList.unsafeBytes(), byteList.begin(), byteList.length()); + this.decoder = new Decoder(ctx.getRuntime(), this.registry, byteList.unsafeBytes(), byteList.begin(), byteList.length()); decoder.symbolizeKeys(symbolizeKeys); + decoder.allowUnknownExt(allowUnknownExt); return getStream(ctx); } } From edd345b584c6404cb5dddf10c67db56f12200ba1 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Mon, 7 Sep 2015 21:21:13 +0900 Subject: [PATCH 02/39] add TODOs to remove unused code --- ext/msgpack/.factory_class.c.swp | Bin 0 -> 16384 bytes ext/msgpack/packer_ext_registry.c | 1 + ext/msgpack/packer_ext_registry.h | 1 + ext/msgpack/unpacker_ext_registry.c | 1 + ext/msgpack/unpacker_ext_registry.h | 1 + 5 files changed, 4 insertions(+) create mode 100644 ext/msgpack/.factory_class.c.swp diff --git a/ext/msgpack/.factory_class.c.swp b/ext/msgpack/.factory_class.c.swp new file mode 100644 index 0000000000000000000000000000000000000000..65f20450bcc23761d46d13cb7c3169ed0ac22ec1 GIT binary patch literal 16384 zcmeI2UyK_^9mltAXdC*6ihoZOO?^#cUwpRDfg~ios?J@mv3j{{&UZc-Zq~`ZDpx(v_54!Q^ySRu;Z*v)hxHvb4;U`toCRmu-viyZqO|Ez0SZe*QVT{ zxmo6CeRZMSHca1fH+8dTcwWg&O3A)w7RW5{!7Ol-wzoQ6p}G^f+t?>QzWTw$WVJF2 zWERLQkXazJKxTo=0+|Ie3uG4fzqEiqxLW%XG{0JE|Cs!L)zJTI{FtWw z0lWpC2hV{ua65SKW=;DC_%V1HJPZop^_w*9aj*tDpbhQ?li)h=&W)P(Yw#j?0XzjZ zz&dyUxEKo?Ad3ivem{YPML@C-0P6D)yaU;$hUc7S(wXxd-F z)4&2Y!;aflufWcQ<=T#$w``wT zGnGTkVt2CY=l9{uo;|9-iQ2;I5f&@KW+L@$zL7_Bp`7{J?8?&esl3N~ZPE(Y&uN}- z_?F3phsX<$mDWEU@V{81Bl(1tDz8jcu zlT@+Dwg~o%u-~{={Yl)E{W``y&*<>`&{us`%7b|<-!gjE7kS=rJ7$sL?>Z#P`%0XIlwrFKL^|U zbEF96%@K7+dQ%9AeJO>)lY?PTs#}w|HNubgg#&k-7e(ci55=V+4@xN`{3q^wK<|mR zuH1JNL1bU!7>20j8kWcN_`+*$=ZNb%Zjt3ST*vM(JLosL%bYe1jG)hL-(&e*ErtNZ zDurA)tSM?R`6-v#%F^5ta|XWU*dDVyW;;Ifg2BLXecoc5+z$&Qx*9>xKN!_)y4-*# zNREjJ$|~ZPw_*8amxYsvf?HVN^21hQ8dAKxqM=S9O(+yk2RJwkAtzm9pkl}xz0Fr- zYHsxncP;+ab~%@kHB@~|m`TRCaJ;_N99AP0i(w;?xGh~^lR|vhe`<6B)84Wd z2<=&Yi?)pv^DzjqWcXdq7A%w79xus44bGF7!$pLOc>``*ZEQJvhTRFUm0}%d9h*yR zp4ouAearJg!G_(El1|oFjxMdPu#>gr<=Wy({YZl?EwkCB#ku-QeQ6QD=2>m=6uY;+ zI9FsGuVq6cesq#VI>+Wv;& za!8WG$gEx89~>-~H#Rm(2K>|ICCBZQA&|`Kl^5!>M;04LrmCe%T-zK^{88Bhklr5qf54IBsm zrCb~QhisgEG7EfIEFjMyn!4C*=q45=Ki^Np3Zysbbs34wHmkPW7s_=ynG{xv;nwDI zYHuSSJ98y_M5-unK?#kP1f*%QUw!U(omTo>Ko&*^)qzcTT!a3p7E;%XY>GDiX}JT} zS~q;IU!MFe2SOv-+ES*7Y$4b;wZLi;Yvy zghk?uqeX`P*V}HR?y=jR~0+nke(dv;N-;jpIFp_~JYAyO-!zCP$#9UI*+yN0_Pic@ps;n=e33LY>_;lTxs zg6Ps+c$Bd}sVP+zUc{kc6|K{m-TLVYJ=@U8Sc{*iAZ^=Ll&F*z6dQwdyyQ?jilpb#BTaTkjWd#* z7;EaT7-{rKOSz8=uuwgyEyG166x$6$n2V?|Tm*;*IjV_eN` zx|mkIy3Mh)SPZ2SVgZTzLqCP0sZ!`>dKpWWkuFJIqUje#7I6`?ESiyWmZ_t)%YURN zd`hCA4OZ1q!7)#QCb-?Q+8Vh^KIFsDHSqWjPZjs(t6{WO>mD7e(n4FJ`Id^~c-ALk dIQkwvCxss;XnEUScsO_ISz$^#7KD_b{{iS>1e5>( literal 0 HcmV?d00001 diff --git a/ext/msgpack/packer_ext_registry.c b/ext/msgpack/packer_ext_registry.c index de5b46c0..5ce7f44a 100644 --- a/ext/msgpack/packer_ext_registry.c +++ b/ext/msgpack/packer_ext_registry.c @@ -78,6 +78,7 @@ VALUE msgpack_packer_ext_registry_put(msgpack_packer_ext_registry_t* pkrg, return rb_hash_aset(pkrg->hash, ext_class, e); } +// TODO: delete this function (unused) VALUE msgpack_packer_ext_registry_call(msgpack_packer_ext_registry_t* pkrg, VALUE proc, VALUE ext_value) { diff --git a/ext/msgpack/packer_ext_registry.h b/ext/msgpack/packer_ext_registry.h index 5fb216b0..6f3016ec 100644 --- a/ext/msgpack/packer_ext_registry.h +++ b/ext/msgpack/packer_ext_registry.h @@ -95,6 +95,7 @@ static inline VALUE msgpack_packer_ext_registry_lookup(msgpack_packer_ext_regist return Qnil; } +// TODO: delete this function (unused) VALUE msgpack_packer_ext_registry_call(msgpack_packer_ext_registry_t* pkrg, VALUE proc, VALUE ext_value); diff --git a/ext/msgpack/unpacker_ext_registry.c b/ext/msgpack/unpacker_ext_registry.c index 9a8b78b3..45c7f343 100644 --- a/ext/msgpack/unpacker_ext_registry.c +++ b/ext/msgpack/unpacker_ext_registry.c @@ -61,6 +61,7 @@ VALUE msgpack_unpacker_ext_registry_put(msgpack_unpacker_ext_registry_t* ukrg, return before; } +// TODO: delete this function (unused) VALUE msgpack_unpacker_ext_registry_call(msgpack_unpacker_ext_registry_t* ukrg, VALUE proc, VALUE ext_data) { diff --git a/ext/msgpack/unpacker_ext_registry.h b/ext/msgpack/unpacker_ext_registry.h index 683213dd..ee2d19b5 100644 --- a/ext/msgpack/unpacker_ext_registry.h +++ b/ext/msgpack/unpacker_ext_registry.h @@ -56,6 +56,7 @@ static inline VALUE msgpack_unpacker_ext_registry_lookup(msgpack_unpacker_ext_re return rb_ary_entry(e, 1); } +// TODO: delete this function (unused) VALUE msgpack_unpacker_ext_registry_call(msgpack_unpacker_ext_registry_t* ukrg, VALUE proc, VALUE ext_data); From 7af605991b2119329289c46b939aaf007d26773d Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Mon, 7 Sep 2015 21:21:50 +0900 Subject: [PATCH 03/39] cross-compile utility only for CRuby runtime --- msgpack.gemspec | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/msgpack.gemspec b/msgpack.gemspec index 826dae8d..4ea7d289 100644 --- a/msgpack.gemspec +++ b/msgpack.gemspec @@ -25,7 +25,9 @@ Gem::Specification.new do |s| s.add_development_dependency 'bundler', ['~> 1.0'] s.add_development_dependency 'rake', ['~> 0.9.2'] s.add_development_dependency 'rake-compiler', ['~> 0.9.4'] - s.add_development_dependency 'rake-compiler-dock', ['~> 0.4.3'] + if /java/ !~ RUBY_PLATFORM + s.add_development_dependency 'rake-compiler-dock', ['~> 0.4.3'] + end s.add_development_dependency 'rspec', ['~> 3.3'] s.add_development_dependency 'yard', ['~> 0.8.2'] s.add_development_dependency 'json' From 76ba9025ec90abb0ad5b96110237ba1949387141 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Tue, 8 Sep 2015 13:46:50 +0900 Subject: [PATCH 04/39] Fix bug to do invalid cast before check whether value is of int correctly or not --- ext/java/org/msgpack/jruby/Encoder.java | 4 ++-- ext/java/org/msgpack/jruby/Factory.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Encoder.java b/ext/java/org/msgpack/jruby/Encoder.java index a8c47cc2..04712c20 100644 --- a/ext/java/org/msgpack/jruby/Encoder.java +++ b/ext/java/org/msgpack/jruby/Encoder.java @@ -339,12 +339,12 @@ private void appendExt(int type, ByteList payloadBytes) { } private void appendExtensionValue(ExtensionValue object) { - int type = (int) ((RubyFixnum)object.get_type()).getLongValue(); + long type = ((RubyFixnum)object.get_type()).getLongValue(); if (type < -128 || type > 127) { throw object.getRuntime().newRangeError(String.format("integer %d too big to convert to `signed char'", type)); } ByteList payloadBytes = ((RubyString)object.payload()).getByteList(); - appendExt(type, payloadBytes); + appendExt((int) type, payloadBytes); } private void appendOther(IRubyObject object, IRubyObject destination) { diff --git a/ext/java/org/msgpack/jruby/Factory.java b/ext/java/org/msgpack/jruby/Factory.java index 259fec2e..1a6fcc07 100644 --- a/ext/java/org/msgpack/jruby/Factory.java +++ b/ext/java/org/msgpack/jruby/Factory.java @@ -95,7 +95,7 @@ public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args) { throw runtime.newArgumentError(String.format("wrong number of arguments (%d for 2..3)", 2 + args.length)); } - int typeId = (int) ((RubyFixnum) type).getLongValue(); + long typeId = ((RubyFixnum) type).getLongValue(); if (typeId < -128 || typeId > 127) { throw runtime.newRangeError(String.format("integer %d too big to convert to `signed char'", typeId)); } @@ -118,8 +118,8 @@ public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args) { } } - this.packerExtRegistry.put(extClass, typeId, packerProc, packerArg); - this.unpackerExtRegistry.put(extClass, typeId, unpackerProc, unpackerArg); + this.packerExtRegistry.put(extClass, (int) typeId, packerProc, packerArg); + this.unpackerExtRegistry.put(extClass, (int) typeId, unpackerProc, unpackerArg); return runtime.getNil(); } From b498368fba50776813d4ad216b741a14e0caa772 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Tue, 8 Sep 2015 16:45:06 +0900 Subject: [PATCH 05/39] add registered_types_internal and register_type for jruby implementation --- ext/java/org/msgpack/jruby/Factory.java | 13 ++++-- ext/java/org/msgpack/jruby/Packer.java | 51 +++++++++++++++++++-- ext/java/org/msgpack/jruby/Unpacker.java | 57 ++++++++++++++++++++++-- spec/msgpack_spec.rb | 2 +- spec/packer_spec.rb | 4 -- spec/unpacker_spec.rb | 6 --- 6 files changed, 111 insertions(+), 22 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Factory.java b/ext/java/org/msgpack/jruby/Factory.java index 1a6fcc07..a3efc35d 100644 --- a/ext/java/org/msgpack/jruby/Factory.java +++ b/ext/java/org/msgpack/jruby/Factory.java @@ -64,9 +64,16 @@ public Unpacker unpacker(ThreadContext ctx, IRubyObject[] args) { @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE) public IRubyObject registeredTypesInternal(ThreadContext ctx) { - //TODO: unpackerExtRegistry - // IRubyObject[] ary = { packerRegistry().hash, unpackerRegistry().array }; - IRubyObject[] ary = new IRubyObject[] {}; + // unpacker + Unpacker.ExtRegistry reg = unpackerRegistry(); + RubyHash mapping = RubyHash.newHash(ctx.getRuntime()); + for (int i = 0; i < 256; i++) { + if (reg.array[i] != null) { + mapping.fastASet(RubyFixnum.newFixnum(ctx.getRuntime(), i - 128), reg.array[i]); + } + } + + IRubyObject[] ary = { packerRegistry().hash, mapping }; return RubyArray.newArray(ctx.getRuntime(), ary); } diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index a972b2ef..d9b99c10 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -9,6 +9,7 @@ import org.jruby.RubyIO; import org.jruby.RubyInteger; import org.jruby.RubyFixnum; +import org.jruby.runtime.Block; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.anno.JRubyClass; import org.jruby.anno.JRubyMethod; @@ -16,10 +17,11 @@ import org.jruby.runtime.ObjectAllocator; import org.jruby.util.ByteList; +import static org.jruby.runtime.Visibility.PRIVATE; @JRubyClass(name="MessagePack::Packer") public class Packer extends RubyObject { - public ExtRegistry extRegistry; + public ExtRegistry registry; private Buffer buffer; private Encoder encoder; @@ -99,11 +101,12 @@ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { this.encoder = new Encoder(ctx.getRuntime(), compatibilityMode); this.buffer = new Buffer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Buffer")); this.buffer.initialize(ctx, args); + this.registry = new ExtRegistry(ctx.getRuntime()); return this; } public void setExtRegistry(ExtRegistry registry) { - this.extRegistry = registry; + this.registry = registry; this.encoder.setRegistry(registry); } @@ -114,8 +117,48 @@ public static Packer newPacker(ThreadContext ctx, ExtRegistry extRegistry, IRuby return packer; } - //TODO: registered_types_internal - //TODO: register_type + @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE) + public IRubyObject registeredTypesInternal(ThreadContext ctx) { + ////Nullpo + return this.registry.hash.dup(ctx); + } + + @JRubyMethod(name = "register_type", required = 2, optional = 1) + public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args, final Block block) { + // register_type(type, Class){|obj| how_to_serialize.... } + // register_type(type, Class, :to_msgpack_ext) + Ruby runtime = ctx.getRuntime(); + IRubyObject type = args[0]; + IRubyObject klass = args[1]; + + IRubyObject arg; + IRubyObject proc; + if (args.length == 2) { + if (! block.isGiven()) { + throw runtime.newLocalJumpErrorNoBlock(); + } + proc = block.getProcObject(); + arg = proc; + } else if (args.length == 3) { + arg = args[2]; + proc = arg.callMethod(ctx, "to_proc"); + } else { + throw runtime.newArgumentError(String.format("wrong number of arguments (%d for 2..3)", 2 + args.length)); + } + + long typeId = ((RubyFixnum) type).getLongValue(); + if (typeId < -128 || typeId > 127) { + throw runtime.newRangeError(String.format("integer %d too big to convert to `signed char'", typeId)); + } + + if (!(klass instanceof RubyClass)) { + throw runtime.newArgumentError(String.format("expected Class but found %s.", klass.getType().getName())); + } + RubyClass extClass = (RubyClass) klass; + + this.registry.put(extClass, (int) typeId, proc, arg); + return runtime.getNil(); + } @JRubyMethod(name = "write") public IRubyObject write(ThreadContext ctx, IRubyObject obj) { diff --git a/ext/java/org/msgpack/jruby/Unpacker.java b/ext/java/org/msgpack/jruby/Unpacker.java index 964e26ed..5d30957a 100644 --- a/ext/java/org/msgpack/jruby/Unpacker.java +++ b/ext/java/org/msgpack/jruby/Unpacker.java @@ -10,6 +10,7 @@ import org.jruby.RubyHash; import org.jruby.RubyNumeric; import org.jruby.RubyFixnum; +import org.jruby.RubyProc; import org.jruby.RubyIO; import org.jruby.exceptions.RaiseException; import org.jruby.runtime.builtin.IRubyObject; @@ -23,7 +24,6 @@ import static org.jruby.runtime.Visibility.PRIVATE; - @JRubyClass(name="MessagePack::Unpacker") public class Unpacker extends RubyObject { public ExtRegistry registry; @@ -61,7 +61,11 @@ public ExtRegistry dup() { } public void put(RubyClass klass, int typeId, IRubyObject proc, IRubyObject arg) { - RubyArray e = RubyArray.newArray(runtime, new IRubyObject[] { RubyFixnum.newFixnum(runtime, typeId), proc, arg }); + IRubyObject k = klass; + if (k == null) { + k = runtime.getNil(); + } + RubyArray e = RubyArray.newArray(runtime, new IRubyObject[] { k, proc, arg }); array[typeId + 128] = e; } @@ -94,6 +98,7 @@ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { setStream(ctx, args[0]); } } + this.registry = new ExtRegistry(ctx.getRuntime()); return this; } @@ -108,8 +113,52 @@ public static Unpacker newUnpacker(ThreadContext ctx, ExtRegistry extRegistry, I return unpacker; } - //TODO: registered_types_internal - //TODO: register_type + @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE) + public IRubyObject registeredTypesInternal(ThreadContext ctx) { + RubyHash mapping = RubyHash.newHash(ctx.getRuntime()); + for (int i = 0; i < 256; i++) { + if (registry.array[i] != null) { + mapping.fastASet(RubyFixnum.newFixnum(ctx.getRuntime(), i - 128), registry.array[i].dup()); + } + } + return mapping; + } + + @JRubyMethod(name = "register_type", required = 1, optional = 2) + public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args, final Block block) { + // register_type(type){|data| ExtClass.deserialize(...) } + // register_type(type, Class, :from_msgpack_ext) + Ruby runtime = ctx.getRuntime(); + IRubyObject type = args[0]; + + RubyClass extClass; + IRubyObject arg; + IRubyObject proc; + if (args.length == 1) { + if (! block.isGiven()) { + throw runtime.newLocalJumpErrorNoBlock(); + } + proc = RubyProc.newProc(runtime, block, block.type); + if (proc == null) + System.err.println("proc from Block is null"); + arg = proc; + extClass = null; + } else if (args.length == 3) { + extClass = (RubyClass) args[1]; + arg = args[2]; + proc = extClass.method(arg); + } else { + throw runtime.newArgumentError(String.format("wrong number of arguments (%d for 1 or 3)", 2 + args.length)); + } + + long typeId = ((RubyFixnum) type).getLongValue(); + if (typeId < -128 || typeId > 127) { + throw runtime.newRangeError(String.format("integer %d too big to convert to `signed char'", typeId)); + } + + this.registry.put(extClass, (int) typeId, proc, arg); + return runtime.getNil(); + } @JRubyMethod(required = 2) public IRubyObject execute(ThreadContext ctx, IRubyObject data, IRubyObject offset) { diff --git a/spec/msgpack_spec.rb b/spec/msgpack_spec.rb index 2c41ff75..3be6777b 100644 --- a/spec/msgpack_spec.rb +++ b/spec/msgpack_spec.rb @@ -116,7 +116,7 @@ def asciienc(str) end it 'rasies an error on #unpack with garbage' do - skip "???" + skip "but nothing was raised. why?" expect { MessagePack.unpack('asdka;sd') }.to raise_error(MessagePack::UnpackError) end end diff --git a/spec/packer_spec.rb b/spec/packer_spec.rb index 94813cb0..952a6b6f 100644 --- a/spec/packer_spec.rb +++ b/spec/packer_spec.rb @@ -33,14 +33,12 @@ def self.from_msgpack_ext(data) describe '#type_registered?' do it 'receive Class or Integer, and return bool' do - skip("not supported yet in JRuby implementation") if java? expect(subject.type_registered?(0x00)).to be_falsy expect(subject.type_registered?(0x01)).to be_falsy expect(subject.type_registered?(::ValueOne)).to be_falsy end it 'returns true if specified type or class is already registered' do - skip("not supported yet in JRuby implementation") if java? subject.register_type(0x30, ::ValueOne, :to_msgpack_ext) subject.register_type(0x31, ::ValueTwo, :to_msgpack_ext) @@ -56,7 +54,6 @@ def self.from_msgpack_ext(data) describe '#register_type' do it 'get type and class mapping for packing' do - skip("not supported yet in JRuby implementation") if java? packer = MessagePack::Packer.new packer.register_type(0x01, ValueOne){|obj| obj.to_msgpack_ext } packer.register_type(0x02, ValueTwo){|obj| obj.to_msgpack_ext } @@ -71,7 +68,6 @@ def self.from_msgpack_ext(data) end it 'returns a Hash which contains map of Class and type' do - skip("not supported yet in JRuby implementation") if java? packer = MessagePack::Packer.new packer.register_type(0x01, ValueOne, :to_msgpack_ext) packer.register_type(0x02, ValueTwo, :to_msgpack_ext) diff --git a/spec/unpacker_spec.rb b/spec/unpacker_spec.rb index e5b5f23d..702614df 100644 --- a/spec/unpacker_spec.rb +++ b/spec/unpacker_spec.rb @@ -41,14 +41,12 @@ def self.from_msgpack_ext(data) describe '#type_registered?' do it 'receive Class or Integer, and return bool' do - skip("not supported yet in JRuby implementation") if java? expect(subject.type_registered?(0x00)).to be_falsy expect(subject.type_registered?(0x01)).to be_falsy expect(subject.type_registered?(::ValueOne)).to be_falsy end it 'returns true if specified type or class is already registered' do - skip("not supported yet in JRuby implementation") if java? subject.register_type(0x30, ::ValueOne, :from_msgpack_ext) subject.register_type(0x31, ::ValueTwo, :from_msgpack_ext) @@ -62,7 +60,6 @@ def self.from_msgpack_ext(data) end it 'cannot detect unpack rule with block, not method' do - skip("not supported yet in JRuby implementation") if java? subject.register_type(0x40){|data| ValueOne.from_msgpack_ext(data) } expect(subject.type_registered?(0x40)).to be_truthy @@ -72,7 +69,6 @@ def self.from_msgpack_ext(data) context 'with ext definitions' do it 'get type and class mapping for packing' do - skip("not supported yet in JRuby implementation") if java? unpacker = MessagePack::Unpacker.new unpacker.register_type(0x01){|data| ValueOne.from_msgpack_ext } unpacker.register_type(0x02){|data| ValueTwo.from_msgpack_ext(data) } @@ -83,7 +79,6 @@ def self.from_msgpack_ext(data) end it 'returns a Array of Hash which contains :type, :class and :unpacker' do - skip("not supported yet in JRuby implementation") if java? unpacker = MessagePack::Unpacker.new unpacker.register_type(0x02, ValueTwo, :from_msgpack_ext) unpacker.register_type(0x01, ValueOne, :from_msgpack_ext) @@ -107,7 +102,6 @@ def self.from_msgpack_ext(data) end it 'returns a Array of Hash, which contains nil for class if block unpacker specified' do - skip("not supported yet in JRuby implementation") if java? unpacker = MessagePack::Unpacker.new unpacker.register_type(0x01){|data| ValueOne.from_msgpack_ext } unpacker.register_type(0x02, &ValueTwo.method(:from_msgpack_ext)) From 8aec89aa674b0c8ccbbb545e630696d1f2817a8c Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Tue, 8 Sep 2015 19:30:25 +0900 Subject: [PATCH 06/39] Fix to check included classes to fetch how to serialize --- ext/java/org/msgpack/jruby/Packer.java | 42 +++++++++++++++----------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index d9b99c10..f9cac2be 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -60,34 +60,40 @@ public void put(RubyClass klass, int typeId, IRubyObject proc, IRubyObject arg) } // proc, typeId(Fixnum) - public IRubyObject[] lookup(RubyClass klass) { + public IRubyObject[] lookup(final RubyClass klass) { RubyArray e = (RubyArray) hash.fastARef(klass); if (e == null) { e = (RubyArray) cache.fastARef(klass); } if (e != null) { - IRubyObject[] pair = new IRubyObject[] {}; - pair[0] = e.entry(1); - pair[1] = e.entry(0); + IRubyObject[] hit = new IRubyObject[] {}; + hit[0] = e.entry(1); + hit[1] = e.entry(0); + return hit; } + final IRubyObject[] pair = new IRubyObject[2]; // check all keys whether it's super class of klass, or not - /* - for (IRubyObject keyValue : hash.keys()) { - RubyClass key = (RubyClass) keyValue; - // TODO: are there any way to check `key` is a superclass of `klass`? - if (false) { - IRubyObject hit = RubyArray.newArray(2); // TODO: value - cache.fastASet(klass, hit); - IRubyObject[] pair = new IRubyObject[] {}; - pair[0] = hit.entry(1); - pair[1] = hit.entry(0); - return pair; + hash.visitAll(new RubyHash.Visitor() { + public void visit(IRubyObject keyValue, IRubyObject value) { + if (pair[0] != null) { + return; + } + RubyClass key = (RubyClass) keyValue; + IRubyObject rb_class_inherited_p = klass.include_p(runtime.getCurrentContext(), key); + if (rb_class_inherited_p.isTrue()) { + RubyArray hit = (RubyArray) hash.fastARef(keyValue); + cache.fastASet(klass, hit); + pair[0] = hit.entry(1); + pair[1] = hit.entry(0); + } } - } - */ + }); - return null; + if (pair[0] == null) { + return null; + } + return pair; } } From 1a2edf6ba1be56e97d5ca644084b0c59d112f95c Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Tue, 8 Sep 2015 19:31:15 +0900 Subject: [PATCH 07/39] fix to check compatibility_mode option is missing --- ext/java/org/msgpack/jruby/Packer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index f9cac2be..840727c1 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -102,7 +102,8 @@ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { boolean compatibilityMode = false; if (args.length > 0 && args[args.length - 1] instanceof RubyHash) { RubyHash options = (RubyHash) args[args.length - 1]; - compatibilityMode = options.fastARef(ctx.getRuntime().newSymbol("compatibility_mode")).isTrue(); + IRubyObject mode = options.fastARef(ctx.getRuntime().newSymbol("compatibility_mode")); + compatibilityMode = (mode != null) && mode.isTrue(); } this.encoder = new Encoder(ctx.getRuntime(), compatibilityMode); this.buffer = new Buffer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Buffer")); From fdc71d44dc1d3b7a9c5646348e2a1c2592f38ee2 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Tue, 8 Sep 2015 19:31:47 +0900 Subject: [PATCH 08/39] add missing aliases as same with cruby implementation --- ext/java/org/msgpack/jruby/Packer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index 840727c1..768c1e16 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -167,7 +167,7 @@ public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args, final Blo return runtime.getNil(); } - @JRubyMethod(name = "write") + @JRubyMethod(name = "write", alias = { "pack" }) public IRubyObject write(ThreadContext ctx, IRubyObject obj) { buffer.write(ctx, encoder.encode(obj, this)); return this; @@ -193,7 +193,7 @@ public IRubyObject writeMapHeader(ThreadContext ctx, IRubyObject size) { return this; } - @JRubyMethod(name = "to_s") + @JRubyMethod(name = "to_s", alias = { "to_str" }) public IRubyObject toS(ThreadContext ctx) { return buffer.toS(ctx); } From ca3b4a09c70a8791c2a6b4e17da5dc6e9550abb6 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Tue, 8 Sep 2015 19:32:37 +0900 Subject: [PATCH 09/39] add missing aliases and exceptions --- ext/java/org/msgpack/jruby/Decoder.java | 20 ++++++++++++------ .../org/msgpack/jruby/MessagePackLibrary.java | 5 ++++- ext/java/org/msgpack/jruby/Unpacker.java | 16 +++++++++++++- ext/msgpack/.unpacker.c.swp | Bin 0 -> 16384 bytes 4 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 ext/msgpack/.unpacker.c.swp diff --git a/ext/java/org/msgpack/jruby/Decoder.java b/ext/java/org/msgpack/jruby/Decoder.java index 6d7a2720..f4eb2925 100644 --- a/ext/java/org/msgpack/jruby/Decoder.java +++ b/ext/java/org/msgpack/jruby/Decoder.java @@ -30,7 +30,10 @@ public class Decoder implements Iterator { private final Encoding utf8Encoding; private final RubyClass unpackErrorClass; private final RubyClass underflowErrorClass; + private final RubyClass malformedFormatErrorClass; + private final RubyClass stackErrorClass; private final RubyClass unexpectedTypeErrorClass; + private final RubyClass unknownExtTypeErrorClass; private Unpacker.ExtRegistry registry; private ByteBuffer buffer; @@ -60,7 +63,10 @@ public Decoder(Ruby runtime, Unpacker.ExtRegistry registry, byte[] bytes, int of this.utf8Encoding = UTF8Encoding.INSTANCE; this.unpackErrorClass = runtime.getModule("MessagePack").getClass("UnpackError"); this.underflowErrorClass = runtime.getModule("MessagePack").getClass("UnderflowError"); + this.malformedFormatErrorClass = runtime.getModule("MessagePack").getClass("MalformedFormatError"); + this.stackErrorClass = runtime.getModule("MessagePack").getClass("StackError"); this.unexpectedTypeErrorClass = runtime.getModule("MessagePack").getClass("UnexpectedTypeError"); + this.unknownExtTypeErrorClass = runtime.getModule("MessagePack").getClass("UnknownExtTypeError"); feed(bytes, offset, length); } @@ -171,11 +177,11 @@ public IRubyObject read_array_header() { try { byte b = buffer.get(); if ((b & 0xf0) == 0x90) { - return runtime.newFixnum(b & 0x0f); + return runtime.newFixnum(b & 0x0f); } else if (b == ARY16) { - return runtime.newFixnum(buffer.getShort() & 0xffff); + return runtime.newFixnum(buffer.getShort() & 0xffff); } else if (b == ARY32) { - return runtime.newFixnum(buffer.getInt()); + return runtime.newFixnum(buffer.getInt()); } throw runtime.newRaiseException(unexpectedTypeErrorClass, "unexpected type"); } catch (RaiseException re) { @@ -192,11 +198,11 @@ public IRubyObject read_map_header() { try { byte b = buffer.get(); if ((b & 0xf0) == 0x80) { - return runtime.newFixnum(b & 0x0f); + return runtime.newFixnum(b & 0x0f); } else if (b == MAP16) { - return runtime.newFixnum(buffer.getShort() & 0xffff); + return runtime.newFixnum(buffer.getShort() & 0xffff); } else if (b == MAP32) { - return runtime.newFixnum(buffer.getInt()); + return runtime.newFixnum(buffer.getInt()); } throw runtime.newRaiseException(unexpectedTypeErrorClass, "unexpected type"); } catch (RaiseException re) { @@ -262,7 +268,7 @@ public IRubyObject next() { default: return runtime.newFixnum(b); } buffer.position(position); - throw runtime.newRaiseException(unpackErrorClass, "Illegal byte sequence"); + throw runtime.newRaiseException(malformedFormatErrorClass, "Illegal byte sequence"); } catch (RaiseException re) { buffer.position(position); throw re; diff --git a/ext/java/org/msgpack/jruby/MessagePackLibrary.java b/ext/java/org/msgpack/jruby/MessagePackLibrary.java index b252875a..f2526c77 100644 --- a/ext/java/org/msgpack/jruby/MessagePackLibrary.java +++ b/ext/java/org/msgpack/jruby/MessagePackLibrary.java @@ -28,9 +28,12 @@ public void load(Ruby runtime, boolean wrap) { RubyClass standardErrorClass = runtime.getStandardError(); RubyClass unpackErrorClass = msgpackModule.defineClassUnder("UnpackError", standardErrorClass, standardErrorClass.getAllocator()); RubyClass underflowErrorClass = msgpackModule.defineClassUnder("UnderflowError", unpackErrorClass, unpackErrorClass.getAllocator()); + RubyClass malformedFormatErrorClass = msgpackModule.defineClassUnder("MalformedFormatError", unpackErrorClass, unpackErrorClass.getAllocator()); + RubyClass stackErrorClass = msgpackModule.defineClassUnder("StackError", unpackErrorClass, unpackErrorClass.getAllocator()); RubyModule typeErrorModule = msgpackModule.defineModuleUnder("TypeError"); - RubyClass unexpectedTypeErrorClass = msgpackModule.defineClassUnder("UnexpetedTypeError", unpackErrorClass, standardErrorClass.getAllocator()); + RubyClass unexpectedTypeErrorClass = msgpackModule.defineClassUnder("UnexpectedTypeError", unpackErrorClass, unpackErrorClass.getAllocator()); unexpectedTypeErrorClass.includeModule(typeErrorModule); + RubyClass unknownExtTypeErrorClass = msgpackModule.defineClassUnder("UnknownExtTypeError", unpackErrorClass, unpackErrorClass.getAllocator()); RubyClass extensionValueClass = msgpackModule.defineClassUnder("ExtensionValue", runtime.getObject(), new ExtensionValue.ExtensionValueAllocator()); extensionValueClass.defineAnnotatedMethods(ExtensionValue.class); RubyClass packerClass = msgpackModule.defineClassUnder("Packer", runtime.getObject(), new Packer.PackerAllocator()); diff --git a/ext/java/org/msgpack/jruby/Unpacker.java b/ext/java/org/msgpack/jruby/Unpacker.java index 5d30957a..aadb64a8 100644 --- a/ext/java/org/msgpack/jruby/Unpacker.java +++ b/ext/java/org/msgpack/jruby/Unpacker.java @@ -255,7 +255,7 @@ public IRubyObject reset(ThreadContext ctx) { return ctx.getRuntime().getNil(); } - @JRubyMethod + @JRubyMethod(name = "read", alias = { "unpack" }) public IRubyObject read(ThreadContext ctx) { if (decoder != null) { try { @@ -271,6 +271,20 @@ public IRubyObject read(ThreadContext ctx) { return ctx.getRuntime().getNil(); } + /* + skip & skip_nil don't exist in JRuby implementation: + these depend on CRuby msgpack implemntation too highly. + */ + @JRubyMethod(name = "skip") + public IRubyObject skip(ThreadContext ctx) { + throw ctx.getRuntime().newNotImplementedError("Not supported yet in JRuby implementation"); + } + + @JRubyMethod(name = "skip_nil") + public IRubyObject skipNil(ThreadContext ctx) { + throw ctx.getRuntime().newNotImplementedError("Not supported yet in JRuby implementation"); + } + @JRubyMethod public IRubyObject read_array_header(ThreadContext ctx) { if (decoder != null) { diff --git a/ext/msgpack/.unpacker.c.swp b/ext/msgpack/.unpacker.c.swp new file mode 100644 index 0000000000000000000000000000000000000000..adb3bf980d864a551a25e92cd2db5c80a177e6ec GIT binary patch literal 16384 zcmeI2Uu+{s9mj`qcfEs_B6y>;o#v|G8rURGvTEX_wsWVH!fL%cwinyG zzWsBF8}5)QD#59h`qCG8;iV_&0|ilmprTSa0Tn#K6C4kqN>Bupdw^D=?NPq7J8Q4K zadN%#0$NL-IG*{$FOt_@A;eXyR@1T# zhu7`#DoUlb;<7w$TH~v#HfLG&amlbJ8kVLuqRlRL9Hy&=Tl324rc-UHT7BI1%I!4U zcEf_SXYz7pPiRpG$^VH0i30CKf!oRP$!vx@I68DM{rJZ=-iNQGX`(=)K%zjRK%zjR zK%zjRK%zjR!2g;8ZtF1l5`w-(1pZa={(-*tcg5>*QGThf{@F z+kN%#i2Aaqf3>gvZBc(g)GvsDL;r7ydQa59+*kiWQGQaqPlB=je7}t6|Ka}s`W=M)5xfds0N(|5a36T{ zBZT||yaZkZ=fDWKdOIOcgF2{!3RnlT;7;%f@T=Qk4>mv^tb$|U2sjMh{4gPJfUDs9 z;Cb+Ea0z?`G{F-14EQv-2iyfd3Eub+;sU<`&x3D*%ivk?1o$#o2YGN0xEowMLdf62 z&%jT?x4={2NpKF#f_D!S@-Of;FbhtC>$ei}Hh2qM0WX8gzylU|1QbC5P;fW63;g8} zA%6xx2baOKU>i({{Obat zc-hSLY?GE}{7*p{FE7&vQK#xkjsGdPU6%6dBY~AsNul~dx}1|1l)24PPATLb-jEBq zg%P^r`!P({_u$pXXFCpN$PRH_)ipF~m<_{Zcyno$xsg7^4^ouy_a}-wO3^PNJ<;~K z<$?A`FGjUqkXPlBd?u&l))qUl+dTI1Y&2FB(Xi{XSE(>t@duwxZRFRb`G;}^Wo~0} z5pP@t$PgKc+pkmTsDBLUQOdQTTufT=OwBbc)1jkj%waG*J}%y`VOAAe-9ija)iCi1 z;bnsO`%F;ruxT{9bq&>V6|4LOrn#Lue@b{msIS!(oweLr*JFCL$(jyxF*0E>Ml)m7 zF&<-9B^4jc2<21K<7{Mx4+2@^S9B&EeZ*5_CNK>B3bnNcQ|-ZGpSKL1Dq)+Bur}jy z$BO%MrmR#orK#F=e}tXst%z*Kw1gf+oo~DQ#PiwW<3yt(V|0}{j#_2wT-LcDZ?wcf?ZWPY=Y@M}5@7$`lE|@$~U9s%c40Y&f>M&Esf5(o6c^d}ZQ6rdL44#`p z`_3fMW8MFQ*$?t%4-e7iV#Xp-a?g=G6|wdiL^2TuJH(&ESeL%mvC`fd46DWT=*)3# zt34=N$WaX6s1UAf#b$in63L1%jqMt$Dt>ckxNT^^T3q7Gj?&lRyo6G@w8_WO8Dqx* z+BMY%e)X5yr#ZgBgTCD&hxii5^-GUNz2oPEH{gBn($RH}Z&7z91}s4P48mV?c#P2q zL#_Qu4WjLt)xD>Zgzh4_4N>320(&z{9~O9bU@r5BI?MAu>oNu>zGd-b^g9XFiDlwZ zBsaev$5(5b+19fB@)Sw9(2Xy0nC0(5=0QsV(gXJfSfGNhcjuz-;G5BCU?Q-<;EsXW zW@?C+3}L_nL8zVUhOdN6qwB|>Yesk*Brl*19|{H{T=H9@f^=3X zZLa5(IXN#CHZk(a`%lj7YEdi|M)B7^UoWf?11yU2BRP7y zyGC)Z%DS|aQ+N$uD(SsJKIy3ixhT!8;QkOcBXoE;Ub9PxzTuBaJQ9-MqP3XaG#nnKLse53p3cgp<+Y6x zJu4LoQobbTigc|&=hyNJxG`PJAz%uST=gFqj?& zrW^Qj7-i38dOUh+I5cWtnFxdqNku9bhv=MCl#62`tPQilJFwX~&#>XE+@@*^J!xvW z!7#cl-nUxCTT&hydYT;9*M|#y2y|jh3X}Q#M`21kF z6$9b2Z4I}yJV?#ra;c`q=oX}mItmpOGaZ#r@&gWYV2qw&HlId{U}=h{!H{UgSDawf z^N7?osx_CU=11scCNn)gnaLifMO9bZUfrOJp6%6Cr)G!-kvl~{n+o{FJxPxezW<-b zJ~)H@GvEJ*&&T<`{|fjC_&RXGDi{U_!2$4l?D>BLo&hbe2J(QP1>6FDhyDIb;K$$_ z;BlaXC9nvF!6EP(_WgeXuYl*kQ{YLk1J=PJ$bm6%5F7y45bx{YZ-Ac@ya1j7PXGt- zbAnC4&k7`P9Ap6>XBs50M1e$sM1e$sM1e$sM1e$sM1dPppyxq=I24Z@vj&edvcd6H z_@DxZ7@6?cG-g|l*q){xyYLZ4@AC=&8ORRdmEkc+xX)Cay3tZ_7{A|vbP;>P`DF#$ zLF}1x`Gw#>oNuCHk1gc<8EHih9 z{~TgfS`T{X6`G38S7uwUoWl8F8XLG4Jw8cz4S$8TtXrF%8tj?xigBFjH;uH`gVw5T zV|%!ZcP(1Kmwz0k#Jp=$LF+O$jB(%E{oPu4?u$%2(XE{xJgnWi)yqLsJlH|EG)=LS z-=%+D?V4q$gjqZg{eiC8w6NMEAUR*kE#(R`{o~P(2W}jrYGSL;^Lpw;%xE%Z6xU79 z#*B7}IukRZHxacaa?it|&>nGXA_;rU;*o12+j`96v1=mfdd&8UesN_@D$VqZsfjeu JEe}U6 Date: Tue, 8 Sep 2015 19:43:30 +0900 Subject: [PATCH 10/39] WIP: merging crubyjruby specs --- spec/cruby/packer_spec.rb | 138 ------- spec/cruby/unpacker_spec.rb | 247 ------------ spec/jruby/msgpack/unpacker_spec.rb | 315 ---------------- spec/packer_spec.rb | 127 +++++++ spec/unpacker_spec.rb | 567 ++++++++++++++++++++++++++++ 5 files changed, 694 insertions(+), 700 deletions(-) delete mode 100644 spec/cruby/packer_spec.rb delete mode 100644 spec/jruby/msgpack/unpacker_spec.rb diff --git a/spec/cruby/packer_spec.rb b/spec/cruby/packer_spec.rb deleted file mode 100644 index 59fde8b1..00000000 --- a/spec/cruby/packer_spec.rb +++ /dev/null @@ -1,138 +0,0 @@ -# encoding: ascii-8bit -require 'spec_helper' - -require 'stringio' -if defined?(Encoding) - Encoding.default_external = 'ASCII-8BIT' -end - -describe Packer do - let :packer do - Packer.new - end - - it 'initialize' do - Packer.new - Packer.new(nil) - Packer.new(StringIO.new) - Packer.new({}) - Packer.new(StringIO.new, {}) - end - - #it 'Packer' do - # Packer(packer).object_id.should == packer.object_id - # Packer(nil).class.should == Packer - # Packer('').class.should == Packer - # Packer('initbuf').to_s.should == 'initbuf' - #end - - it 'write' do - packer.write([]) - packer.to_s.should == "\x90" - end - - it 'write_nil' do - packer.write_nil - packer.to_s.should == "\xc0" - end - - it 'write_array_header 0' do - packer.write_array_header(0) - packer.to_s.should == "\x90" - end - - it 'write_array_header 1' do - packer.write_array_header(1) - packer.to_s.should == "\x91" - end - - it 'write_map_header 0' do - packer.write_map_header(0) - packer.to_s.should == "\x80" - end - - it 'write_map_header 1' do - packer.write_map_header(1) - packer.to_s.should == "\x81" - end - - it 'flush' do - io = StringIO.new - pk = Packer.new(io) - pk.write_nil - pk.flush - pk.to_s.should == '' - io.string.should == "\xc0" - end - - it 'to_msgpack returns String' do - nil.to_msgpack.class.should == String - true.to_msgpack.class.should == String - false.to_msgpack.class.should == String - 1.to_msgpack.class.should == String - 1.0.to_msgpack.class.should == String - "".to_msgpack.class.should == String - Hash.new.to_msgpack.class.should == String - Array.new.to_msgpack.class.should == String - end - - class CustomPack01 - def to_msgpack(pk=nil) - return MessagePack.pack(self, pk) unless pk.class == MessagePack::Packer - pk.write_array_header(2) - pk.write(1) - pk.write(2) - return pk - end - end - - class CustomPack02 - def to_msgpack(pk=nil) - [1,2].to_msgpack(pk) - end - end - - it 'calls custom to_msgpack method' do - MessagePack.pack(CustomPack01.new).should == [1,2].to_msgpack - MessagePack.pack(CustomPack02.new).should == [1,2].to_msgpack - CustomPack01.new.to_msgpack.should == [1,2].to_msgpack - CustomPack02.new.to_msgpack.should == [1,2].to_msgpack - end - - it 'calls custom to_msgpack method with io' do - s01 = StringIO.new - MessagePack.pack(CustomPack01.new, s01) - s01.string.should == [1,2].to_msgpack - - s02 = StringIO.new - MessagePack.pack(CustomPack02.new, s02) - s02.string.should == [1,2].to_msgpack - - s03 = StringIO.new - CustomPack01.new.to_msgpack(s03) - s03.string.should == [1,2].to_msgpack - - s04 = StringIO.new - CustomPack02.new.to_msgpack(s04) - s04.string.should == [1,2].to_msgpack - end - - context 'in compatibility mode' do - it 'does not use the bin types' do - packed = MessagePack.pack('hello'.force_encoding(Encoding::BINARY), compatibility_mode: true) - packed.should eq("\xA5hello") - packed = MessagePack.pack(('hello' * 100).force_encoding(Encoding::BINARY), compatibility_mode: true) - packed.should start_with("\xDA\x01\xF4") - - packer = MessagePack::Packer.new(compatibility_mode: 1) - packed = packer.pack(('hello' * 100).force_encoding(Encoding::BINARY)) - packed.to_str.should start_with("\xDA\x01\xF4") - end - - it 'does not use the str8 type' do - packed = MessagePack.pack('x' * 32, compatibility_mode: true) - packed.should start_with("\xDA\x00\x20") - end - end -end - diff --git a/spec/cruby/unpacker_spec.rb b/spec/cruby/unpacker_spec.rb index 62ecfddb..2f55c4d1 100644 --- a/spec/cruby/unpacker_spec.rb +++ b/spec/cruby/unpacker_spec.rb @@ -10,60 +10,6 @@ Packer.new end - # TODO initialize - - it 'read_array_header succeeds' do - unpacker.feed("\x91") - unpacker.read_array_header.should == 1 - end - - it 'read_array_header fails' do - unpacker.feed("\x81") - lambda { - unpacker.read_array_header - }.should raise_error(MessagePack::TypeError) # TypeError is included in UnexpectedTypeError - lambda { - unpacker.read_array_header - }.should raise_error(MessagePack::UnexpectedTypeError) - end - - it 'read_map_header converts an map to key-value sequence' do - packer.write_array_header(2) - packer.write("e") - packer.write(1) - unpacker = Unpacker.new - unpacker.feed(packer.to_s) - unpacker.read_array_header.should == 2 - unpacker.read.should == "e" - unpacker.read.should == 1 - end - - it 'read_map_header succeeds' do - unpacker.feed("\x81") - unpacker.read_map_header.should == 1 - end - - it 'read_map_header converts an map to key-value sequence' do - packer.write_map_header(1) - packer.write("k") - packer.write("v") - unpacker = Unpacker.new - unpacker.feed(packer.to_s) - unpacker.read_map_header.should == 1 - unpacker.read.should == "k" - unpacker.read.should == "v" - end - - it 'read_map_header fails' do - unpacker.feed("\x91") - lambda { - unpacker.read_map_header - }.should raise_error(MessagePack::TypeError) # TypeError is included in UnexpectedTypeError - lambda { - unpacker.read_map_header - }.should raise_error(MessagePack::UnexpectedTypeError) - end - it 'skip_nil succeeds' do unpacker.feed("\xc0") unpacker.skip_nil.should == true @@ -91,12 +37,6 @@ unpacker.read.should == 5 end - it 'read raises EOFError' do - lambda { - unpacker.read - }.should raise_error(EOFError) - end - it 'skip raises EOFError' do lambda { unpacker.skip @@ -109,88 +49,6 @@ }.should raise_error(EOFError) end - let :sample_object do - [1024, {["a","b"]=>["c","d"]}, ["e","f"], "d", 70000, 4.12, 1.5, 1.5, 1.5] - end - - it 'feed and each continue internal state' do - raw = sample_object.to_msgpack.to_s * 4 - objects = [] - - raw.split(//).each do |b| - unpacker.feed(b) - unpacker.each {|c| - objects << c - } - end - - objects.should == [sample_object] * 4 - end - - it 'feed_each continues internal state' do - raw = sample_object.to_msgpack.to_s * 4 - objects = [] - - raw.split(//).each do |b| - unpacker.feed_each(b) {|c| - objects << c - } - end - - objects.should == [sample_object] * 4 - end - - it 'feed_each enumerator' do - raw = sample_object.to_msgpack.to_s * 4 - - unpacker.feed_each(raw).to_a.should == [sample_object] * 4 - end - - it 'reset clears internal buffer' do - # 1-element array - unpacker.feed("\x91") - unpacker.reset - unpacker.feed("\x01") - - unpacker.each.map {|x| x }.should == [1] - end - - it 'reset clears internal state' do - # 1-element array - unpacker.feed("\x91") - unpacker.each.map {|x| x }.should == [] - - unpacker.reset - - unpacker.feed("\x01") - unpacker.each.map {|x| x }.should == [1] - end - - it 'frozen short strings' do - raw = sample_object.to_msgpack.to_s.force_encoding('UTF-8') - lambda { - unpacker.feed_each(raw.freeze) { } - }.should_not raise_error - end - - it 'frozen long strings' do - raw = (sample_object.to_msgpack.to_s * 10240).force_encoding('UTF-8') - lambda { - unpacker.feed_each(raw.freeze) { } - }.should_not raise_error - end - - it 'read raises level stack too deep error' do - 512.times { packer.write_array_header(1) } - packer.write(nil) - - unpacker = Unpacker.new - unpacker.feed(packer.to_s) - lambda { - unpacker.read - }.should raise_error(MessagePack::StackError) - end - it 'skip raises level stack too deep error' do 512.times { packer.write_array_header(1) } packer.write(nil) @@ -202,116 +60,11 @@ }.should raise_error(MessagePack::StackError) end - it 'read raises invalid byte error' do - unpacker.feed("\xc1") - lambda { - unpacker.read - }.should raise_error(MessagePack::MalformedFormatError) - end - it 'skip raises invalid byte error' do unpacker.feed("\xc1") lambda { unpacker.skip }.should raise_error(MessagePack::MalformedFormatError) end - - it "gc mark" do - raw = sample_object.to_msgpack.to_s * 4 - - n = 0 - raw.split(//).each do |b| - GC.start - unpacker.feed_each(b) {|o| - GC.start - o.should == sample_object - n += 1 - } - GC.start - end - - n.should == 4 - end - - it "buffer" do - orig = "a"*32*1024*4 - raw = orig.to_msgpack.to_s - - n = 655 - times = raw.size / n - times += 1 unless raw.size % n == 0 - - off = 0 - parsed = false - - times.times do - parsed.should == false - - seg = raw[off, n] - off += seg.length - - unpacker.feed_each(seg) {|obj| - parsed.should == false - obj.should == orig - parsed = true - } - end - - parsed.should == true - end - - it 'MessagePack.unpack symbolize_keys' do - symbolized_hash = {:a => 'b', :c => 'd'} - MessagePack.load(MessagePack.pack(symbolized_hash), :symbolize_keys => true).should == symbolized_hash - MessagePack.unpack(MessagePack.pack(symbolized_hash), :symbolize_keys => true).should == symbolized_hash - end - - it 'Unpacker#unpack symbolize_keys' do - unpacker = Unpacker.new(:symbolize_keys => true) - symbolized_hash = {:a => 'b', :c => 'd'} - unpacker.feed(MessagePack.pack(symbolized_hash)).read.should == symbolized_hash - end - - it "msgpack str 8 type" do - MessagePack.unpack([0xd9, 0x00].pack('C*')).should == "" - MessagePack.unpack([0xd9, 0x00].pack('C*')).encoding.should == Encoding::UTF_8 - MessagePack.unpack([0xd9, 0x01].pack('C*') + 'a').should == "a" - MessagePack.unpack([0xd9, 0x02].pack('C*') + 'aa').should == "aa" - end - - it "msgpack str 16 type" do - MessagePack.unpack([0xda, 0x00, 0x00].pack('C*')).should == "" - MessagePack.unpack([0xda, 0x00, 0x00].pack('C*')).encoding.should == Encoding::UTF_8 - MessagePack.unpack([0xda, 0x00, 0x01].pack('C*') + 'a').should == "a" - MessagePack.unpack([0xda, 0x00, 0x02].pack('C*') + 'aa').should == "aa" - end - - it "msgpack str 32 type" do - MessagePack.unpack([0xdb, 0x00, 0x00, 0x00, 0x00].pack('C*')).should == "" - MessagePack.unpack([0xdb, 0x00, 0x00, 0x00, 0x00].pack('C*')).encoding.should == Encoding::UTF_8 - MessagePack.unpack([0xdb, 0x00, 0x00, 0x00, 0x01].pack('C*') + 'a').should == "a" - MessagePack.unpack([0xdb, 0x00, 0x00, 0x00, 0x02].pack('C*') + 'aa').should == "aa" - end - - it "msgpack bin 8 type" do - MessagePack.unpack([0xc4, 0x00].pack('C*')).should == "" - MessagePack.unpack([0xc4, 0x00].pack('C*')).encoding.should == Encoding::ASCII_8BIT - MessagePack.unpack([0xc4, 0x01].pack('C*') + 'a').should == "a" - MessagePack.unpack([0xc4, 0x02].pack('C*') + 'aa').should == "aa" - end - - it "msgpack bin 16 type" do - MessagePack.unpack([0xc5, 0x00, 0x00].pack('C*')).should == "" - MessagePack.unpack([0xc5, 0x00, 0x00].pack('C*')).encoding.should == Encoding::ASCII_8BIT - MessagePack.unpack([0xc5, 0x00, 0x01].pack('C*') + 'a').should == "a" - MessagePack.unpack([0xc5, 0x00, 0x02].pack('C*') + 'aa').should == "aa" - end - - it "msgpack bin 32 type" do - MessagePack.unpack([0xc6, 0x00, 0x00, 0x00, 0x00].pack('C*')).should == "" - MessagePack.unpack([0xc6, 0x0, 0x00, 0x00, 0x000].pack('C*')).encoding.should == Encoding::ASCII_8BIT - MessagePack.unpack([0xc6, 0x00, 0x00, 0x00, 0x01].pack('C*') + 'a').should == "a" - MessagePack.unpack([0xc6, 0x00, 0x00, 0x00, 0x02].pack('C*') + 'aa').should == "aa" - end end diff --git a/spec/jruby/msgpack/unpacker_spec.rb b/spec/jruby/msgpack/unpacker_spec.rb deleted file mode 100644 index bc620783..00000000 --- a/spec/jruby/msgpack/unpacker_spec.rb +++ /dev/null @@ -1,315 +0,0 @@ -# encoding: ascii-8bit - -require 'stringio' -require 'tempfile' -require 'spec_helper' - - -describe ::MessagePack::Unpacker do - def flatten(struct, results = []) - case struct - when Array - struct.each { |v| flatten(v, results) } - when Hash - struct.each { |k, v| flatten(v, flatten(k, results)) } - else - results << struct - end - results - end - - subject do - described_class.new - end - - let :buffer1 do - MessagePack.pack(:foo => 'bar') - end - - let :buffer2 do - MessagePack.pack(:hello => {:world => [1, 2, 3]}) - end - - let :buffer3 do - MessagePack.pack(:x => 'y') - end - - describe '#execute/#execute_limit/#finished?' do - let :buffer do - buffer1 + buffer2 + buffer3 - end - - it 'extracts an object from the buffer' do - subject.execute(buffer, 0) - subject.data.should == {'foo' => 'bar'} - end - - it 'extracts an object from the buffer, starting at an offset' do - subject.execute(buffer, buffer1.length) - subject.data.should == {'hello' => {'world' => [1, 2, 3]}} - end - - it 'extracts an object from the buffer, starting at an offset reading bytes up to a limit' do - subject.execute_limit(buffer, buffer1.length, buffer2.length) - subject.data.should == {'hello' => {'world' => [1, 2, 3]}} - end - - it 'extracts nothing if the limit cuts an object in half' do - subject.execute_limit(buffer, buffer1.length, 3) - subject.data.should be_nil - end - - it 'returns the offset where the object ended' do - subject.execute(buffer, 0).should == buffer1.length - subject.execute(buffer, buffer1.length).should == buffer1.length + buffer2.length - end - - it 'is finished if #data returns an object' do - subject.execute_limit(buffer, buffer1.length, buffer2.length) - subject.should be_finished - - subject.execute_limit(buffer, buffer1.length, 3) - subject.should_not be_finished - end - end - - describe '#read' do - context 'with a buffer' do - it 'reads objects' do - objects = [] - subject.feed(buffer1) - subject.feed(buffer2) - subject.feed(buffer3) - objects << subject.read - objects << subject.read - objects << subject.read - objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] - end - - it 'reads map header' do - subject.feed({}.to_msgpack) - subject.read_map_header.should == 0 - end - - it 'reads array header' do - subject.feed([].to_msgpack) - subject.read_array_header.should == 0 - end - end - end - - describe '#each' do - context 'with a buffer' do - it 'yields each object in the buffer' do - objects = [] - subject.feed(buffer1) - subject.feed(buffer2) - subject.feed(buffer3) - subject.each do |obj| - objects << obj - end - objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] - end - - it 'returns an enumerator when no block is given' do - subject.feed(buffer1) - subject.feed(buffer2) - subject.feed(buffer3) - enum = subject.each - enum.map { |obj| obj.keys.first }.should == %w[foo hello x] - end - end - - context 'with a StringIO stream' do - it 'yields each object in the stream' do - objects = [] - subject.stream = StringIO.new(buffer1 + buffer2 + buffer3) - subject.each do |obj| - objects << obj - end - objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] - end - end - - context 'with a File stream' do - it 'yields each object in the stream' do - objects = [] - file = Tempfile.new('msgpack') - file.write(buffer1) - file.write(buffer2) - file.write(buffer3) - file.open - subject.stream = file - subject.each do |obj| - objects << obj - end - objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] - end - end - - context 'with a stream passed to the constructor' do - it 'yields each object in the stream' do - objects = [] - unpacker = described_class.new(StringIO.new(buffer1 + buffer2 + buffer3)) - unpacker.each do |obj| - objects << obj - end - objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] - end - end - end - - describe '#feed_each' do - it 'feeds the buffer then runs #each' do - objects = [] - subject.feed_each(buffer1 + buffer2 + buffer3) do |obj| - objects << obj - end - objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] - end - - it 'handles chunked data' do - objects = [] - buffer = buffer1 + buffer2 + buffer3 - buffer.chars.each do |ch| - subject.feed_each(ch) do |obj| - objects << obj - end - end - objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] - end - end - - describe '#fill' do - it 'is a no-op' do - subject.stream = StringIO.new(buffer1 + buffer2 + buffer3) - subject.fill - subject.each { |obj| } - end - end - - describe '#reset' do - context 'with a buffer' do - it 'is unclear what it is supposed to do' - end - - context 'with a stream' do - it 'is unclear what it is supposed to do' - end - end - - context 'regressions' do - it 'handles massive arrays (issue #2)' do - array = ['foo'] * 10_000 - MessagePack.unpack(MessagePack.pack(array)).should have(10_000).items - end - end - - context 'encoding', :encodings do - before :all do - @default_internal = Encoding.default_internal - @default_external = Encoding.default_external - end - - after :all do - Encoding.default_internal = @default_internal - Encoding.default_external = @default_external - end - - let :buffer do - MessagePack.pack({'hello' => 'world', 'nested' => ['object', {"sk\xC3\xA5l".force_encoding('utf-8') => true}]}) - end - - let :unpacker do - described_class.new - end - - before do - Encoding.default_internal = Encoding::UTF_8 - Encoding.default_external = Encoding::ISO_8859_1 - end - - it 'produces results with default internal encoding' do - unpacker.execute(buffer, 0) - strings = flatten(unpacker.data).grep(String) - strings.map(&:encoding).uniq.should == [Encoding.default_internal] - end - - it 'recodes to internal encoding' do - unpacker.execute(buffer, 0) - unpacker.data['nested'][1].keys.should == ["sk\xC3\xA5l".force_encoding(Encoding.default_internal)] - end - end - - context 'extensions' do - context 'symbolized keys' do - let :buffer do - MessagePack.pack({'hello' => 'world', 'nested' => ['object', {'structure' => true}]}) - end - - let :unpacker do - described_class.new(:symbolize_keys => true) - end - - it 'can symbolize keys when using #execute' do - unpacker.execute(buffer, 0) - unpacker.data.should == {:hello => 'world', :nested => ['object', {:structure => true}]} - end - - it 'can symbolize keys when using #each' do - objs = [] - unpacker.feed(buffer) - unpacker.each do |obj| - objs << obj - end - objs.should == [{:hello => 'world', :nested => ['object', {:structure => true}]}] - end - - it 'can symbolize keys when using #feed_each' do - objs = [] - unpacker.feed_each(buffer) do |obj| - objs << obj - end - objs.should == [{:hello => 'world', :nested => ['object', {:structure => true}]}] - end - end - - context 'encoding', :encodings do - let :buffer do - MessagePack.pack({'hello' => 'world', 'nested' => ['object', {'structure' => true}]}) - end - - let :unpacker do - described_class.new(:encoding => 'UTF-8') - end - - it 'can hardcode encoding when using #execute' do - unpacker.execute(buffer, 0) - strings = flatten(unpacker.data).grep(String) - strings.should == %w[hello world nested object structure] - strings.map(&:encoding).uniq.should == [Encoding::UTF_8] - end - - it 'can hardcode encoding when using #each' do - objs = [] - unpacker.feed(buffer) - unpacker.each do |obj| - objs << obj - end - strings = flatten(objs).grep(String) - strings.should == %w[hello world nested object structure] - strings.map(&:encoding).uniq.should == [Encoding::UTF_8] - end - - it 'can hardcode encoding when using #feed_each' do - objs = [] - unpacker.feed_each(buffer) do |obj| - objs << obj - end - strings = flatten(objs).grep(String) - strings.should == %w[hello world nested object structure] - strings.map(&:encoding).uniq.should == [Encoding::UTF_8] - end - end - end -end diff --git a/spec/packer_spec.rb b/spec/packer_spec.rb index 952a6b6f..8a112b7e 100644 --- a/spec/packer_spec.rb +++ b/spec/packer_spec.rb @@ -1,6 +1,133 @@ +# encoding: ascii-8bit require 'spec_helper' +require 'stringio' +if defined?(Encoding) + Encoding.default_external = 'ASCII-8BIT' +end + describe MessagePack::Packer do + let :packer do + MessagePack::Packer.new + end + + it 'initialize' do + MessagePack::Packer.new + MessagePack::Packer.new(nil) + MessagePack::Packer.new(StringIO.new) + MessagePack::Packer.new({}) + MessagePack::Packer.new(StringIO.new, {}) + end + + it 'write' do + packer.write([]) + packer.to_s.should == "\x90" + end + + it 'write_nil' do + packer.write_nil + packer.to_s.should == "\xc0" + end + + it 'write_array_header 0' do + packer.write_array_header(0) + packer.to_s.should == "\x90" + end + + it 'write_array_header 1' do + packer.write_array_header(1) + packer.to_s.should == "\x91" + end + + it 'write_map_header 0' do + packer.write_map_header(0) + packer.to_s.should == "\x80" + end + + it 'write_map_header 1' do + packer.write_map_header(1) + packer.to_s.should == "\x81" + end + + it 'flush' do + io = StringIO.new + pk = MessagePack::Packer.new(io) + pk.write_nil + pk.flush + pk.to_s.should == '' + io.string.should == "\xc0" + end + + it 'to_msgpack returns String' do + nil.to_msgpack.class.should == String + true.to_msgpack.class.should == String + false.to_msgpack.class.should == String + 1.to_msgpack.class.should == String + 1.0.to_msgpack.class.should == String + "".to_msgpack.class.should == String + Hash.new.to_msgpack.class.should == String + Array.new.to_msgpack.class.should == String + end + + class CustomPack01 + def to_msgpack(pk=nil) + return MessagePack.pack(self, pk) unless pk.class == MessagePack::Packer + pk.write_array_header(2) + pk.write(1) + pk.write(2) + return pk + end + end + + class CustomPack02 + def to_msgpack(pk=nil) + [1,2].to_msgpack(pk) + end + end + + it 'calls custom to_msgpack method' do + MessagePack.pack(CustomPack01.new).should == [1,2].to_msgpack + MessagePack.pack(CustomPack02.new).should == [1,2].to_msgpack + CustomPack01.new.to_msgpack.should == [1,2].to_msgpack + CustomPack02.new.to_msgpack.should == [1,2].to_msgpack + end + + it 'calls custom to_msgpack method with io' do + s01 = StringIO.new + MessagePack.pack(CustomPack01.new, s01) + s01.string.should == [1,2].to_msgpack + + s02 = StringIO.new + MessagePack.pack(CustomPack02.new, s02) + s02.string.should == [1,2].to_msgpack + + s03 = StringIO.new + CustomPack01.new.to_msgpack(s03) + s03.string.should == [1,2].to_msgpack + + s04 = StringIO.new + CustomPack02.new.to_msgpack(s04) + s04.string.should == [1,2].to_msgpack + end + + context 'in compatibility mode' do + it 'does not use the bin types' do + packed = MessagePack.pack('hello'.force_encoding(Encoding::BINARY), compatibility_mode: true) + packed.should eq("\xA5hello") + packed = MessagePack.pack(('hello' * 100).force_encoding(Encoding::BINARY), compatibility_mode: true) + packed.should start_with("\xDA\x01\xF4") + + packer = MessagePack::Packer.new(compatibility_mode: 1) + packed = packer.pack(('hello' * 100).force_encoding(Encoding::BINARY)) + packed.to_str.should start_with("\xDA\x01\xF4") + end + + it 'does not use the str8 type' do + packed = MessagePack.pack('x' * 32, compatibility_mode: true) + packed.should start_with("\xDA\x00\x20") + end + end + class ValueOne def initialize(num) @num = num diff --git a/spec/unpacker_spec.rb b/spec/unpacker_spec.rb index 702614df..360c262e 100644 --- a/spec/unpacker_spec.rb +++ b/spec/unpacker_spec.rb @@ -1,6 +1,266 @@ +# encoding: ascii-8bit + +require 'stringio' +require 'tempfile' + require 'spec_helper' describe MessagePack::Unpacker do + let :unpacker do + MessagePack::Unpacker.new + end + + let :packer do + MessagePack::Packer.new + end + + # TODO initialize + + it 'read_array_header succeeds' do + unpacker.feed("\x91") + unpacker.read_array_header.should == 1 + end + + it 'read_array_header fails' do + unpacker.feed("\x81") + lambda { + unpacker.read_array_header + }.should raise_error(MessagePack::TypeError) # TypeError is included in UnexpectedTypeError + lambda { + unpacker.read_array_header + }.should raise_error(MessagePack::UnexpectedTypeError) + end + + it 'read_map_header converts an map to key-value sequence' do + packer.write_array_header(2) + packer.write("e") + packer.write(1) + unpacker = MessagePack::Unpacker.new + unpacker.feed(packer.to_s) + unpacker.read_array_header.should == 2 + unpacker.read.should == "e" + unpacker.read.should == 1 + end + + it 'read_map_header succeeds' do + unpacker.feed("\x81") + unpacker.read_map_header.should == 1 + end + + it 'read_map_header converts an map to key-value sequence' do + packer.write_map_header(1) + packer.write("k") + packer.write("v") + unpacker = MessagePack::Unpacker.new + unpacker.feed(packer.to_s) + unpacker.read_map_header.should == 1 + unpacker.read.should == "k" + unpacker.read.should == "v" + end + + it 'read_map_header fails' do + unpacker.feed("\x91") + lambda { + unpacker.read_map_header + }.should raise_error(MessagePack::TypeError) # TypeError is included in UnexpectedTypeError + lambda { + unpacker.read_map_header + }.should raise_error(MessagePack::UnexpectedTypeError) + end + + it 'read raises EOFError' do + lambda { + unpacker.read + }.should raise_error(EOFError) + end + + let :sample_object do + [1024, {["a","b"]=>["c","d"]}, ["e","f"], "d", 70000, 4.12, 1.5, 1.5, 1.5] + end + + it 'feed and each continue internal state' do + raw = sample_object.to_msgpack.to_s * 4 + objects = [] + + raw.split(//).each do |b| + unpacker.feed(b) + unpacker.each {|c| + objects << c + } + end + + objects.should == [sample_object] * 4 + end + + it 'feed_each continues internal state' do + raw = sample_object.to_msgpack.to_s * 4 + objects = [] + + raw.split(//).each do |b| + unpacker.feed_each(b) {|c| + objects << c + } + end + + objects.should == [sample_object] * 4 + end + + it 'feed_each enumerator' do + raw = sample_object.to_msgpack.to_s * 4 + + unpacker.feed_each(raw).to_a.should == [sample_object] * 4 + end + + it 'reset clears internal buffer' do + # 1-element array + unpacker.feed("\x91") + unpacker.reset + unpacker.feed("\x01") + + unpacker.each.map {|x| x }.should == [1] + end + + it 'reset clears internal state' do + # 1-element array + unpacker.feed("\x91") + unpacker.each.map {|x| x }.should == [] + + unpacker.reset + + unpacker.feed("\x01") + unpacker.each.map {|x| x }.should == [1] + end + + it 'frozen short strings' do + raw = sample_object.to_msgpack.to_s.force_encoding('UTF-8') + lambda { + unpacker.feed_each(raw.freeze) { } + }.should_not raise_error + end + + it 'frozen long strings' do + raw = (sample_object.to_msgpack.to_s * 10240).force_encoding('UTF-8') + lambda { + unpacker.feed_each(raw.freeze) { } + }.should_not raise_error + end + + it 'read raises level stack too deep error' do + 512.times { packer.write_array_header(1) } + packer.write(nil) + + unpacker = MessagePack::Unpacker.new + unpacker.feed(packer.to_s) + lambda { + unpacker.read + }.should raise_error(MessagePack::StackError) + end + + it 'read raises invalid byte error' do + unpacker.feed("\xc1") + lambda { + unpacker.read + }.should raise_error(MessagePack::MalformedFormatError) + end + + it "gc mark" do + raw = sample_object.to_msgpack.to_s * 4 + + n = 0 + raw.split(//).each do |b| + GC.start + unpacker.feed_each(b) {|o| + GC.start + o.should == sample_object + n += 1 + } + GC.start + end + + n.should == 4 + end + + it "buffer" do + orig = "a"*32*1024*4 + raw = orig.to_msgpack.to_s + + n = 655 + times = raw.size / n + times += 1 unless raw.size % n == 0 + + off = 0 + parsed = false + + times.times do + parsed.should == false + + seg = raw[off, n] + off += seg.length + + unpacker.feed_each(seg) {|obj| + parsed.should == false + obj.should == orig + parsed = true + } + end + + parsed.should == true + end + + it 'MessagePack.unpack symbolize_keys' do + symbolized_hash = {:a => 'b', :c => 'd'} + MessagePack.load(MessagePack.pack(symbolized_hash), :symbolize_keys => true).should == symbolized_hash + MessagePack.unpack(MessagePack.pack(symbolized_hash), :symbolize_keys => true).should == symbolized_hash + end + + it 'Unpacker#unpack symbolize_keys' do + unpacker = MessagePack::Unpacker.new(:symbolize_keys => true) + symbolized_hash = {:a => 'b', :c => 'd'} + unpacker.feed(MessagePack.pack(symbolized_hash)).read.should == symbolized_hash + end + + it "msgpack str 8 type" do + MessagePack.unpack([0xd9, 0x00].pack('C*')).should == "" + MessagePack.unpack([0xd9, 0x00].pack('C*')).encoding.should == Encoding::UTF_8 + MessagePack.unpack([0xd9, 0x01].pack('C*') + 'a').should == "a" + MessagePack.unpack([0xd9, 0x02].pack('C*') + 'aa').should == "aa" + end + + it "msgpack str 16 type" do + MessagePack.unpack([0xda, 0x00, 0x00].pack('C*')).should == "" + MessagePack.unpack([0xda, 0x00, 0x00].pack('C*')).encoding.should == Encoding::UTF_8 + MessagePack.unpack([0xda, 0x00, 0x01].pack('C*') + 'a').should == "a" + MessagePack.unpack([0xda, 0x00, 0x02].pack('C*') + 'aa').should == "aa" + end + + it "msgpack str 32 type" do + MessagePack.unpack([0xdb, 0x00, 0x00, 0x00, 0x00].pack('C*')).should == "" + MessagePack.unpack([0xdb, 0x00, 0x00, 0x00, 0x00].pack('C*')).encoding.should == Encoding::UTF_8 + MessagePack.unpack([0xdb, 0x00, 0x00, 0x00, 0x01].pack('C*') + 'a').should == "a" + MessagePack.unpack([0xdb, 0x00, 0x00, 0x00, 0x02].pack('C*') + 'aa').should == "aa" + end + + it "msgpack bin 8 type" do + MessagePack.unpack([0xc4, 0x00].pack('C*')).should == "" + MessagePack.unpack([0xc4, 0x00].pack('C*')).encoding.should == Encoding::ASCII_8BIT + MessagePack.unpack([0xc4, 0x01].pack('C*') + 'a').should == "a" + MessagePack.unpack([0xc4, 0x02].pack('C*') + 'aa').should == "aa" + end + + it "msgpack bin 16 type" do + MessagePack.unpack([0xc5, 0x00, 0x00].pack('C*')).should == "" + MessagePack.unpack([0xc5, 0x00, 0x00].pack('C*')).encoding.should == Encoding::ASCII_8BIT + MessagePack.unpack([0xc5, 0x00, 0x01].pack('C*') + 'a').should == "a" + MessagePack.unpack([0xc5, 0x00, 0x02].pack('C*') + 'aa').should == "aa" + end + + it "msgpack bin 32 type" do + MessagePack.unpack([0xc6, 0x00, 0x00, 0x00, 0x00].pack('C*')).should == "" + MessagePack.unpack([0xc6, 0x0, 0x00, 0x00, 0x000].pack('C*')).encoding.should == Encoding::ASCII_8BIT + MessagePack.unpack([0xc6, 0x00, 0x00, 0x00, 0x01].pack('C*') + 'a').should == "a" + MessagePack.unpack([0xc6, 0x00, 0x00, 0x00, 0x02].pack('C*') + 'aa').should == "aa" + end + class ValueOne attr_reader :num def initialize(num) @@ -124,4 +384,311 @@ def self.from_msgpack_ext(data) expect(two[:unpacker]).to be_instance_of(Proc) end end + + def flatten(struct, results = []) + case struct + when Array + struct.each { |v| flatten(v, results) } + when Hash + struct.each { |k, v| flatten(v, flatten(k, results)) } + else + results << struct + end + results + end + + subject do + described_class.new + end + + let :buffer1 do + MessagePack.pack(:foo => 'bar') + end + + let :buffer2 do + MessagePack.pack(:hello => {:world => [1, 2, 3]}) + end + + let :buffer3 do + MessagePack.pack(:x => 'y') + end + + describe '#execute/#execute_limit/#finished?' do + let :buffer do + buffer1 + buffer2 + buffer3 + end + + it 'extracts an object from the buffer' do + subject.execute(buffer, 0) + subject.data.should == {'foo' => 'bar'} + end + + it 'extracts an object from the buffer, starting at an offset' do + subject.execute(buffer, buffer1.length) + subject.data.should == {'hello' => {'world' => [1, 2, 3]}} + end + + it 'extracts an object from the buffer, starting at an offset reading bytes up to a limit' do + subject.execute_limit(buffer, buffer1.length, buffer2.length) + subject.data.should == {'hello' => {'world' => [1, 2, 3]}} + end + + it 'extracts nothing if the limit cuts an object in half' do + subject.execute_limit(buffer, buffer1.length, 3) + subject.data.should be_nil + end + + it 'returns the offset where the object ended' do + subject.execute(buffer, 0).should == buffer1.length + subject.execute(buffer, buffer1.length).should == buffer1.length + buffer2.length + end + + it 'is finished if #data returns an object' do + subject.execute_limit(buffer, buffer1.length, buffer2.length) + subject.should be_finished + + subject.execute_limit(buffer, buffer1.length, 3) + subject.should_not be_finished + end + end + + describe '#read' do + context 'with a buffer' do + it 'reads objects' do + objects = [] + subject.feed(buffer1) + subject.feed(buffer2) + subject.feed(buffer3) + objects << subject.read + objects << subject.read + objects << subject.read + objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] + end + + it 'reads map header' do + subject.feed({}.to_msgpack) + subject.read_map_header.should == 0 + end + + it 'reads array header' do + subject.feed([].to_msgpack) + subject.read_array_header.should == 0 + end + end + end + + describe '#each' do + context 'with a buffer' do + it 'yields each object in the buffer' do + objects = [] + subject.feed(buffer1) + subject.feed(buffer2) + subject.feed(buffer3) + subject.each do |obj| + objects << obj + end + objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] + end + + it 'returns an enumerator when no block is given' do + subject.feed(buffer1) + subject.feed(buffer2) + subject.feed(buffer3) + enum = subject.each + enum.map { |obj| obj.keys.first }.should == %w[foo hello x] + end + end + + context 'with a StringIO stream' do + it 'yields each object in the stream' do + objects = [] + subject.stream = StringIO.new(buffer1 + buffer2 + buffer3) + subject.each do |obj| + objects << obj + end + objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] + end + end + + context 'with a File stream' do + it 'yields each object in the stream' do + objects = [] + file = Tempfile.new('msgpack') + file.write(buffer1) + file.write(buffer2) + file.write(buffer3) + file.open + subject.stream = file + subject.each do |obj| + objects << obj + end + objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] + end + end + + context 'with a stream passed to the constructor' do + it 'yields each object in the stream' do + objects = [] + unpacker = described_class.new(StringIO.new(buffer1 + buffer2 + buffer3)) + unpacker.each do |obj| + objects << obj + end + objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] + end + end + end + + describe '#feed_each' do + it 'feeds the buffer then runs #each' do + objects = [] + subject.feed_each(buffer1 + buffer2 + buffer3) do |obj| + objects << obj + end + objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] + end + + it 'handles chunked data' do + objects = [] + buffer = buffer1 + buffer2 + buffer3 + buffer.chars.each do |ch| + subject.feed_each(ch) do |obj| + objects << obj + end + end + objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] + end + end + + describe '#fill' do + it 'is a no-op' do + subject.stream = StringIO.new(buffer1 + buffer2 + buffer3) + subject.fill + subject.each { |obj| } + end + end + + describe '#reset' do + context 'with a buffer' do + it 'is unclear what it is supposed to do' + end + + context 'with a stream' do + it 'is unclear what it is supposed to do' + end + end + + context 'regressions' do + it 'handles massive arrays (issue #2)' do + array = ['foo'] * 10_000 + MessagePack.unpack(MessagePack.pack(array)).should have(10_000).items + end + end + + context 'encoding', :encodings do + before :all do + @default_internal = Encoding.default_internal + @default_external = Encoding.default_external + end + + after :all do + Encoding.default_internal = @default_internal + Encoding.default_external = @default_external + end + + let :buffer do + MessagePack.pack({'hello' => 'world', 'nested' => ['object', {"sk\xC3\xA5l".force_encoding('utf-8') => true}]}) + end + + let :unpacker do + described_class.new + end + + before do + Encoding.default_internal = Encoding::UTF_8 + Encoding.default_external = Encoding::ISO_8859_1 + end + + it 'produces results with default internal encoding' do + unpacker.execute(buffer, 0) + strings = flatten(unpacker.data).grep(String) + strings.map(&:encoding).uniq.should == [Encoding.default_internal] + end + + it 'recodes to internal encoding' do + unpacker.execute(buffer, 0) + unpacker.data['nested'][1].keys.should == ["sk\xC3\xA5l".force_encoding(Encoding.default_internal)] + end + end + + context 'extensions' do + context 'symbolized keys' do + let :buffer do + MessagePack.pack({'hello' => 'world', 'nested' => ['object', {'structure' => true}]}) + end + + let :unpacker do + described_class.new(:symbolize_keys => true) + end + + it 'can symbolize keys when using #execute' do + unpacker.execute(buffer, 0) + unpacker.data.should == {:hello => 'world', :nested => ['object', {:structure => true}]} + end + + it 'can symbolize keys when using #each' do + objs = [] + unpacker.feed(buffer) + unpacker.each do |obj| + objs << obj + end + objs.should == [{:hello => 'world', :nested => ['object', {:structure => true}]}] + end + + it 'can symbolize keys when using #feed_each' do + objs = [] + unpacker.feed_each(buffer) do |obj| + objs << obj + end + objs.should == [{:hello => 'world', :nested => ['object', {:structure => true}]}] + end + end + + context 'encoding', :encodings do + let :buffer do + MessagePack.pack({'hello' => 'world', 'nested' => ['object', {'structure' => true}]}) + end + + let :unpacker do + described_class.new(:encoding => 'UTF-8') + end + + it 'can hardcode encoding when using #execute' do + unpacker.execute(buffer, 0) + strings = flatten(unpacker.data).grep(String) + strings.should == %w[hello world nested object structure] + strings.map(&:encoding).uniq.should == [Encoding::UTF_8] + end + + it 'can hardcode encoding when using #each' do + objs = [] + unpacker.feed(buffer) + unpacker.each do |obj| + objs << obj + end + strings = flatten(objs).grep(String) + strings.should == %w[hello world nested object structure] + strings.map(&:encoding).uniq.should == [Encoding::UTF_8] + end + + it 'can hardcode encoding when using #feed_each' do + objs = [] + unpacker.feed_each(buffer) do |obj| + objs << obj + end + strings = flatten(objs).grep(String) + strings.should == %w[hello world nested object structure] + strings.map(&:encoding).uniq.should == [Encoding::UTF_8] + end + end + end end From 130be0cac5fd051f84c0f61c54d7a72436217704 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Wed, 9 Sep 2015 17:32:26 +0900 Subject: [PATCH 11/39] ignore vim swp files --- .gitignore | 1 + ext/msgpack/.unpacker.c.swp | Bin 16384 -> 0 bytes 2 files changed, 1 insertion(+) delete mode 100644 ext/msgpack/.unpacker.c.swp diff --git a/.gitignore b/.gitignore index f406b2b0..023c8d07 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ Gemfile* pkg test/debug.log *~ +*.swp /rdoc tmp .classpath diff --git a/ext/msgpack/.unpacker.c.swp b/ext/msgpack/.unpacker.c.swp deleted file mode 100644 index adb3bf980d864a551a25e92cd2db5c80a177e6ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI2Uu+{s9mj`qcfEs_B6y>;o#v|G8rURGvTEX_wsWVH!fL%cwinyG zzWsBF8}5)QD#59h`qCG8;iV_&0|ilmprTSa0Tn#K6C4kqN>Bupdw^D=?NPq7J8Q4K zadN%#0$NL-IG*{$FOt_@A;eXyR@1T# zhu7`#DoUlb;<7w$TH~v#HfLG&amlbJ8kVLuqRlRL9Hy&=Tl324rc-UHT7BI1%I!4U zcEf_SXYz7pPiRpG$^VH0i30CKf!oRP$!vx@I68DM{rJZ=-iNQGX`(=)K%zjRK%zjR zK%zjRK%zjR!2g;8ZtF1l5`w-(1pZa={(-*tcg5>*QGThf{@F z+kN%#i2Aaqf3>gvZBc(g)GvsDL;r7ydQa59+*kiWQGQaqPlB=je7}t6|Ka}s`W=M)5xfds0N(|5a36T{ zBZT||yaZkZ=fDWKdOIOcgF2{!3RnlT;7;%f@T=Qk4>mv^tb$|U2sjMh{4gPJfUDs9 z;Cb+Ea0z?`G{F-14EQv-2iyfd3Eub+;sU<`&x3D*%ivk?1o$#o2YGN0xEowMLdf62 z&%jT?x4={2NpKF#f_D!S@-Of;FbhtC>$ei}Hh2qM0WX8gzylU|1QbC5P;fW63;g8} zA%6xx2baOKU>i({{Obat zc-hSLY?GE}{7*p{FE7&vQK#xkjsGdPU6%6dBY~AsNul~dx}1|1l)24PPATLb-jEBq zg%P^r`!P({_u$pXXFCpN$PRH_)ipF~m<_{Zcyno$xsg7^4^ouy_a}-wO3^PNJ<;~K z<$?A`FGjUqkXPlBd?u&l))qUl+dTI1Y&2FB(Xi{XSE(>t@duwxZRFRb`G;}^Wo~0} z5pP@t$PgKc+pkmTsDBLUQOdQTTufT=OwBbc)1jkj%waG*J}%y`VOAAe-9ija)iCi1 z;bnsO`%F;ruxT{9bq&>V6|4LOrn#Lue@b{msIS!(oweLr*JFCL$(jyxF*0E>Ml)m7 zF&<-9B^4jc2<21K<7{Mx4+2@^S9B&EeZ*5_CNK>B3bnNcQ|-ZGpSKL1Dq)+Bur}jy z$BO%MrmR#orK#F=e}tXst%z*Kw1gf+oo~DQ#PiwW<3yt(V|0}{j#_2wT-LcDZ?wcf?ZWPY=Y@M}5@7$`lE|@$~U9s%c40Y&f>M&Esf5(o6c^d}ZQ6rdL44#`p z`_3fMW8MFQ*$?t%4-e7iV#Xp-a?g=G6|wdiL^2TuJH(&ESeL%mvC`fd46DWT=*)3# zt34=N$WaX6s1UAf#b$in63L1%jqMt$Dt>ckxNT^^T3q7Gj?&lRyo6G@w8_WO8Dqx* z+BMY%e)X5yr#ZgBgTCD&hxii5^-GUNz2oPEH{gBn($RH}Z&7z91}s4P48mV?c#P2q zL#_Qu4WjLt)xD>Zgzh4_4N>320(&z{9~O9bU@r5BI?MAu>oNu>zGd-b^g9XFiDlwZ zBsaev$5(5b+19fB@)Sw9(2Xy0nC0(5=0QsV(gXJfSfGNhcjuz-;G5BCU?Q-<;EsXW zW@?C+3}L_nL8zVUhOdN6qwB|>Yesk*Brl*19|{H{T=H9@f^=3X zZLa5(IXN#CHZk(a`%lj7YEdi|M)B7^UoWf?11yU2BRP7y zyGC)Z%DS|aQ+N$uD(SsJKIy3ixhT!8;QkOcBXoE;Ub9PxzTuBaJQ9-MqP3XaG#nnKLse53p3cgp<+Y6x zJu4LoQobbTigc|&=hyNJxG`PJAz%uST=gFqj?& zrW^Qj7-i38dOUh+I5cWtnFxdqNku9bhv=MCl#62`tPQilJFwX~&#>XE+@@*^J!xvW z!7#cl-nUxCTT&hydYT;9*M|#y2y|jh3X}Q#M`21kF z6$9b2Z4I}yJV?#ra;c`q=oX}mItmpOGaZ#r@&gWYV2qw&HlId{U}=h{!H{UgSDawf z^N7?osx_CU=11scCNn)gnaLifMO9bZUfrOJp6%6Cr)G!-kvl~{n+o{FJxPxezW<-b zJ~)H@GvEJ*&&T<`{|fjC_&RXGDi{U_!2$4l?D>BLo&hbe2J(QP1>6FDhyDIb;K$$_ z;BlaXC9nvF!6EP(_WgeXuYl*kQ{YLk1J=PJ$bm6%5F7y45bx{YZ-Ac@ya1j7PXGt- zbAnC4&k7`P9Ap6>XBs50M1e$sM1e$sM1e$sM1e$sM1dPppyxq=I24Z@vj&edvcd6H z_@DxZ7@6?cG-g|l*q){xyYLZ4@AC=&8ORRdmEkc+xX)Cay3tZ_7{A|vbP;>P`DF#$ zLF}1x`Gw#>oNuCHk1gc<8EHih9 z{~TgfS`T{X6`G38S7uwUoWl8F8XLG4Jw8cz4S$8TtXrF%8tj?xigBFjH;uH`gVw5T zV|%!ZcP(1Kmwz0k#Jp=$LF+O$jB(%E{oPu4?u$%2(XE{xJgnWi)yqLsJlH|EG)=LS z-=%+D?V4q$gjqZg{eiC8w6NMEAUR*kE#(R`{o~P(2W}jrYGSL;^Lpw;%xE%Z6xU79 z#*B7}IukRZHxacaa?it|&>nGXA_;rU;*o12+j`96v1=mfdd&8UesN_@D$VqZsfjeu JEe}U6 Date: Wed, 9 Sep 2015 17:47:07 +0900 Subject: [PATCH 12/39] improve compatibility: raise EOFError for reading before feeding --- ext/java/org/msgpack/jruby/Unpacker.java | 20 ++++++++++---------- spec/unpacker_spec.rb | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Unpacker.java b/ext/java/org/msgpack/jruby/Unpacker.java index aadb64a8..8c235314 100644 --- a/ext/java/org/msgpack/jruby/Unpacker.java +++ b/ext/java/org/msgpack/jruby/Unpacker.java @@ -257,18 +257,18 @@ public IRubyObject reset(ThreadContext ctx) { @JRubyMethod(name = "read", alias = { "unpack" }) public IRubyObject read(ThreadContext ctx) { - if (decoder != null) { - try { - return decoder.next(); - } catch (RaiseException re) { - if (re.getException().getType() != underflowErrorClass) { - throw re; - } else { - throw ctx.getRuntime().newEOFError(); - } + if (decoder == null) { + throw ctx.getRuntime().newEOFError(); + } + try { + return decoder.next(); + } catch (RaiseException re) { + if (re.getException().getType() != underflowErrorClass) { + throw re; + } else { + throw ctx.getRuntime().newEOFError(); } } - return ctx.getRuntime().getNil(); } /* diff --git a/spec/unpacker_spec.rb b/spec/unpacker_spec.rb index 360c262e..eae74dea 100644 --- a/spec/unpacker_spec.rb +++ b/spec/unpacker_spec.rb @@ -68,7 +68,7 @@ }.should raise_error(MessagePack::UnexpectedTypeError) end - it 'read raises EOFError' do + it 'read raises EOFError before feeding' do lambda { unpacker.read }.should raise_error(EOFError) From f580c475699e3343ae855400e6d1aba340093089 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Wed, 9 Sep 2015 17:57:18 +0900 Subject: [PATCH 13/39] improve compatibility: Unpacker#feed_each without blocks should return Enumerator --- ext/java/org/msgpack/jruby/Unpacker.java | 8 ++++++-- spec/unpacker_spec.rb | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Unpacker.java b/ext/java/org/msgpack/jruby/Unpacker.java index 8c235314..dbeff86c 100644 --- a/ext/java/org/msgpack/jruby/Unpacker.java +++ b/ext/java/org/msgpack/jruby/Unpacker.java @@ -218,8 +218,12 @@ public IRubyObject feed(ThreadContext ctx, IRubyObject data) { @JRubyMethod(name = "feed_each", required = 1) public IRubyObject feedEach(ThreadContext ctx, IRubyObject data, Block block) { feed(ctx, data); - each(ctx, block); - return ctx.getRuntime().getNil(); + if (block.isGiven()) { + each(ctx, block); + return ctx.getRuntime().getNil(); + } else { + return callMethod(ctx, "to_enum"); + } } @JRubyMethod diff --git a/spec/unpacker_spec.rb b/spec/unpacker_spec.rb index eae74dea..4140a25a 100644 --- a/spec/unpacker_spec.rb +++ b/spec/unpacker_spec.rb @@ -108,7 +108,9 @@ it 'feed_each enumerator' do raw = sample_object.to_msgpack.to_s * 4 - unpacker.feed_each(raw).to_a.should == [sample_object] * 4 + enum = unpacker.feed_each(raw) + enum.should be_instance_of(Enumerator) + enum.to_a.should == [sample_object] * 4 end it 'reset clears internal buffer' do From b0203f976f9889e785b2f5fc71e2b1a68cefd14e Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Wed, 9 Sep 2015 19:29:05 +0900 Subject: [PATCH 14/39] improve compatibility: reset should just clear internal buffer --- ext/java/org/msgpack/jruby/Decoder.java | 2 +- spec/unpacker_spec.rb | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Decoder.java b/ext/java/org/msgpack/jruby/Decoder.java index f4eb2925..b96cdeab 100644 --- a/ext/java/org/msgpack/jruby/Decoder.java +++ b/ext/java/org/msgpack/jruby/Decoder.java @@ -95,7 +95,7 @@ public void feed(byte[] bytes, int offset, int length) { } public void reset() { - buffer.rewind(); + buffer = null; } public int offset() { diff --git a/spec/unpacker_spec.rb b/spec/unpacker_spec.rb index 4140a25a..79777517 100644 --- a/spec/unpacker_spec.rb +++ b/spec/unpacker_spec.rb @@ -569,16 +569,6 @@ def flatten(struct, results = []) end end - describe '#reset' do - context 'with a buffer' do - it 'is unclear what it is supposed to do' - end - - context 'with a stream' do - it 'is unclear what it is supposed to do' - end - end - context 'regressions' do it 'handles massive arrays (issue #2)' do array = ['foo'] * 10_000 From 20c42e5dcb99d62e5440567d0b44de680c89f18a Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Wed, 9 Sep 2015 19:30:15 +0900 Subject: [PATCH 15/39] it highly depends on CRuby implementation internals * and this test exists in cruby/unpacker_spec --- spec/unpacker_spec.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/spec/unpacker_spec.rb b/spec/unpacker_spec.rb index 79777517..ac18a1ea 100644 --- a/spec/unpacker_spec.rb +++ b/spec/unpacker_spec.rb @@ -147,17 +147,6 @@ }.should_not raise_error end - it 'read raises level stack too deep error' do - 512.times { packer.write_array_header(1) } - packer.write(nil) - - unpacker = MessagePack::Unpacker.new - unpacker.feed(packer.to_s) - lambda { - unpacker.read - }.should raise_error(MessagePack::StackError) - end - it 'read raises invalid byte error' do unpacker.feed("\xc1") lambda { From 5e0ce6d15f574aee8f5b0a427be2fed71a8afd32 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Wed, 9 Sep 2015 19:31:10 +0900 Subject: [PATCH 16/39] correct code for current dependent Rspec --- spec/unpacker_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unpacker_spec.rb b/spec/unpacker_spec.rb index ac18a1ea..2b7f6e7d 100644 --- a/spec/unpacker_spec.rb +++ b/spec/unpacker_spec.rb @@ -561,7 +561,7 @@ def flatten(struct, results = []) context 'regressions' do it 'handles massive arrays (issue #2)' do array = ['foo'] * 10_000 - MessagePack.unpack(MessagePack.pack(array)).should have(10_000).items + MessagePack.unpack(MessagePack.pack(array)).size.should == 10_000 end end From 534b705163d4a3c8863e0c26f8ed747467bb32c7 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Wed, 30 Sep 2015 19:49:21 +0900 Subject: [PATCH 17/39] separate jruby-specific specs for unpacker into jruby/ directory --- spec/jruby/unpacker_spec.rb | 174 ++++++++++++++++++++++++++++++++++++ spec/unpacker_spec.rb | 122 ------------------------- 2 files changed, 174 insertions(+), 122 deletions(-) create mode 100644 spec/jruby/unpacker_spec.rb diff --git a/spec/jruby/unpacker_spec.rb b/spec/jruby/unpacker_spec.rb new file mode 100644 index 00000000..b500c40e --- /dev/null +++ b/spec/jruby/unpacker_spec.rb @@ -0,0 +1,174 @@ +# encoding: ascii-8bit + +require 'stringio' +require 'tempfile' + +require 'spec_helper' + +describe MessagePack::Unpacker do + let :unpacker do + MessagePack::Unpacker.new + end + + let :packer do + MessagePack::Packer.new + end + + let :buffer1 do + MessagePack.pack(:foo => 'bar') + end + + let :buffer2 do + MessagePack.pack(:hello => {:world => [1, 2, 3]}) + end + + let :buffer3 do + MessagePack.pack(:x => 'y') + end + + describe '#execute/#execute_limit/#finished?' do + let :buffer do + buffer1 + buffer2 + buffer3 + end + + it 'extracts an object from the buffer' do + subject.execute(buffer, 0) + subject.data.should == {'foo' => 'bar'} + end + + it 'extracts an object from the buffer, starting at an offset' do + subject.execute(buffer, buffer1.length) + subject.data.should == {'hello' => {'world' => [1, 2, 3]}} + end + + it 'extracts an object from the buffer, starting at an offset reading bytes up to a limit' do + subject.execute_limit(buffer, buffer1.length, buffer2.length) + subject.data.should == {'hello' => {'world' => [1, 2, 3]}} + end + + it 'extracts nothing if the limit cuts an object in half' do + subject.execute_limit(buffer, buffer1.length, 3) + subject.data.should be_nil + end + + it 'returns the offset where the object ended' do + subject.execute(buffer, 0).should == buffer1.length + subject.execute(buffer, buffer1.length).should == buffer1.length + buffer2.length + end + + it 'is finished if #data returns an object' do + subject.execute_limit(buffer, buffer1.length, buffer2.length) + subject.should be_finished + + subject.execute_limit(buffer, buffer1.length, 3) + subject.should_not be_finished + end + end + + describe '#each' do + context 'with a StringIO stream' do + it 'yields each object in the stream' do + objects = [] + subject.stream = StringIO.new(buffer1 + buffer2 + buffer3) + subject.each do |obj| + objects << obj + end + objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] + end + end + + context 'with a File stream' do + it 'yields each object in the stream' do + objects = [] + file = Tempfile.new('msgpack') + file.write(buffer1) + file.write(buffer2) + file.write(buffer3) + file.open + subject.stream = file + subject.each do |obj| + objects << obj + end + objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] + end + end + end + + describe '#fill' do + it 'is a no-op' do + subject.stream = StringIO.new(buffer1 + buffer2 + buffer3) + subject.fill + subject.each { |obj| } + end + end + + context 'encoding', :encodings do + before :all do + @default_internal = Encoding.default_internal + @default_external = Encoding.default_external + end + + after :all do + Encoding.default_internal = @default_internal + Encoding.default_external = @default_external + end + + let :buffer do + MessagePack.pack({'hello' => 'world', 'nested' => ['object', {"sk\xC3\xA5l".force_encoding('utf-8') => true}]}) + end + + let :unpacker do + described_class.new + end + + before do + Encoding.default_internal = Encoding::UTF_8 + Encoding.default_external = Encoding::ISO_8859_1 + end + + it 'produces results with default internal encoding' do + unpacker.execute(buffer, 0) + strings = flatten(unpacker.data).grep(String) + strings.map(&:encoding).uniq.sort.should == [Encoding.default_internal] + end + + it 'recodes to internal encoding' do + unpacker.execute(buffer, 0) + unpacker.data['nested'][1].keys.should == ["sk\xC3\xA5l".force_encoding(Encoding.default_internal)] + end + end + + context 'extensions' do + context 'symbolized keys' do + let :buffer do + MessagePack.pack({'hello' => 'world', 'nested' => ['object', {'structure' => true}]}) + end + + let :unpacker do + described_class.new(:symbolize_keys => true) + end + + it 'can symbolize keys when using #execute' do + unpacker.execute(buffer, 0) + unpacker.data.should == {:hello => 'world', :nested => ['object', {:structure => true}]} + end + end + + context 'encoding', :encodings do + let :buffer do + MessagePack.pack({'hello' => 'world', 'nested' => ['object', {'structure' => true}]}) + end + + let :unpacker do + described_class.new(:encoding => 'UTF-8') + end + + it 'can hardcode encoding when using #execute' do + unpacker.execute(buffer, 0) + strings = flatten(unpacker.data).grep(String) + strings.should == %w[hello world nested object structure] + strings.map(&:encoding).uniq.should == [Encoding::UTF_8] + end + end + end +end diff --git a/spec/unpacker_spec.rb b/spec/unpacker_spec.rb index 2b7f6e7d..ee23f102 100644 --- a/spec/unpacker_spec.rb +++ b/spec/unpacker_spec.rb @@ -404,45 +404,6 @@ def flatten(struct, results = []) MessagePack.pack(:x => 'y') end - describe '#execute/#execute_limit/#finished?' do - let :buffer do - buffer1 + buffer2 + buffer3 - end - - it 'extracts an object from the buffer' do - subject.execute(buffer, 0) - subject.data.should == {'foo' => 'bar'} - end - - it 'extracts an object from the buffer, starting at an offset' do - subject.execute(buffer, buffer1.length) - subject.data.should == {'hello' => {'world' => [1, 2, 3]}} - end - - it 'extracts an object from the buffer, starting at an offset reading bytes up to a limit' do - subject.execute_limit(buffer, buffer1.length, buffer2.length) - subject.data.should == {'hello' => {'world' => [1, 2, 3]}} - end - - it 'extracts nothing if the limit cuts an object in half' do - subject.execute_limit(buffer, buffer1.length, 3) - subject.data.should be_nil - end - - it 'returns the offset where the object ended' do - subject.execute(buffer, 0).should == buffer1.length - subject.execute(buffer, buffer1.length).should == buffer1.length + buffer2.length - end - - it 'is finished if #data returns an object' do - subject.execute_limit(buffer, buffer1.length, buffer2.length) - subject.should be_finished - - subject.execute_limit(buffer, buffer1.length, 3) - subject.should_not be_finished - end - end - describe '#read' do context 'with a buffer' do it 'reads objects' do @@ -490,33 +451,6 @@ def flatten(struct, results = []) end end - context 'with a StringIO stream' do - it 'yields each object in the stream' do - objects = [] - subject.stream = StringIO.new(buffer1 + buffer2 + buffer3) - subject.each do |obj| - objects << obj - end - objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] - end - end - - context 'with a File stream' do - it 'yields each object in the stream' do - objects = [] - file = Tempfile.new('msgpack') - file.write(buffer1) - file.write(buffer2) - file.write(buffer3) - file.open - subject.stream = file - subject.each do |obj| - objects << obj - end - objects.should == [{'foo' => 'bar'}, {'hello' => {'world' => [1, 2, 3]}}, {'x' => 'y'}] - end - end - context 'with a stream passed to the constructor' do it 'yields each object in the stream' do objects = [] @@ -550,14 +484,6 @@ def flatten(struct, results = []) end end - describe '#fill' do - it 'is a no-op' do - subject.stream = StringIO.new(buffer1 + buffer2 + buffer3) - subject.fill - subject.each { |obj| } - end - end - context 'regressions' do it 'handles massive arrays (issue #2)' do array = ['foo'] * 10_000 @@ -565,42 +491,6 @@ def flatten(struct, results = []) end end - context 'encoding', :encodings do - before :all do - @default_internal = Encoding.default_internal - @default_external = Encoding.default_external - end - - after :all do - Encoding.default_internal = @default_internal - Encoding.default_external = @default_external - end - - let :buffer do - MessagePack.pack({'hello' => 'world', 'nested' => ['object', {"sk\xC3\xA5l".force_encoding('utf-8') => true}]}) - end - - let :unpacker do - described_class.new - end - - before do - Encoding.default_internal = Encoding::UTF_8 - Encoding.default_external = Encoding::ISO_8859_1 - end - - it 'produces results with default internal encoding' do - unpacker.execute(buffer, 0) - strings = flatten(unpacker.data).grep(String) - strings.map(&:encoding).uniq.should == [Encoding.default_internal] - end - - it 'recodes to internal encoding' do - unpacker.execute(buffer, 0) - unpacker.data['nested'][1].keys.should == ["sk\xC3\xA5l".force_encoding(Encoding.default_internal)] - end - end - context 'extensions' do context 'symbolized keys' do let :buffer do @@ -611,11 +501,6 @@ def flatten(struct, results = []) described_class.new(:symbolize_keys => true) end - it 'can symbolize keys when using #execute' do - unpacker.execute(buffer, 0) - unpacker.data.should == {:hello => 'world', :nested => ['object', {:structure => true}]} - end - it 'can symbolize keys when using #each' do objs = [] unpacker.feed(buffer) @@ -643,13 +528,6 @@ def flatten(struct, results = []) described_class.new(:encoding => 'UTF-8') end - it 'can hardcode encoding when using #execute' do - unpacker.execute(buffer, 0) - strings = flatten(unpacker.data).grep(String) - strings.should == %w[hello world nested object structure] - strings.map(&:encoding).uniq.should == [Encoding::UTF_8] - end - it 'can hardcode encoding when using #each' do objs = [] unpacker.feed(buffer) From fe0f5b9f9801b4c0adf87cb4826d9fcdb7d8f501 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Thu, 1 Oct 2015 14:07:36 +0900 Subject: [PATCH 18/39] specs merged for both of CRuby and JRuby --- spec/jruby/unpacker_spec.rb | 24 +++++++++++++++++------ spec/unpacker_spec.rb | 39 +++++++++++++++++++++++++++++++++---- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/spec/jruby/unpacker_spec.rb b/spec/jruby/unpacker_spec.rb index b500c40e..445f2417 100644 --- a/spec/jruby/unpacker_spec.rb +++ b/spec/jruby/unpacker_spec.rb @@ -102,6 +102,18 @@ end end + def flatten(struct, results = []) + case struct + when Array + struct.each { |v| flatten(v, results) } + when Hash + struct.each { |k, v| flatten(v, flatten(k, results)) } + else + results << struct + end + results + end + context 'encoding', :encodings do before :all do @default_internal = Encoding.default_internal @@ -126,15 +138,15 @@ Encoding.default_external = Encoding::ISO_8859_1 end - it 'produces results with default internal encoding' do + it 'produces results with encoding as binary or string(utf8)' do unpacker.execute(buffer, 0) strings = flatten(unpacker.data).grep(String) - strings.map(&:encoding).uniq.sort.should == [Encoding.default_internal] + strings.map(&:encoding).uniq.sort{|a,b| a.to_s <=> b.to_s}.should == [Encoding::ASCII_8BIT, Encoding::UTF_8] end it 'recodes to internal encoding' do unpacker.execute(buffer, 0) - unpacker.data['nested'][1].keys.should == ["sk\xC3\xA5l".force_encoding(Encoding.default_internal)] + unpacker.data['nested'][1].keys.should == ["sk\xC3\xA5l".force_encoding(Encoding::UTF_8)] end end @@ -160,14 +172,14 @@ end let :unpacker do - described_class.new(:encoding => 'UTF-8') + described_class.new() end - it 'can hardcode encoding when using #execute' do + it 'decode binary as ascii-8bit string when using #execute' do unpacker.execute(buffer, 0) strings = flatten(unpacker.data).grep(String) strings.should == %w[hello world nested object structure] - strings.map(&:encoding).uniq.should == [Encoding::UTF_8] + strings.map(&:encoding).uniq.should == [Encoding::ASCII_8BIT] end end end diff --git a/spec/unpacker_spec.rb b/spec/unpacker_spec.rb index ee23f102..dc8fd24d 100644 --- a/spec/unpacker_spec.rb +++ b/spec/unpacker_spec.rb @@ -519,16 +519,47 @@ def flatten(struct, results = []) end end - context 'encoding', :encodings do + context 'binary encoding', :encodings do let :buffer do MessagePack.pack({'hello' => 'world', 'nested' => ['object', {'structure' => true}]}) end let :unpacker do - described_class.new(:encoding => 'UTF-8') + described_class.new() end - it 'can hardcode encoding when using #each' do + it 'decodes binary as ascii-8bit when using #feed' do + objs = [] + unpacker.feed(buffer) + unpacker.each do |obj| + objs << obj + end + strings = flatten(objs).grep(String) + strings.should == %w[hello world nested object structure] + strings.map(&:encoding).uniq.should == [Encoding::ASCII_8BIT] + end + + it 'decodes binary as ascii-8bit when using #feed_each' do + objs = [] + unpacker.feed_each(buffer) do |obj| + objs << obj + end + strings = flatten(objs).grep(String) + strings.should == %w[hello world nested object structure] + strings.map(&:encoding).uniq.should == [Encoding::ASCII_8BIT] + end + end + + context 'string encoding', :encodings do + let :buffer do + MessagePack.pack({'hello'.force_encoding(Encoding::UTF_8) => 'world'.force_encoding(Encoding::UTF_8), 'nested'.force_encoding(Encoding::UTF_8) => ['object'.force_encoding(Encoding::UTF_8), {'structure'.force_encoding(Encoding::UTF_8) => true}]}) + end + + let :unpacker do + described_class.new() + end + + it 'decodes string as utf-8 when using #feed' do objs = [] unpacker.feed(buffer) unpacker.each do |obj| @@ -539,7 +570,7 @@ def flatten(struct, results = []) strings.map(&:encoding).uniq.should == [Encoding::UTF_8] end - it 'can hardcode encoding when using #feed_each' do + it 'decodes binary as ascii-8bit when using #feed_each' do objs = [] unpacker.feed_each(buffer) do |obj| objs << obj From a0e72d1c39fc1558ea30a1373e5997697a81dd74 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Mon, 19 Oct 2015 14:41:59 +0200 Subject: [PATCH 19/39] remove .swp file which added in mistake --- ext/msgpack/.factory_class.c.swp | Bin 16384 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ext/msgpack/.factory_class.c.swp diff --git a/ext/msgpack/.factory_class.c.swp b/ext/msgpack/.factory_class.c.swp deleted file mode 100644 index 65f20450bcc23761d46d13cb7c3169ed0ac22ec1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI2UyK_^9mltAXdC*6ihoZOO?^#cUwpRDfg~ios?J@mv3j{{&UZc-Zq~`ZDpx(v_54!Q^ySRu;Z*v)hxHvb4;U`toCRmu-viyZqO|Ez0SZe*QVT{ zxmo6CeRZMSHca1fH+8dTcwWg&O3A)w7RW5{!7Ol-wzoQ6p}G^f+t?>QzWTw$WVJF2 zWERLQkXazJKxTo=0+|Ie3uG4fzqEiqxLW%XG{0JE|Cs!L)zJTI{FtWw z0lWpC2hV{ua65SKW=;DC_%V1HJPZop^_w*9aj*tDpbhQ?li)h=&W)P(Yw#j?0XzjZ zz&dyUxEKo?Ad3ivem{YPML@C-0P6D)yaU;$hUc7S(wXxd-F z)4&2Y!;aflufWcQ<=T#$w``wT zGnGTkVt2CY=l9{uo;|9-iQ2;I5f&@KW+L@$zL7_Bp`7{J?8?&esl3N~ZPE(Y&uN}- z_?F3phsX<$mDWEU@V{81Bl(1tDz8jcu zlT@+Dwg~o%u-~{={Yl)E{W``y&*<>`&{us`%7b|<-!gjE7kS=rJ7$sL?>Z#P`%0XIlwrFKL^|U zbEF96%@K7+dQ%9AeJO>)lY?PTs#}w|HNubgg#&k-7e(ci55=V+4@xN`{3q^wK<|mR zuH1JNL1bU!7>20j8kWcN_`+*$=ZNb%Zjt3ST*vM(JLosL%bYe1jG)hL-(&e*ErtNZ zDurA)tSM?R`6-v#%F^5ta|XWU*dDVyW;;Ifg2BLXecoc5+z$&Qx*9>xKN!_)y4-*# zNREjJ$|~ZPw_*8amxYsvf?HVN^21hQ8dAKxqM=S9O(+yk2RJwkAtzm9pkl}xz0Fr- zYHsxncP;+ab~%@kHB@~|m`TRCaJ;_N99AP0i(w;?xGh~^lR|vhe`<6B)84Wd z2<=&Yi?)pv^DzjqWcXdq7A%w79xus44bGF7!$pLOc>``*ZEQJvhTRFUm0}%d9h*yR zp4ouAearJg!G_(El1|oFjxMdPu#>gr<=Wy({YZl?EwkCB#ku-QeQ6QD=2>m=6uY;+ zI9FsGuVq6cesq#VI>+Wv;& za!8WG$gEx89~>-~H#Rm(2K>|ICCBZQA&|`Kl^5!>M;04LrmCe%T-zK^{88Bhklr5qf54IBsm zrCb~QhisgEG7EfIEFjMyn!4C*=q45=Ki^Np3Zysbbs34wHmkPW7s_=ynG{xv;nwDI zYHuSSJ98y_M5-unK?#kP1f*%QUw!U(omTo>Ko&*^)qzcTT!a3p7E;%XY>GDiX}JT} zS~q;IU!MFe2SOv-+ES*7Y$4b;wZLi;Yvy zghk?uqeX`P*V}HR?y=jR~0+nke(dv;N-;jpIFp_~JYAyO-!zCP$#9UI*+yN0_Pic@ps;n=e33LY>_;lTxs zg6Ps+c$Bd}sVP+zUc{kc6|K{m-TLVYJ=@U8Sc{*iAZ^=Ll&F*z6dQwdyyQ?jilpb#BTaTkjWd#* z7;EaT7-{rKOSz8=uuwgyEyG166x$6$n2V?|Tm*;*IjV_eN` zx|mkIy3Mh)SPZ2SVgZTzLqCP0sZ!`>dKpWWkuFJIqUje#7I6`?ESiyWmZ_t)%YURN zd`hCA4OZ1q!7)#QCb-?Q+8Vh^KIFsDHSqWjPZjs(t6{WO>mD7e(n4FJ`Id^~c-ALk dIQkwvCxss;XnEUScsO_ISz$^#7KD_b{{iS>1e5>( From 0ca45a4764585a5313edc49778d4a93b9e98b18b Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Mon, 19 Oct 2015 14:50:37 +0200 Subject: [PATCH 20/39] remove skips for java implementations (missed to be removed) --- spec/factory_spec.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/spec/factory_spec.rb b/spec/factory_spec.rb index fca8b486..9bd058ce 100644 --- a/spec/factory_spec.rb +++ b/spec/factory_spec.rb @@ -1,8 +1,6 @@ # encoding: ascii-8bit require 'spec_helper' -eval("return") if java? - describe MessagePack::Factory do subject do described_class.new @@ -220,7 +218,6 @@ class MyType2 < MyType require_relative 'exttypes' it 'should be referred by MessagePack.pack and MessagePack.unpack' do - skip("not supported yet in JRuby implementation") if java? MessagePack::DefaultFactory.register_type(DummyTimeStamp1::TYPE, DummyTimeStamp1) MessagePack::DefaultFactory.register_type(DummyTimeStamp2::TYPE, DummyTimeStamp2, packer: :serialize, unpacker: :deserialize) From 629cc505922301370ac88a6fc1fd40f7bec4ed1c Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Mon, 19 Oct 2015 14:58:16 +0200 Subject: [PATCH 21/39] fix to raise same exception with c impl. for unknown extension types --- ext/java/org/msgpack/jruby/Decoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/java/org/msgpack/jruby/Decoder.java b/ext/java/org/msgpack/jruby/Decoder.java index b96cdeab..9151d172 100644 --- a/ext/java/org/msgpack/jruby/Decoder.java +++ b/ext/java/org/msgpack/jruby/Decoder.java @@ -153,7 +153,7 @@ private IRubyObject consumeExtension(int size) { return ExtensionValue.newExtensionValue(runtime, type, payload); } - throw runtime.newRaiseException(unexpectedTypeErrorClass, "unexpected type"); + throw runtime.newRaiseException(unknownExtTypeErrorClass, "unexpected extension type"); } private byte[] readBytes(int size) { From 2441df79765df2dadcd3f5461dc945a3e7617411 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Mon, 19 Oct 2015 15:10:52 +0200 Subject: [PATCH 22/39] RubyHash#fastARef returns null instead of nil if specified key is missing --- ext/java/org/msgpack/jruby/Factory.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Factory.java b/ext/java/org/msgpack/jruby/Factory.java index a3efc35d..5e2d79d0 100644 --- a/ext/java/org/msgpack/jruby/Factory.java +++ b/ext/java/org/msgpack/jruby/Factory.java @@ -114,10 +114,10 @@ public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args) { IRubyObject packerProc = runtime.getNil(); IRubyObject unpackerProc = runtime.getNil(); - if (packerArg != runtime.getNil()) { + if (packerArg != null) { packerProc = packerArg.callMethod(ctx, "to_proc"); } - if (unpackerArg != runtime.getNil()) { + if (unpackerArg != null) { if (unpackerArg instanceof RubyString || unpackerArg instanceof RubySymbol) { unpackerProc = extClass.method(unpackerArg.callMethod(ctx, "to_sym")); } else { From b85c3ba48b2310e0f6ac4d07e666da662768553e Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Mon, 19 Oct 2015 15:12:42 +0200 Subject: [PATCH 23/39] array size must be 2 not 0 --- ext/java/org/msgpack/jruby/Packer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index 768c1e16..66fb3736 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -66,7 +66,7 @@ public IRubyObject[] lookup(final RubyClass klass) { e = (RubyArray) cache.fastARef(klass); } if (e != null) { - IRubyObject[] hit = new IRubyObject[] {}; + IRubyObject[] hit = new IRubyObject[2]; hit[0] = e.entry(1); hit[1] = e.entry(0); return hit; From 6977b427dc055c9fac51fd151245f08b48683cb2 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Mon, 19 Oct 2015 18:01:03 +0200 Subject: [PATCH 24/39] include_p is only for included Modules: check ancestors list instead --- ext/java/org/msgpack/jruby/Packer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index 66fb3736..d8f3a21b 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -79,9 +79,9 @@ public void visit(IRubyObject keyValue, IRubyObject value) { if (pair[0] != null) { return; } - RubyClass key = (RubyClass) keyValue; - IRubyObject rb_class_inherited_p = klass.include_p(runtime.getCurrentContext(), key); - if (rb_class_inherited_p.isTrue()) { + ThreadContext ctx = runtime.getCurrentContext(); + RubyArray ancestors = (RubyArray) klass.callMethod(ctx, "ancestors"); + if (ancestors.callMethod(ctx, "include?", keyValue).isTrue()) { RubyArray hit = (RubyArray) hash.fastARef(keyValue); cache.fastASet(klass, hit); pair[0] = hit.entry(1); From 1c3e205029fa58a9c7bbd65e11b1349cce2b393a Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Mon, 19 Oct 2015 18:16:52 +0200 Subject: [PATCH 25/39] fix code styles and remove useless comments/unused functions --- ext/java/org/msgpack/jruby/Decoder.java | 4 ++-- ext/java/org/msgpack/jruby/Encoder.java | 7 +++---- ext/java/org/msgpack/jruby/Factory.java | 3 --- ext/msgpack/packer_ext_registry.c | 9 --------- ext/msgpack/packer_ext_registry.h | 4 ---- ext/msgpack/unpacker_ext_registry.c | 7 ------- ext/msgpack/unpacker_ext_registry.h | 4 ---- 7 files changed, 5 insertions(+), 33 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Decoder.java b/ext/java/org/msgpack/jruby/Decoder.java index 9151d172..5e720231 100644 --- a/ext/java/org/msgpack/jruby/Decoder.java +++ b/ext/java/org/msgpack/jruby/Decoder.java @@ -141,8 +141,8 @@ private IRubyObject consumeExtension(int size) { int type = buffer.get(); byte[] payload = readBytes(size); - if (this.registry != null) { - IRubyObject proc = this.registry.lookup(type); + if (registry != null) { + IRubyObject proc = registry.lookup(type); if (proc != null) { ByteList byteList = new ByteList(payload, runtime.getEncodingService().getAscii8bitEncoding()); return proc.callMethod(runtime.getCurrentContext(), "call", runtime.newString(byteList)); diff --git a/ext/java/org/msgpack/jruby/Encoder.java b/ext/java/org/msgpack/jruby/Encoder.java index 04712c20..48fb6f1b 100644 --- a/ext/java/org/msgpack/jruby/Encoder.java +++ b/ext/java/org/msgpack/jruby/Encoder.java @@ -341,15 +341,15 @@ private void appendExt(int type, ByteList payloadBytes) { private void appendExtensionValue(ExtensionValue object) { long type = ((RubyFixnum)object.get_type()).getLongValue(); if (type < -128 || type > 127) { - throw object.getRuntime().newRangeError(String.format("integer %d too big to convert to `signed char'", type)); + throw object.getRuntime().newRangeError(String.format("integer %d too big to convert to `signed char'", type)); } ByteList payloadBytes = ((RubyString)object.payload()).getByteList(); appendExt((int) type, payloadBytes); } private void appendOther(IRubyObject object, IRubyObject destination) { - if (this.registry != null) { - IRubyObject[] pair = this.registry.lookup(object.getType()); + if (registry != null) { + IRubyObject[] pair = registry.lookup(object.getType()); if (pair != null) { RubyString bytes = pair[0].callMethod(runtime.getCurrentContext(), "call", object).asString(); int type = (int) ((RubyFixnum) pair[1]).getLongValue(); @@ -357,7 +357,6 @@ private void appendOther(IRubyObject object, IRubyObject destination) { return; } } - // registry is null or type is not registered appendCustom(object, destination); } diff --git a/ext/java/org/msgpack/jruby/Factory.java b/ext/java/org/msgpack/jruby/Factory.java index 5e2d79d0..9e4c8d53 100644 --- a/ext/java/org/msgpack/jruby/Factory.java +++ b/ext/java/org/msgpack/jruby/Factory.java @@ -64,7 +64,6 @@ public Unpacker unpacker(ThreadContext ctx, IRubyObject[] args) { @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE) public IRubyObject registeredTypesInternal(ThreadContext ctx) { - // unpacker Unpacker.ExtRegistry reg = unpackerRegistry(); RubyHash mapping = RubyHash.newHash(ctx.getRuntime()); for (int i = 0; i < 256; i++) { @@ -79,8 +78,6 @@ public IRubyObject registeredTypesInternal(ThreadContext ctx) { @JRubyMethod(name = "register_type", required = 2, optional = 1) public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args) { - // register_type(type, Class) - // register_type(type, Class, packer: proc-like, unpacker: proc-like) Ruby runtime = ctx.getRuntime(); IRubyObject type = args[0]; IRubyObject klass = args[1]; diff --git a/ext/msgpack/packer_ext_registry.c b/ext/msgpack/packer_ext_registry.c index 5ce7f44a..8601cc6e 100644 --- a/ext/msgpack/packer_ext_registry.c +++ b/ext/msgpack/packer_ext_registry.c @@ -77,12 +77,3 @@ VALUE msgpack_packer_ext_registry_put(msgpack_packer_ext_registry_t* pkrg, #endif return rb_hash_aset(pkrg->hash, ext_class, e); } - -// TODO: delete this function (unused) -VALUE msgpack_packer_ext_registry_call(msgpack_packer_ext_registry_t* pkrg, - VALUE proc, VALUE ext_value) -{ - VALUE string = rb_funcall(proc, s_call, 1, ext_value); - StringValue(string); - return string; -} diff --git a/ext/msgpack/packer_ext_registry.h b/ext/msgpack/packer_ext_registry.h index 6f3016ec..570dde57 100644 --- a/ext/msgpack/packer_ext_registry.h +++ b/ext/msgpack/packer_ext_registry.h @@ -95,8 +95,4 @@ static inline VALUE msgpack_packer_ext_registry_lookup(msgpack_packer_ext_regist return Qnil; } -// TODO: delete this function (unused) -VALUE msgpack_packer_ext_registry_call(msgpack_packer_ext_registry_t* pkrg, - VALUE proc, VALUE ext_value); - #endif diff --git a/ext/msgpack/unpacker_ext_registry.c b/ext/msgpack/unpacker_ext_registry.c index 45c7f343..94c6694b 100644 --- a/ext/msgpack/unpacker_ext_registry.c +++ b/ext/msgpack/unpacker_ext_registry.c @@ -60,10 +60,3 @@ VALUE msgpack_unpacker_ext_registry_put(msgpack_unpacker_ext_registry_t* ukrg, ukrg->array[ext_type + 128] = e; return before; } - -// TODO: delete this function (unused) -VALUE msgpack_unpacker_ext_registry_call(msgpack_unpacker_ext_registry_t* ukrg, - VALUE proc, VALUE ext_data) -{ - return rb_funcall(proc, s_call, 1, ext_data); -} diff --git a/ext/msgpack/unpacker_ext_registry.h b/ext/msgpack/unpacker_ext_registry.h index ee2d19b5..fddef9d2 100644 --- a/ext/msgpack/unpacker_ext_registry.h +++ b/ext/msgpack/unpacker_ext_registry.h @@ -56,8 +56,4 @@ static inline VALUE msgpack_unpacker_ext_registry_lookup(msgpack_unpacker_ext_re return rb_ary_entry(e, 1); } -// TODO: delete this function (unused) -VALUE msgpack_unpacker_ext_registry_call(msgpack_unpacker_ext_registry_t* ukrg, - VALUE proc, VALUE ext_data); - #endif From e04ee3b1cd9585565c76bcebb631cdafe4699940 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Mon, 19 Oct 2015 18:31:06 +0200 Subject: [PATCH 26/39] remove additional this. and comments --- ext/java/org/msgpack/jruby/Packer.java | 10 ++++------ ext/java/org/msgpack/jruby/Unpacker.java | 18 +++++++++--------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index d8f3a21b..238d8687 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -47,8 +47,8 @@ public ExtRegistry(Ruby runtime) { } public ExtRegistry dup() { - ExtRegistry copy = new ExtRegistry(this.runtime); - copy.hash = (RubyHash) this.hash.dup(runtime.getCurrentContext()); + ExtRegistry copy = new ExtRegistry(runtime); + copy.hash = (RubyHash) hash.dup(runtime.getCurrentContext()); copy.cache = RubyHash.newHash(runtime); return copy; } @@ -59,7 +59,6 @@ public void put(RubyClass klass, int typeId, IRubyObject proc, IRubyObject arg) hash.fastASet(klass, e); } - // proc, typeId(Fixnum) public IRubyObject[] lookup(final RubyClass klass) { RubyArray e = (RubyArray) hash.fastARef(klass); if (e == null) { @@ -126,8 +125,7 @@ public static Packer newPacker(ThreadContext ctx, ExtRegistry extRegistry, IRuby @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE) public IRubyObject registeredTypesInternal(ThreadContext ctx) { - ////Nullpo - return this.registry.hash.dup(ctx); + return registry.hash.dup(ctx); } @JRubyMethod(name = "register_type", required = 2, optional = 1) @@ -163,7 +161,7 @@ public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args, final Blo } RubyClass extClass = (RubyClass) klass; - this.registry.put(extClass, (int) typeId, proc, arg); + registry.put(extClass, (int) typeId, proc, arg); return runtime.getNil(); } diff --git a/ext/java/org/msgpack/jruby/Unpacker.java b/ext/java/org/msgpack/jruby/Unpacker.java index dbeff86c..5284df8c 100644 --- a/ext/java/org/msgpack/jruby/Unpacker.java +++ b/ext/java/org/msgpack/jruby/Unpacker.java @@ -55,8 +55,8 @@ public ExtRegistry(Ruby runtime) { } public ExtRegistry dup() { - ExtRegistry copy = new ExtRegistry(this.runtime); - copy.array = Arrays.copyOf(this.array, 256); + ExtRegistry copy = new ExtRegistry(runtime); + copy.array = Arrays.copyOf(array, 256); return copy; } @@ -98,7 +98,7 @@ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { setStream(ctx, args[0]); } } - this.registry = new ExtRegistry(ctx.getRuntime()); + registry = new ExtRegistry(ctx.getRuntime()); return this; } @@ -156,7 +156,7 @@ public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args, final Blo throw runtime.newRangeError(String.format("integer %d too big to convert to `signed char'", typeId)); } - this.registry.put(extClass, (int) typeId, proc, arg); + registry.put(extClass, (int) typeId, proc, arg); return runtime.getNil(); } @@ -174,12 +174,12 @@ public IRubyObject executeLimit(ThreadContext ctx, IRubyObject str, IRubyObject if (limit == -1) { limit = byteList.length() - offset; } - Decoder decoder = new Decoder(ctx.getRuntime(), this.registry, byteList.unsafeBytes(), byteList.begin() + offset, limit); + Decoder decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin() + offset, limit); decoder.symbolizeKeys(symbolizeKeys); decoder.allowUnknownExt(allowUnknownExt); try { - this.data = null; - this.data = decoder.next(); + data = null; + data = decoder.next(); } catch (RaiseException re) { if (re.getException().getType() != underflowErrorClass) { throw re; @@ -206,7 +206,7 @@ public IRubyObject finished_p(ThreadContext ctx) { public IRubyObject feed(ThreadContext ctx, IRubyObject data) { ByteList byteList = data.asString().getByteList(); if (decoder == null) { - decoder = new Decoder(ctx.getRuntime(), this.registry, byteList.unsafeBytes(), byteList.begin(), byteList.length()); + decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin(), byteList.length()); decoder.symbolizeKeys(symbolizeKeys); decoder.allowUnknownExt(allowUnknownExt); } else { @@ -343,7 +343,7 @@ public IRubyObject setStream(ThreadContext ctx, IRubyObject stream) { ByteList byteList = str.getByteList(); this.stream = stream; this.decoder = null; - this.decoder = new Decoder(ctx.getRuntime(), this.registry, byteList.unsafeBytes(), byteList.begin(), byteList.length()); + this.decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin(), byteList.length()); decoder.symbolizeKeys(symbolizeKeys); decoder.allowUnknownExt(allowUnknownExt); return getStream(ctx); From 32a36864726cdb6992e7607a5634844086fc0a6d Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Mon, 19 Oct 2015 18:42:12 +0200 Subject: [PATCH 27/39] use long descriptive name for class --- ext/java/org/msgpack/jruby/Decoder.java | 8 +++---- ext/java/org/msgpack/jruby/Encoder.java | 4 ++-- ext/java/org/msgpack/jruby/Factory.java | 22 +++++++++---------- .../org/msgpack/jruby/MessagePackLibrary.java | 2 +- ext/java/org/msgpack/jruby/Packer.java | 18 +++++++-------- ext/java/org/msgpack/jruby/Unpacker.java | 18 +++++++-------- 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Decoder.java b/ext/java/org/msgpack/jruby/Decoder.java index 5e720231..09a02ac8 100644 --- a/ext/java/org/msgpack/jruby/Decoder.java +++ b/ext/java/org/msgpack/jruby/Decoder.java @@ -35,7 +35,7 @@ public class Decoder implements Iterator { private final RubyClass unexpectedTypeErrorClass; private final RubyClass unknownExtTypeErrorClass; - private Unpacker.ExtRegistry registry; + private Unpacker.ExtensionRegistry registry; private ByteBuffer buffer; private boolean symbolizeKeys; private boolean allowUnknownExt; @@ -44,7 +44,7 @@ public Decoder(Ruby runtime) { this(runtime, null, new byte[] {}, 0, 0); } - public Decoder(Ruby runtime, Unpacker.ExtRegistry registry) { + public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry) { this(runtime, registry, new byte[] {}, 0, 0); } @@ -52,11 +52,11 @@ public Decoder(Ruby runtime, byte[] bytes) { this(runtime, null, bytes, 0, bytes.length); } - public Decoder(Ruby runtime, Unpacker.ExtRegistry registry, byte[] bytes) { + public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry, byte[] bytes) { this(runtime, registry, bytes, 0, bytes.length); } - public Decoder(Ruby runtime, Unpacker.ExtRegistry registry, byte[] bytes, int offset, int length) { + public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry, byte[] bytes, int offset, int length) { this.runtime = runtime; this.registry = registry; this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding(); diff --git a/ext/java/org/msgpack/jruby/Encoder.java b/ext/java/org/msgpack/jruby/Encoder.java index 48fb6f1b..dc775c21 100644 --- a/ext/java/org/msgpack/jruby/Encoder.java +++ b/ext/java/org/msgpack/jruby/Encoder.java @@ -35,7 +35,7 @@ public class Encoder { private final Encoding utf8Encoding; private final boolean compatibilityMode; - private Packer.ExtRegistry registry; + private Packer.ExtensionRegistry registry; private ByteBuffer buffer; public Encoder(Ruby runtime, boolean compatibilityMode) { @@ -46,7 +46,7 @@ public Encoder(Ruby runtime, boolean compatibilityMode) { this.compatibilityMode = compatibilityMode; } - public void setRegistry(Packer.ExtRegistry registry) { + public void setRegistry(Packer.ExtensionRegistry registry) { this.registry = registry; } diff --git a/ext/java/org/msgpack/jruby/Factory.java b/ext/java/org/msgpack/jruby/Factory.java index 9e4c8d53..f494dd5a 100644 --- a/ext/java/org/msgpack/jruby/Factory.java +++ b/ext/java/org/msgpack/jruby/Factory.java @@ -23,8 +23,8 @@ @JRubyClass(name="MessagePack::Factory") public class Factory extends RubyObject { private Ruby runtime; - private Packer.ExtRegistry packerExtRegistry; - private Unpacker.ExtRegistry unpackerExtRegistry; + private Packer.ExtensionRegistry packerExtensionRegistry; + private Unpacker.ExtensionRegistry unpackerExtensionRegistry; public Factory(Ruby runtime, RubyClass type) { super(runtime, type); @@ -39,17 +39,17 @@ public IRubyObject allocate(Ruby runtime, RubyClass type) { @JRubyMethod(name = "initialize") public IRubyObject initialize(ThreadContext ctx) { - this.packerExtRegistry = new Packer.ExtRegistry(ctx.getRuntime()); - this.unpackerExtRegistry = new Unpacker.ExtRegistry(ctx.getRuntime()); + this.packerExtensionRegistry = new Packer.ExtensionRegistry(ctx.getRuntime()); + this.unpackerExtensionRegistry = new Unpacker.ExtensionRegistry(ctx.getRuntime()); return this; } - public Packer.ExtRegistry packerRegistry() { - return this.packerExtRegistry.dup(); + public Packer.ExtensionRegistry packerRegistry() { + return this.packerExtensionRegistry.dup(); } - public Unpacker.ExtRegistry unpackerRegistry() { - return this.unpackerExtRegistry.dup(); + public Unpacker.ExtensionRegistry unpackerRegistry() { + return this.unpackerExtensionRegistry.dup(); } @JRubyMethod(name = "packer", optional = 1) @@ -64,7 +64,7 @@ public Unpacker unpacker(ThreadContext ctx, IRubyObject[] args) { @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE) public IRubyObject registeredTypesInternal(ThreadContext ctx) { - Unpacker.ExtRegistry reg = unpackerRegistry(); + Unpacker.ExtensionRegistry reg = unpackerRegistry(); RubyHash mapping = RubyHash.newHash(ctx.getRuntime()); for (int i = 0; i < 256; i++) { if (reg.array[i] != null) { @@ -122,8 +122,8 @@ public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args) { } } - this.packerExtRegistry.put(extClass, (int) typeId, packerProc, packerArg); - this.unpackerExtRegistry.put(extClass, (int) typeId, unpackerProc, unpackerArg); + this.packerExtensionRegistry.put(extClass, (int) typeId, packerProc, packerArg); + this.unpackerExtensionRegistry.put(extClass, (int) typeId, unpackerProc, unpackerArg); return runtime.getNil(); } diff --git a/ext/java/org/msgpack/jruby/MessagePackLibrary.java b/ext/java/org/msgpack/jruby/MessagePackLibrary.java index f2526c77..358d8635 100644 --- a/ext/java/org/msgpack/jruby/MessagePackLibrary.java +++ b/ext/java/org/msgpack/jruby/MessagePackLibrary.java @@ -117,7 +117,7 @@ public static IRubyObject pack(ThreadContext ctx, IRubyObject recv, IRubyObject[ @JRubyMethod(module = true, required = 1, optional = 1, alias = {"load"}) public static IRubyObject unpack(ThreadContext ctx, IRubyObject recv, IRubyObject[] args) { - Unpacker.ExtRegistry registry = MessagePackLibrary.defaultFactory.unpackerRegistry(); + Unpacker.ExtensionRegistry registry = MessagePackLibrary.defaultFactory.unpackerRegistry(); Decoder decoder = new Decoder(ctx.getRuntime(), registry, args[0].asString().getBytes()); if (args.length > 1 && !args[args.length - 1].isNil()) { RubyHash hash = args[args.length - 1].convertToHash(); diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index 238d8687..2ce9b7e1 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -21,7 +21,7 @@ @JRubyClass(name="MessagePack::Packer") public class Packer extends RubyObject { - public ExtRegistry registry; + public ExtensionRegistry registry; private Buffer buffer; private Encoder encoder; @@ -35,19 +35,19 @@ public IRubyObject allocate(Ruby runtime, RubyClass type) { } } - static class ExtRegistry { + static class ExtensionRegistry { private Ruby runtime; public RubyHash hash; public RubyHash cache; - public ExtRegistry(Ruby runtime) { + public ExtensionRegistry(Ruby runtime) { this.runtime = runtime; hash = RubyHash.newHash(runtime); cache = RubyHash.newHash(runtime); } - public ExtRegistry dup() { - ExtRegistry copy = new ExtRegistry(runtime); + public ExtensionRegistry dup() { + ExtensionRegistry copy = new ExtensionRegistry(runtime); copy.hash = (RubyHash) hash.dup(runtime.getCurrentContext()); copy.cache = RubyHash.newHash(runtime); return copy; @@ -107,19 +107,19 @@ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { this.encoder = new Encoder(ctx.getRuntime(), compatibilityMode); this.buffer = new Buffer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Buffer")); this.buffer.initialize(ctx, args); - this.registry = new ExtRegistry(ctx.getRuntime()); + this.registry = new ExtensionRegistry(ctx.getRuntime()); return this; } - public void setExtRegistry(ExtRegistry registry) { + public void setExtensionRegistry(ExtensionRegistry registry) { this.registry = registry; this.encoder.setRegistry(registry); } - public static Packer newPacker(ThreadContext ctx, ExtRegistry extRegistry, IRubyObject[] args) { + public static Packer newPacker(ThreadContext ctx, ExtensionRegistry extRegistry, IRubyObject[] args) { Packer packer = new Packer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Packer")); packer.initialize(ctx, args); - packer.setExtRegistry(extRegistry); + packer.setExtensionRegistry(extRegistry); return packer; } diff --git a/ext/java/org/msgpack/jruby/Unpacker.java b/ext/java/org/msgpack/jruby/Unpacker.java index 5284df8c..33ca9b8a 100644 --- a/ext/java/org/msgpack/jruby/Unpacker.java +++ b/ext/java/org/msgpack/jruby/Unpacker.java @@ -26,7 +26,7 @@ @JRubyClass(name="MessagePack::Unpacker") public class Unpacker extends RubyObject { - public ExtRegistry registry; + public ExtensionRegistry registry; private IRubyObject stream; private IRubyObject data; private Decoder decoder; @@ -45,17 +45,17 @@ public IRubyObject allocate(Ruby runtime, RubyClass klass) { } } - static class ExtRegistry { + static class ExtensionRegistry { private Ruby runtime; public RubyArray[] array; - public ExtRegistry(Ruby runtime) { + public ExtensionRegistry(Ruby runtime) { this.runtime = runtime; this.array = new RubyArray[256]; } - public ExtRegistry dup() { - ExtRegistry copy = new ExtRegistry(runtime); + public ExtensionRegistry dup() { + ExtensionRegistry copy = new ExtensionRegistry(runtime); copy.array = Arrays.copyOf(array, 256); return copy; } @@ -98,18 +98,18 @@ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { setStream(ctx, args[0]); } } - registry = new ExtRegistry(ctx.getRuntime()); + registry = new ExtensionRegistry(ctx.getRuntime()); return this; } - public void setExtRegistry(ExtRegistry registry) { + public void setExtensionRegistry(ExtensionRegistry registry) { this.registry = registry; } - public static Unpacker newUnpacker(ThreadContext ctx, ExtRegistry extRegistry, IRubyObject[] args) { + public static Unpacker newUnpacker(ThreadContext ctx, ExtensionRegistry extRegistry, IRubyObject[] args) { Unpacker unpacker = new Unpacker(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Unpacker")); unpacker.initialize(ctx, args); - unpacker.setExtRegistry(extRegistry); + unpacker.setExtensionRegistry(extRegistry); return unpacker; } From 6dcc117ab6390696b13b23b4680a0faafe1465b4 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Mon, 19 Oct 2015 19:28:13 +0200 Subject: [PATCH 28/39] add getter for Packer/Unpacker configuration (mainly for tests) --- ext/java/org/msgpack/jruby/Encoder.java | 4 ++++ ext/java/org/msgpack/jruby/Packer.java | 5 +++++ ext/java/org/msgpack/jruby/Unpacker.java | 10 ++++++++++ ext/msgpack/packer_class.c | 7 +++++++ ext/msgpack/unpacker_class.c | 14 ++++++++++++++ spec/packer_spec.rb | 8 ++++++++ spec/unpacker_spec.rb | 10 +++++++++- 7 files changed, 57 insertions(+), 1 deletion(-) diff --git a/ext/java/org/msgpack/jruby/Encoder.java b/ext/java/org/msgpack/jruby/Encoder.java index dc775c21..7deef769 100644 --- a/ext/java/org/msgpack/jruby/Encoder.java +++ b/ext/java/org/msgpack/jruby/Encoder.java @@ -50,6 +50,10 @@ public void setRegistry(Packer.ExtensionRegistry registry) { this.registry = registry; } + public boolean isCompatibilityMode() { + return compatibilityMode; + } + private void ensureRemainingCapacity(int c) { if (buffer.remaining() < c) { int newLength = Math.max(buffer.capacity() + (buffer.capacity() >> 1), buffer.capacity() + c); diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index 2ce9b7e1..cbfe66d7 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -123,6 +123,11 @@ public static Packer newPacker(ThreadContext ctx, ExtensionRegistry extRegistry, return packer; } + @JRubyMethod(name = "compatibility_mode?") + public IRubyObject isCompatibilityMode(ThreadContext ctx) { + return encoder.isCompatibilityMode() ? ctx.getRuntime().getTrue() : ctx.getRuntime().getFalse(); + } + @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE) public IRubyObject registeredTypesInternal(ThreadContext ctx) { return registry.hash.dup(ctx); diff --git a/ext/java/org/msgpack/jruby/Unpacker.java b/ext/java/org/msgpack/jruby/Unpacker.java index 33ca9b8a..de007d03 100644 --- a/ext/java/org/msgpack/jruby/Unpacker.java +++ b/ext/java/org/msgpack/jruby/Unpacker.java @@ -113,6 +113,16 @@ public static Unpacker newUnpacker(ThreadContext ctx, ExtensionRegistry extRegis return unpacker; } + @JRubyMethod(name = "symbolize_keys?") + public IRubyObject isSymbolizeKeys(ThreadContext ctx) { + return symbolizeKeys ? ctx.getRuntime().getTrue() : ctx.getRuntime().getFalse(); + } + + @JRubyMethod(name = "allow_unknown_ext?") + public IRubyObject isAllowUnknownExt(ThreadContext ctx) { + return allowUnknownExt ? ctx.getRuntime().getTrue() : ctx.getRuntime().getFalse(); + } + @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE) public IRubyObject registeredTypesInternal(ThreadContext ctx) { RubyHash mapping = RubyHash.newHash(ctx.getRuntime()); diff --git a/ext/msgpack/packer_class.c b/ext/msgpack/packer_class.c index 79552400..36bdf674 100644 --- a/ext/msgpack/packer_class.c +++ b/ext/msgpack/packer_class.c @@ -109,6 +109,12 @@ VALUE MessagePack_Packer_initialize(int argc, VALUE* argv, VALUE self) return self; } +static VALUE Packer_compatibility_mode_p(VALUE self) +{ + PACKER(self, pk); + return pk->compatibility_mode ? Qtrue : Qfalse; +} + static VALUE Packer_buffer(VALUE self) { PACKER(self, pk); @@ -330,6 +336,7 @@ void MessagePack_Packer_module_init(VALUE mMessagePack) rb_define_alloc_func(cMessagePack_Packer, MessagePack_Packer_alloc); rb_define_method(cMessagePack_Packer, "initialize", MessagePack_Packer_initialize, -1); + rb_define_method(cMessagePack_Packer, "compatibility_mode?", Packer_compatibility_mode_p, 0); rb_define_method(cMessagePack_Packer, "buffer", Packer_buffer, 0); rb_define_method(cMessagePack_Packer, "write", Packer_write, 1); rb_define_alias(cMessagePack_Packer, "pack", "write"); diff --git a/ext/msgpack/unpacker_class.c b/ext/msgpack/unpacker_class.c index 7128821f..d0458793 100644 --- a/ext/msgpack/unpacker_class.c +++ b/ext/msgpack/unpacker_class.c @@ -116,6 +116,18 @@ VALUE MessagePack_Unpacker_initialize(int argc, VALUE* argv, VALUE self) return self; } +static VALUE Unpacker_symbolized_keys_p(VALUE self) +{ + UNPACKER(self, uk); + return uk->symbolize_keys ? Qtrue : Qfalse; +} + +static VALUE Unpacker_allow_unknown_ext_p(VALUE self) +{ + UNPACKER(self, uk); + return uk->allow_unknown_ext ? Qtrue : Qfalse; +} + static void raise_unpacker_error(int r) { switch(r) { @@ -455,6 +467,8 @@ void MessagePack_Unpacker_module_init(VALUE mMessagePack) rb_define_alloc_func(cMessagePack_Unpacker, MessagePack_Unpacker_alloc); rb_define_method(cMessagePack_Unpacker, "initialize", MessagePack_Unpacker_initialize, -1); + rb_define_method(cMessagePack_Unpacker, "symbolize_keys?", Unpacker_symbolized_keys_p, 0); + rb_define_method(cMessagePack_Unpacker, "allow_unknown_ext?", Unpacker_allow_unknown_ext_p, 0); rb_define_method(cMessagePack_Unpacker, "buffer", Unpacker_buffer, 0); rb_define_method(cMessagePack_Unpacker, "read", Unpacker_read, 0); rb_define_alias(cMessagePack_Unpacker, "unpack", "read"); diff --git a/spec/packer_spec.rb b/spec/packer_spec.rb index 8a112b7e..48096a74 100644 --- a/spec/packer_spec.rb +++ b/spec/packer_spec.rb @@ -19,6 +19,14 @@ MessagePack::Packer.new(StringIO.new, {}) end + it 'gets options to specify how to pack values' do + u1 = MessagePack::Packer.new + u1.compatibility_mode?.should == false + + u2 = MessagePack::Packer.new(compatibility_mode: true) + u2.compatibility_mode?.should == true + end + it 'write' do packer.write([]) packer.to_s.should == "\x90" diff --git a/spec/unpacker_spec.rb b/spec/unpacker_spec.rb index dc8fd24d..25f0fe72 100644 --- a/spec/unpacker_spec.rb +++ b/spec/unpacker_spec.rb @@ -14,7 +14,15 @@ MessagePack::Packer.new end - # TODO initialize + it 'gets options to specify how to unpack values' do + u1 = MessagePack::Unpacker.new + u1.symbolize_keys?.should == false + u1.allow_unknown_ext?.should == false + + u2 = MessagePack::Unpacker.new(symbolize_keys: true, allow_unknown_ext: true) + u2.symbolize_keys?.should == true + u2.allow_unknown_ext?.should == true + end it 'read_array_header succeeds' do unpacker.feed("\x91") From f763ed3bff5783e2859e9ff0d2391f55c45da896 Mon Sep 17 00:00:00 2001 From: Theo Date: Fri, 23 Oct 2015 08:20:40 +0200 Subject: [PATCH 29/39] Remove setters from Decoder --- ext/java/org/msgpack/jruby/Decoder.java | 20 +++++++------------ .../org/msgpack/jruby/MessagePackLibrary.java | 8 +++++--- ext/java/org/msgpack/jruby/Unpacker.java | 12 +++-------- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Decoder.java b/ext/java/org/msgpack/jruby/Decoder.java index 09a02ac8..a3df5510 100644 --- a/ext/java/org/msgpack/jruby/Decoder.java +++ b/ext/java/org/msgpack/jruby/Decoder.java @@ -41,22 +41,22 @@ public class Decoder implements Iterator { private boolean allowUnknownExt; public Decoder(Ruby runtime) { - this(runtime, null, new byte[] {}, 0, 0); + this(runtime, null, new byte[] {}, 0, 0, false, false); } public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry) { - this(runtime, registry, new byte[] {}, 0, 0); + this(runtime, registry, new byte[] {}, 0, 0, false, false); } public Decoder(Ruby runtime, byte[] bytes) { - this(runtime, null, bytes, 0, bytes.length); + this(runtime, null, bytes, 0, bytes.length, false, false); } public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry, byte[] bytes) { - this(runtime, registry, bytes, 0, bytes.length); + this(runtime, registry, bytes, 0, bytes.length, false, false); } - public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry, byte[] bytes, int offset, int length) { + public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry, byte[] bytes, int offset, int length, boolean symbolizeKeys, boolean allowUnknownExt) { this.runtime = runtime; this.registry = registry; this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding(); @@ -67,17 +67,11 @@ public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry, byte[] bytes, this.stackErrorClass = runtime.getModule("MessagePack").getClass("StackError"); this.unexpectedTypeErrorClass = runtime.getModule("MessagePack").getClass("UnexpectedTypeError"); this.unknownExtTypeErrorClass = runtime.getModule("MessagePack").getClass("UnknownExtTypeError"); + this.symbolizeKeys = symbolizeKeys; + this.allowUnknownExt = allowUnknownExt; feed(bytes, offset, length); } - public void symbolizeKeys(boolean symbolize) { - this.symbolizeKeys = symbolize; - } - - public void allowUnknownExt(boolean allow) { - this.allowUnknownExt = allow; - } - public void feed(byte[] bytes) { feed(bytes, 0, bytes.length); } diff --git a/ext/java/org/msgpack/jruby/MessagePackLibrary.java b/ext/java/org/msgpack/jruby/MessagePackLibrary.java index 358d8635..ade115f9 100644 --- a/ext/java/org/msgpack/jruby/MessagePackLibrary.java +++ b/ext/java/org/msgpack/jruby/MessagePackLibrary.java @@ -118,12 +118,14 @@ public static IRubyObject pack(ThreadContext ctx, IRubyObject recv, IRubyObject[ @JRubyMethod(module = true, required = 1, optional = 1, alias = {"load"}) public static IRubyObject unpack(ThreadContext ctx, IRubyObject recv, IRubyObject[] args) { Unpacker.ExtensionRegistry registry = MessagePackLibrary.defaultFactory.unpackerRegistry(); - Decoder decoder = new Decoder(ctx.getRuntime(), registry, args[0].asString().getBytes()); + boolean symbolizeKeys = false; if (args.length > 1 && !args[args.length - 1].isNil()) { RubyHash hash = args[args.length - 1].convertToHash(); - IRubyObject symbolizeKeys = hash.fastARef(ctx.getRuntime().newSymbol("symbolize_keys")); - decoder.symbolizeKeys(symbolizeKeys != null && symbolizeKeys.isTrue()); + IRubyObject symbolizeKeysVal = hash.fastARef(ctx.getRuntime().newSymbol("symbolize_keys")); + symbolizeKeys = symbolizeKeysVal != null && symbolizeKeysVal.isTrue(); } + byte[] bytes = args[0].asString().getBytes(); + Decoder decoder = new Decoder(ctx.getRuntime(), registry, bytes, 0, bytes.length, symbolizeKeys, false); return decoder.next(); } } diff --git a/ext/java/org/msgpack/jruby/Unpacker.java b/ext/java/org/msgpack/jruby/Unpacker.java index de007d03..9a87d94b 100644 --- a/ext/java/org/msgpack/jruby/Unpacker.java +++ b/ext/java/org/msgpack/jruby/Unpacker.java @@ -184,9 +184,7 @@ public IRubyObject executeLimit(ThreadContext ctx, IRubyObject str, IRubyObject if (limit == -1) { limit = byteList.length() - offset; } - Decoder decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin() + offset, limit); - decoder.symbolizeKeys(symbolizeKeys); - decoder.allowUnknownExt(allowUnknownExt); + Decoder decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin() + offset, limit, symbolizeKeys, allowUnknownExt); try { data = null; data = decoder.next(); @@ -216,9 +214,7 @@ public IRubyObject finished_p(ThreadContext ctx) { public IRubyObject feed(ThreadContext ctx, IRubyObject data) { ByteList byteList = data.asString().getByteList(); if (decoder == null) { - decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin(), byteList.length()); - decoder.symbolizeKeys(symbolizeKeys); - decoder.allowUnknownExt(allowUnknownExt); + decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin(), byteList.length(), symbolizeKeys, allowUnknownExt); } else { decoder.feed(byteList.unsafeBytes(), byteList.begin(), byteList.length()); } @@ -353,9 +349,7 @@ public IRubyObject setStream(ThreadContext ctx, IRubyObject stream) { ByteList byteList = str.getByteList(); this.stream = stream; this.decoder = null; - this.decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin(), byteList.length()); - decoder.symbolizeKeys(symbolizeKeys); - decoder.allowUnknownExt(allowUnknownExt); + this.decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin(), byteList.length(), symbolizeKeys, allowUnknownExt); return getStream(ctx); } } From 48f9ab589041c01b2d5a5a6ea6bbf6ab27a8c58c Mon Sep 17 00:00:00 2001 From: Theo Date: Fri, 23 Oct 2015 08:28:48 +0200 Subject: [PATCH 30/39] Remove setter from Encoder --- ext/java/org/msgpack/jruby/Encoder.java | 7 ++----- ext/java/org/msgpack/jruby/Packer.java | 15 +++++---------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Encoder.java b/ext/java/org/msgpack/jruby/Encoder.java index 7deef769..182a6a14 100644 --- a/ext/java/org/msgpack/jruby/Encoder.java +++ b/ext/java/org/msgpack/jruby/Encoder.java @@ -34,19 +34,16 @@ public class Encoder { private final Encoding binaryEncoding; private final Encoding utf8Encoding; private final boolean compatibilityMode; + private final Packer.ExtensionRegistry registry; - private Packer.ExtensionRegistry registry; private ByteBuffer buffer; - public Encoder(Ruby runtime, boolean compatibilityMode) { + public Encoder(Ruby runtime, boolean compatibilityMode, Packer.ExtensionRegistry registry) { this.runtime = runtime; this.buffer = ByteBuffer.allocate(CACHE_LINE_SIZE - ARRAY_HEADER_SIZE); this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding(); this.utf8Encoding = UTF8Encoding.INSTANCE; this.compatibilityMode = compatibilityMode; - } - - public void setRegistry(Packer.ExtensionRegistry registry) { this.registry = registry; } diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index cbfe66d7..916b7deb 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -25,13 +25,14 @@ public class Packer extends RubyObject { private Buffer buffer; private Encoder encoder; - public Packer(Ruby runtime, RubyClass type) { + public Packer(Ruby runtime, RubyClass type, ExtensionRegistry registry) { super(runtime, type); + this.registry = registry; } static class PackerAllocator implements ObjectAllocator { public IRubyObject allocate(Ruby runtime, RubyClass type) { - return new Packer(runtime, type); + return new Packer(runtime, type, null); } } @@ -104,22 +105,16 @@ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { IRubyObject mode = options.fastARef(ctx.getRuntime().newSymbol("compatibility_mode")); compatibilityMode = (mode != null) && mode.isTrue(); } - this.encoder = new Encoder(ctx.getRuntime(), compatibilityMode); + this.encoder = new Encoder(ctx.getRuntime(), compatibilityMode, registry); this.buffer = new Buffer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Buffer")); this.buffer.initialize(ctx, args); this.registry = new ExtensionRegistry(ctx.getRuntime()); return this; } - public void setExtensionRegistry(ExtensionRegistry registry) { - this.registry = registry; - this.encoder.setRegistry(registry); - } - public static Packer newPacker(ThreadContext ctx, ExtensionRegistry extRegistry, IRubyObject[] args) { - Packer packer = new Packer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Packer")); + Packer packer = new Packer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Packer"), extRegistry); packer.initialize(ctx, args); - packer.setExtensionRegistry(extRegistry); return packer; } From ac21a805c6ed390f8aad8a2620e3e604131cef9f Mon Sep 17 00:00:00 2001 From: Theo Date: Fri, 23 Oct 2015 08:34:17 +0200 Subject: [PATCH 31/39] Remove comments Comments are at best outdated, but most of the time they confuse more than they help. Whenever you feel the urge to write a comment, think about how you can rewrite the code to make it not need a comment to explain what it does. --- ext/java/org/msgpack/jruby/Packer.java | 2 -- ext/java/org/msgpack/jruby/Unpacker.java | 3 --- 2 files changed, 5 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index 916b7deb..1198a6a0 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -130,8 +130,6 @@ public IRubyObject registeredTypesInternal(ThreadContext ctx) { @JRubyMethod(name = "register_type", required = 2, optional = 1) public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args, final Block block) { - // register_type(type, Class){|obj| how_to_serialize.... } - // register_type(type, Class, :to_msgpack_ext) Ruby runtime = ctx.getRuntime(); IRubyObject type = args[0]; IRubyObject klass = args[1]; diff --git a/ext/java/org/msgpack/jruby/Unpacker.java b/ext/java/org/msgpack/jruby/Unpacker.java index 9a87d94b..744b19f1 100644 --- a/ext/java/org/msgpack/jruby/Unpacker.java +++ b/ext/java/org/msgpack/jruby/Unpacker.java @@ -69,7 +69,6 @@ public void put(RubyClass klass, int typeId, IRubyObject proc, IRubyObject arg) array[typeId + 128] = e; } - // proc, typeId(Fixnum) public IRubyObject lookup(int type) { RubyArray e = array[type + 128]; if (e == null) { @@ -136,8 +135,6 @@ public IRubyObject registeredTypesInternal(ThreadContext ctx) { @JRubyMethod(name = "register_type", required = 1, optional = 2) public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args, final Block block) { - // register_type(type){|data| ExtClass.deserialize(...) } - // register_type(type, Class, :from_msgpack_ext) Ruby runtime = ctx.getRuntime(); IRubyObject type = args[0]; From 230b31321581d0bd9db6275609e914439910b6c7 Mon Sep 17 00:00:00 2001 From: Theo Date: Fri, 23 Oct 2015 08:45:43 +0200 Subject: [PATCH 32/39] Replace a comment with a method By extracting the code below a comment to a method that explains what the code does the comment is not needed and the code is self-explaining. --- ext/java/org/msgpack/jruby/Packer.java | 30 +++++++++++++++----------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index 1198a6a0..4861871e 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -71,29 +71,33 @@ public IRubyObject[] lookup(final RubyClass klass) { hit[1] = e.entry(0); return hit; } + IRubyObject[] pair = findEntryByClassOrAncestor(hash, klass); + if (pair != null) { + cache.fastASet(pair[0], RubyArray.newArray(runtime, pair)); + } + return pair; + } + private IRubyObject[] findEntryByClassOrAncestor(final RubyHash hash, final RubyClass klass) { final IRubyObject[] pair = new IRubyObject[2]; - // check all keys whether it's super class of klass, or not hash.visitAll(new RubyHash.Visitor() { public void visit(IRubyObject keyValue, IRubyObject value) { - if (pair[0] != null) { - return; - } - ThreadContext ctx = runtime.getCurrentContext(); - RubyArray ancestors = (RubyArray) klass.callMethod(ctx, "ancestors"); - if (ancestors.callMethod(ctx, "include?", keyValue).isTrue()) { - RubyArray hit = (RubyArray) hash.fastARef(keyValue); - cache.fastASet(klass, hit); - pair[0] = hit.entry(1); - pair[1] = hit.entry(0); + if (pair[0] == null) { + ThreadContext ctx = runtime.getCurrentContext(); + RubyArray ancestors = (RubyArray) klass.callMethod(ctx, "ancestors"); + if (ancestors.callMethod(ctx, "include?", keyValue).isTrue()) { + RubyArray hit = (RubyArray) hash.fastARef(keyValue); + pair[0] = hit.entry(1); + pair[1] = hit.entry(0); + } } } }); - if (pair[0] == null) { return null; + } else { + return pair; } - return pair; } } From cc5a0198e8b8ada72d4d02d1779c64ca074809ae Mon Sep 17 00:00:00 2001 From: Theo Date: Fri, 23 Oct 2015 08:48:49 +0200 Subject: [PATCH 33/39] Simplify array construction in Packer.ExtensionRegistry --- ext/java/org/msgpack/jruby/Packer.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index 4861871e..9d0257f9 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -66,10 +66,7 @@ public IRubyObject[] lookup(final RubyClass klass) { e = (RubyArray) cache.fastARef(klass); } if (e != null) { - IRubyObject[] hit = new IRubyObject[2]; - hit[0] = e.entry(1); - hit[1] = e.entry(0); - return hit; + return new IRubyObject[] {e.entry(1), e.entry(0)}; } IRubyObject[] pair = findEntryByClassOrAncestor(hash, klass); if (pair != null) { From 18e070e7c23d5476bbcdc7644a959a598519f63a Mon Sep 17 00:00:00 2001 From: Theo Date: Fri, 23 Oct 2015 09:02:16 +0200 Subject: [PATCH 34/39] Remove an unnecessary comment What this comment says is better explained by the exception thrown by the method. --- ext/java/org/msgpack/jruby/Unpacker.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Unpacker.java b/ext/java/org/msgpack/jruby/Unpacker.java index 744b19f1..716a8b75 100644 --- a/ext/java/org/msgpack/jruby/Unpacker.java +++ b/ext/java/org/msgpack/jruby/Unpacker.java @@ -278,10 +278,6 @@ public IRubyObject read(ThreadContext ctx) { } } - /* - skip & skip_nil don't exist in JRuby implementation: - these depend on CRuby msgpack implemntation too highly. - */ @JRubyMethod(name = "skip") public IRubyObject skip(ThreadContext ctx) { throw ctx.getRuntime().newNotImplementedError("Not supported yet in JRuby implementation"); From d1d1f0efca6efaf9709f0c24d0d0215f5da8b416 Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Fri, 23 Oct 2015 16:39:02 +0900 Subject: [PATCH 35/39] fix symbolizeKeys/allowUnknownExt for Decoder as constructor arguments --- ext/java/org/msgpack/jruby/Decoder.java | 18 ++++++++++-------- .../org/msgpack/jruby/MessagePackLibrary.java | 12 +++++++++--- ext/java/org/msgpack/jruby/Unpacker.java | 12 +++--------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Decoder.java b/ext/java/org/msgpack/jruby/Decoder.java index 09a02ac8..556f2e11 100644 --- a/ext/java/org/msgpack/jruby/Decoder.java +++ b/ext/java/org/msgpack/jruby/Decoder.java @@ -56,9 +56,19 @@ public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry, byte[] bytes) this(runtime, registry, bytes, 0, bytes.length); } + public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry, byte[] bytes, boolean symbolizeKeys, boolean allowUnknownExt) { + this(runtime, registry, bytes, 0, bytes.length, symbolizeKeys, allowUnknownExt); + } + public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry, byte[] bytes, int offset, int length) { + this(runtime, registry, bytes, offset, length, false, false); + } + + public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry, byte[] bytes, int offset, int length, boolean symbolizeKeys, boolean allowUnknownExt) { this.runtime = runtime; this.registry = registry; + this.symbolizeKeys = symbolizeKeys; + this.allowUnknownExt = allowUnknownExt; this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding(); this.utf8Encoding = UTF8Encoding.INSTANCE; this.unpackErrorClass = runtime.getModule("MessagePack").getClass("UnpackError"); @@ -70,14 +80,6 @@ public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry, byte[] bytes, feed(bytes, offset, length); } - public void symbolizeKeys(boolean symbolize) { - this.symbolizeKeys = symbolize; - } - - public void allowUnknownExt(boolean allow) { - this.allowUnknownExt = allow; - } - public void feed(byte[] bytes) { feed(bytes, 0, bytes.length); } diff --git a/ext/java/org/msgpack/jruby/MessagePackLibrary.java b/ext/java/org/msgpack/jruby/MessagePackLibrary.java index 358d8635..61b74a5c 100644 --- a/ext/java/org/msgpack/jruby/MessagePackLibrary.java +++ b/ext/java/org/msgpack/jruby/MessagePackLibrary.java @@ -118,12 +118,18 @@ public static IRubyObject pack(ThreadContext ctx, IRubyObject recv, IRubyObject[ @JRubyMethod(module = true, required = 1, optional = 1, alias = {"load"}) public static IRubyObject unpack(ThreadContext ctx, IRubyObject recv, IRubyObject[] args) { Unpacker.ExtensionRegistry registry = MessagePackLibrary.defaultFactory.unpackerRegistry(); - Decoder decoder = new Decoder(ctx.getRuntime(), registry, args[0].asString().getBytes()); + + boolean symbolizeKeys = false; + boolean allowUnknownExt = false; if (args.length > 1 && !args[args.length - 1].isNil()) { RubyHash hash = args[args.length - 1].convertToHash(); - IRubyObject symbolizeKeys = hash.fastARef(ctx.getRuntime().newSymbol("symbolize_keys")); - decoder.symbolizeKeys(symbolizeKeys != null && symbolizeKeys.isTrue()); + IRubyObject symbolizeKeysOpt = hash.fastARef(ctx.getRuntime().newSymbol("symbolize_keys")); + symbolizeKeys = (symbolizeKeysOpt != null && symbolizeKeysOpt.isTrue()); + IRubyObject allowUnknownExtOpt = hash.fastARef(ctx.getRuntime().newSymbol("allow_unknown_ext")); + allowUnknownExt = (allowUnknownExtOpt != null && allowUnknownExtOpt.isTrue()); } + + Decoder decoder = new Decoder(ctx.getRuntime(), registry, args[0].asString().getBytes(), symbolizeKeys, allowUnknownExt); return decoder.next(); } } diff --git a/ext/java/org/msgpack/jruby/Unpacker.java b/ext/java/org/msgpack/jruby/Unpacker.java index de007d03..9a87d94b 100644 --- a/ext/java/org/msgpack/jruby/Unpacker.java +++ b/ext/java/org/msgpack/jruby/Unpacker.java @@ -184,9 +184,7 @@ public IRubyObject executeLimit(ThreadContext ctx, IRubyObject str, IRubyObject if (limit == -1) { limit = byteList.length() - offset; } - Decoder decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin() + offset, limit); - decoder.symbolizeKeys(symbolizeKeys); - decoder.allowUnknownExt(allowUnknownExt); + Decoder decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin() + offset, limit, symbolizeKeys, allowUnknownExt); try { data = null; data = decoder.next(); @@ -216,9 +214,7 @@ public IRubyObject finished_p(ThreadContext ctx) { public IRubyObject feed(ThreadContext ctx, IRubyObject data) { ByteList byteList = data.asString().getByteList(); if (decoder == null) { - decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin(), byteList.length()); - decoder.symbolizeKeys(symbolizeKeys); - decoder.allowUnknownExt(allowUnknownExt); + decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin(), byteList.length(), symbolizeKeys, allowUnknownExt); } else { decoder.feed(byteList.unsafeBytes(), byteList.begin(), byteList.length()); } @@ -353,9 +349,7 @@ public IRubyObject setStream(ThreadContext ctx, IRubyObject stream) { ByteList byteList = str.getByteList(); this.stream = stream; this.decoder = null; - this.decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin(), byteList.length()); - decoder.symbolizeKeys(symbolizeKeys); - decoder.allowUnknownExt(allowUnknownExt); + this.decoder = new Decoder(ctx.getRuntime(), registry, byteList.unsafeBytes(), byteList.begin(), byteList.length(), symbolizeKeys, allowUnknownExt); return getStream(ctx); } } From 1e9ba4076772eb75ae68878761ec0e86b191028a Mon Sep 17 00:00:00 2001 From: Theo Date: Fri, 23 Oct 2015 09:43:37 +0200 Subject: [PATCH 36/39] Extract Packer.ExtensionRegistry to its own class and clean up the internals Use Java collections for the registry and cache, and use a class for the four-tuple containing the registered entries. --- ext/java/org/msgpack/jruby/Encoder.java | 4 +- .../org/msgpack/jruby/ExtensionRegistry.java | 98 +++++++++++++++++++ ext/java/org/msgpack/jruby/Factory.java | 8 +- ext/java/org/msgpack/jruby/Packer.java | 66 +------------ 4 files changed, 106 insertions(+), 70 deletions(-) create mode 100644 ext/java/org/msgpack/jruby/ExtensionRegistry.java diff --git a/ext/java/org/msgpack/jruby/Encoder.java b/ext/java/org/msgpack/jruby/Encoder.java index 182a6a14..879b75c0 100644 --- a/ext/java/org/msgpack/jruby/Encoder.java +++ b/ext/java/org/msgpack/jruby/Encoder.java @@ -34,11 +34,11 @@ public class Encoder { private final Encoding binaryEncoding; private final Encoding utf8Encoding; private final boolean compatibilityMode; - private final Packer.ExtensionRegistry registry; + private final ExtensionRegistry registry; private ByteBuffer buffer; - public Encoder(Ruby runtime, boolean compatibilityMode, Packer.ExtensionRegistry registry) { + public Encoder(Ruby runtime, boolean compatibilityMode, ExtensionRegistry registry) { this.runtime = runtime; this.buffer = ByteBuffer.allocate(CACHE_LINE_SIZE - ARRAY_HEADER_SIZE); this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding(); diff --git a/ext/java/org/msgpack/jruby/ExtensionRegistry.java b/ext/java/org/msgpack/jruby/ExtensionRegistry.java new file mode 100644 index 00000000..a8b63e65 --- /dev/null +++ b/ext/java/org/msgpack/jruby/ExtensionRegistry.java @@ -0,0 +1,98 @@ +package org.msgpack.jruby; + +import org.jruby.Ruby; +import org.jruby.RubyHash; +import org.jruby.RubyArray; +import org.jruby.RubyClass; +import org.jruby.RubyFixnum; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +import java.util.Map; +import java.util.HashMap; + +public class ExtensionRegistry { + private final Map extensionsByClass; + private final Map extensionsByAncestor; + + public ExtensionRegistry() { + this(new HashMap()); + } + + private ExtensionRegistry(Map extensionsByClass) { + this.extensionsByClass = new HashMap(extensionsByClass); + this.extensionsByAncestor = new HashMap(); + } + + public ExtensionRegistry dup() { + return new ExtensionRegistry(extensionsByClass); + } + + public IRubyObject toRubyHash(ThreadContext ctx) { + RubyHash hash = RubyHash.newHash(ctx.getRuntime()); + for (RubyClass extensionClass : extensionsByClass.keySet()) { + hash.put(extensionClass, extensionsByClass.get(extensionClass).toTuple(ctx)); + } + return hash; + } + + public void put(RubyClass cls, int typeId, IRubyObject proc, IRubyObject arg) { + extensionsByClass.put(cls, new ExtensionEntry(cls, typeId, proc, arg)); + extensionsByAncestor.clear(); + } + + public IRubyObject[] lookup(RubyClass cls) { + ExtensionEntry e = extensionsByClass.get(cls); + if (e == null) { + e = extensionsByAncestor.get(cls); + } + if (e == null) { + e = findEntryByClassOrAncestor(cls); + if (e != null) { + extensionsByAncestor.put(e.getExtensionClass(), e); + } + } + if (e == null) { + return null; + } else { + return e.toProcTypePair(cls.getRuntime().getCurrentContext()); + } + } + + private ExtensionEntry findEntryByClassOrAncestor(final RubyClass cls) { + ThreadContext ctx = cls.getRuntime().getCurrentContext(); + for (RubyClass extensionClass : extensionsByClass.keySet()) { + RubyArray ancestors = (RubyArray) cls.callMethod(ctx, "ancestors"); + if (ancestors.callMethod(ctx, "include?", extensionClass).isTrue()) { + return extensionsByClass.get(extensionClass); + } + } + return null; + } + + private static class ExtensionEntry { + private final RubyClass cls; + private final int typeId; + private final IRubyObject proc; + private final IRubyObject arg; + + public ExtensionEntry(RubyClass cls, int typeId, IRubyObject proc, IRubyObject arg) { + this.cls = cls; + this.typeId = typeId; + this.proc = proc; + this.arg = arg; + } + + public RubyClass getExtensionClass() { + return cls; + } + + public RubyArray toTuple(ThreadContext ctx) { + return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {RubyFixnum.newFixnum(ctx.getRuntime(), typeId), proc, arg}); + } + + public IRubyObject[] toProcTypePair(ThreadContext ctx) { + return new IRubyObject[] {proc, RubyFixnum.newFixnum(ctx.getRuntime(), typeId)}; + } + } +} diff --git a/ext/java/org/msgpack/jruby/Factory.java b/ext/java/org/msgpack/jruby/Factory.java index f494dd5a..99c14b01 100644 --- a/ext/java/org/msgpack/jruby/Factory.java +++ b/ext/java/org/msgpack/jruby/Factory.java @@ -23,7 +23,7 @@ @JRubyClass(name="MessagePack::Factory") public class Factory extends RubyObject { private Ruby runtime; - private Packer.ExtensionRegistry packerExtensionRegistry; + private ExtensionRegistry packerExtensionRegistry; private Unpacker.ExtensionRegistry unpackerExtensionRegistry; public Factory(Ruby runtime, RubyClass type) { @@ -39,12 +39,12 @@ public IRubyObject allocate(Ruby runtime, RubyClass type) { @JRubyMethod(name = "initialize") public IRubyObject initialize(ThreadContext ctx) { - this.packerExtensionRegistry = new Packer.ExtensionRegistry(ctx.getRuntime()); + this.packerExtensionRegistry = new ExtensionRegistry(); this.unpackerExtensionRegistry = new Unpacker.ExtensionRegistry(ctx.getRuntime()); return this; } - public Packer.ExtensionRegistry packerRegistry() { + public ExtensionRegistry packerRegistry() { return this.packerExtensionRegistry.dup(); } @@ -72,7 +72,7 @@ public IRubyObject registeredTypesInternal(ThreadContext ctx) { } } - IRubyObject[] ary = { packerRegistry().hash, mapping }; + IRubyObject[] ary = { packerExtensionRegistry.toRubyHash(ctx), mapping }; return RubyArray.newArray(ctx.getRuntime(), ary); } diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index 9d0257f9..3fd47537 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -36,68 +36,6 @@ public IRubyObject allocate(Ruby runtime, RubyClass type) { } } - static class ExtensionRegistry { - private Ruby runtime; - public RubyHash hash; - public RubyHash cache; - - public ExtensionRegistry(Ruby runtime) { - this.runtime = runtime; - hash = RubyHash.newHash(runtime); - cache = RubyHash.newHash(runtime); - } - - public ExtensionRegistry dup() { - ExtensionRegistry copy = new ExtensionRegistry(runtime); - copy.hash = (RubyHash) hash.dup(runtime.getCurrentContext()); - copy.cache = RubyHash.newHash(runtime); - return copy; - } - - public void put(RubyClass klass, int typeId, IRubyObject proc, IRubyObject arg) { - RubyArray e = RubyArray.newArray(runtime, new IRubyObject[] { RubyFixnum.newFixnum(runtime, typeId), proc, arg }); - cache.rb_clear(); - hash.fastASet(klass, e); - } - - public IRubyObject[] lookup(final RubyClass klass) { - RubyArray e = (RubyArray) hash.fastARef(klass); - if (e == null) { - e = (RubyArray) cache.fastARef(klass); - } - if (e != null) { - return new IRubyObject[] {e.entry(1), e.entry(0)}; - } - IRubyObject[] pair = findEntryByClassOrAncestor(hash, klass); - if (pair != null) { - cache.fastASet(pair[0], RubyArray.newArray(runtime, pair)); - } - return pair; - } - - private IRubyObject[] findEntryByClassOrAncestor(final RubyHash hash, final RubyClass klass) { - final IRubyObject[] pair = new IRubyObject[2]; - hash.visitAll(new RubyHash.Visitor() { - public void visit(IRubyObject keyValue, IRubyObject value) { - if (pair[0] == null) { - ThreadContext ctx = runtime.getCurrentContext(); - RubyArray ancestors = (RubyArray) klass.callMethod(ctx, "ancestors"); - if (ancestors.callMethod(ctx, "include?", keyValue).isTrue()) { - RubyArray hit = (RubyArray) hash.fastARef(keyValue); - pair[0] = hit.entry(1); - pair[1] = hit.entry(0); - } - } - } - }); - if (pair[0] == null) { - return null; - } else { - return pair; - } - } - } - @JRubyMethod(name = "initialize", optional = 2) public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { boolean compatibilityMode = false; @@ -109,7 +47,7 @@ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { this.encoder = new Encoder(ctx.getRuntime(), compatibilityMode, registry); this.buffer = new Buffer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Buffer")); this.buffer.initialize(ctx, args); - this.registry = new ExtensionRegistry(ctx.getRuntime()); + this.registry = new ExtensionRegistry(); return this; } @@ -126,7 +64,7 @@ public IRubyObject isCompatibilityMode(ThreadContext ctx) { @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE) public IRubyObject registeredTypesInternal(ThreadContext ctx) { - return registry.hash.dup(ctx); + return registry.toRubyHash(ctx); } @JRubyMethod(name = "register_type", required = 2, optional = 1) From 9b76228a8d3f3a18d3a8364f2d697b7128d81de8 Mon Sep 17 00:00:00 2001 From: Theo Date: Fri, 23 Oct 2015 11:03:31 +0200 Subject: [PATCH 37/39] Replace Unpacker.ExtensionRegistry with ExtensionRegistry Make it possible to look up extensions in ExtensionRegistry by type ID, and add the features needed to make it work for the unpacking side of things. Eliminate duplicated code in Factory#registered_types_internal and Unpacker#registered_types_internal by moving it to ExtensionRegistry. There are a few tests that fail because of this change. The reason is that Factory#registered_types_internal, Packer#registered_types_internal and Unpacker#registered_types_internal have inconsistent interfaces. The consumers of these methods expect slightly different things in the returned values. I consider this a bug, but I can't change it without changing the C code, something I'm not comfortable with. --- ext/java/org/msgpack/jruby/Decoder.java | 10 +- ext/java/org/msgpack/jruby/Encoder.java | 2 +- .../org/msgpack/jruby/ExtensionRegistry.java | 94 +++++++++++++++---- ext/java/org/msgpack/jruby/Factory.java | 41 +++----- .../org/msgpack/jruby/MessagePackLibrary.java | 2 +- ext/java/org/msgpack/jruby/Packer.java | 4 +- ext/java/org/msgpack/jruby/Unpacker.java | 59 ++---------- 7 files changed, 110 insertions(+), 102 deletions(-) diff --git a/ext/java/org/msgpack/jruby/Decoder.java b/ext/java/org/msgpack/jruby/Decoder.java index a3df5510..62f56511 100644 --- a/ext/java/org/msgpack/jruby/Decoder.java +++ b/ext/java/org/msgpack/jruby/Decoder.java @@ -35,7 +35,7 @@ public class Decoder implements Iterator { private final RubyClass unexpectedTypeErrorClass; private final RubyClass unknownExtTypeErrorClass; - private Unpacker.ExtensionRegistry registry; + private ExtensionRegistry registry; private ByteBuffer buffer; private boolean symbolizeKeys; private boolean allowUnknownExt; @@ -44,7 +44,7 @@ public Decoder(Ruby runtime) { this(runtime, null, new byte[] {}, 0, 0, false, false); } - public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry) { + public Decoder(Ruby runtime, ExtensionRegistry registry) { this(runtime, registry, new byte[] {}, 0, 0, false, false); } @@ -52,11 +52,11 @@ public Decoder(Ruby runtime, byte[] bytes) { this(runtime, null, bytes, 0, bytes.length, false, false); } - public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry, byte[] bytes) { + public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes) { this(runtime, registry, bytes, 0, bytes.length, false, false); } - public Decoder(Ruby runtime, Unpacker.ExtensionRegistry registry, byte[] bytes, int offset, int length, boolean symbolizeKeys, boolean allowUnknownExt) { + public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes, int offset, int length, boolean symbolizeKeys, boolean allowUnknownExt) { this.runtime = runtime; this.registry = registry; this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding(); @@ -136,7 +136,7 @@ private IRubyObject consumeExtension(int size) { byte[] payload = readBytes(size); if (registry != null) { - IRubyObject proc = registry.lookup(type); + IRubyObject proc = registry.lookupUnpackerByTypeId(type); if (proc != null) { ByteList byteList = new ByteList(payload, runtime.getEncodingService().getAscii8bitEncoding()); return proc.callMethod(runtime.getCurrentContext(), "call", runtime.newString(byteList)); diff --git a/ext/java/org/msgpack/jruby/Encoder.java b/ext/java/org/msgpack/jruby/Encoder.java index 879b75c0..f803b849 100644 --- a/ext/java/org/msgpack/jruby/Encoder.java +++ b/ext/java/org/msgpack/jruby/Encoder.java @@ -350,7 +350,7 @@ private void appendExtensionValue(ExtensionValue object) { private void appendOther(IRubyObject object, IRubyObject destination) { if (registry != null) { - IRubyObject[] pair = registry.lookup(object.getType()); + IRubyObject[] pair = registry.lookupPackerByClass(object.getType()); if (pair != null) { RubyString bytes = pair[0].callMethod(runtime.getCurrentContext(), "call", object).asString(); int type = (int) ((RubyFixnum) pair[1]).getLongValue(); diff --git a/ext/java/org/msgpack/jruby/ExtensionRegistry.java b/ext/java/org/msgpack/jruby/ExtensionRegistry.java index a8b63e65..aa49b607 100644 --- a/ext/java/org/msgpack/jruby/ExtensionRegistry.java +++ b/ext/java/org/msgpack/jruby/ExtensionRegistry.java @@ -14,6 +14,7 @@ public class ExtensionRegistry { private final Map extensionsByClass; private final Map extensionsByAncestor; + private final ExtensionEntry[] extensionsByTypeId; public ExtensionRegistry() { this(new HashMap()); @@ -22,26 +23,57 @@ public ExtensionRegistry() { private ExtensionRegistry(Map extensionsByClass) { this.extensionsByClass = new HashMap(extensionsByClass); this.extensionsByAncestor = new HashMap(); + this.extensionsByTypeId = new ExtensionEntry[256]; + for (ExtensionEntry entry : extensionsByClass.values()) { + if (entry.hasUnpacker()) { + extensionsByTypeId[entry.getTypeId() + 128] = entry; + } + } } public ExtensionRegistry dup() { return new ExtensionRegistry(extensionsByClass); } - public IRubyObject toRubyHash(ThreadContext ctx) { + public IRubyObject toInternalPackerRegistry(ThreadContext ctx) { RubyHash hash = RubyHash.newHash(ctx.getRuntime()); for (RubyClass extensionClass : extensionsByClass.keySet()) { - hash.put(extensionClass, extensionsByClass.get(extensionClass).toTuple(ctx)); + ExtensionEntry entry = extensionsByClass.get(extensionClass); + if (entry.hasPacker()) { + hash.put(extensionClass, entry.toPackerTuple(ctx)); + } } return hash; } - public void put(RubyClass cls, int typeId, IRubyObject proc, IRubyObject arg) { - extensionsByClass.put(cls, new ExtensionEntry(cls, typeId, proc, arg)); + public IRubyObject toInternalUnpackerRegistry(ThreadContext ctx) { + RubyHash hash = RubyHash.newHash(ctx.getRuntime()); + for (ExtensionEntry entry : extensionsByClass.values()) { + IRubyObject typeId = RubyFixnum.newFixnum(ctx.getRuntime(), entry.getTypeId()); + if (entry.hasUnpacker()) { + hash.put(typeId, entry.toUnpackerTuple(ctx)); + } + } + return hash; + } + + public void put(RubyClass cls, int typeId, IRubyObject packerProc, IRubyObject packerArg, IRubyObject unpackerProc, IRubyObject unpackerArg) { + ExtensionEntry entry = new ExtensionEntry(cls, typeId, packerProc, packerArg, unpackerProc, unpackerArg); + extensionsByClass.put(cls, entry); + extensionsByTypeId[typeId + 128] = entry; extensionsByAncestor.clear(); } - public IRubyObject[] lookup(RubyClass cls) { + public IRubyObject lookupUnpackerByTypeId(int typeId) { + ExtensionEntry e = extensionsByTypeId[typeId + 128]; + if (e != null && e.hasUnpacker()) { + return e.getUnpackerProc(); + } else { + return null; + } + } + + public IRubyObject[] lookupPackerByClass(RubyClass cls) { ExtensionEntry e = extensionsByClass.get(cls); if (e == null) { e = extensionsByAncestor.get(cls); @@ -52,10 +84,10 @@ public IRubyObject[] lookup(RubyClass cls) { extensionsByAncestor.put(e.getExtensionClass(), e); } } - if (e == null) { - return null; + if (e != null && e.hasPacker()) { + return e.toPackerProcTypeIdPair(cls.getRuntime().getCurrentContext()); } else { - return e.toProcTypePair(cls.getRuntime().getCurrentContext()); + return null; } } @@ -73,26 +105,54 @@ private ExtensionEntry findEntryByClassOrAncestor(final RubyClass cls) { private static class ExtensionEntry { private final RubyClass cls; private final int typeId; - private final IRubyObject proc; - private final IRubyObject arg; + private final IRubyObject packerProc; + private final IRubyObject packerArg; + private final IRubyObject unpackerProc; + private final IRubyObject unpackerArg; - public ExtensionEntry(RubyClass cls, int typeId, IRubyObject proc, IRubyObject arg) { + public ExtensionEntry(RubyClass cls, int typeId, IRubyObject packerProc, IRubyObject packerArg, IRubyObject unpackerProc, IRubyObject unpackerArg) { this.cls = cls; this.typeId = typeId; - this.proc = proc; - this.arg = arg; + this.packerProc = packerProc; + this.packerArg = packerArg; + this.unpackerProc = unpackerProc; + this.unpackerArg = unpackerArg; } public RubyClass getExtensionClass() { return cls; } - public RubyArray toTuple(ThreadContext ctx) { - return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {RubyFixnum.newFixnum(ctx.getRuntime(), typeId), proc, arg}); + public int getTypeId() { + return typeId; + } + + public boolean hasPacker() { + return packerProc != null; + } + + public boolean hasUnpacker() { + return unpackerProc != null; + } + + public IRubyObject getPackerProc() { + return packerProc; + } + + public IRubyObject getUnpackerProc() { + return unpackerProc; + } + + public RubyArray toPackerTuple(ThreadContext ctx) { + return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {cls, packerProc, packerArg}); + } + + public RubyArray toUnpackerTuple(ThreadContext ctx) { + return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {RubyFixnum.newFixnum(ctx.getRuntime(), typeId), unpackerProc, unpackerArg}); } - public IRubyObject[] toProcTypePair(ThreadContext ctx) { - return new IRubyObject[] {proc, RubyFixnum.newFixnum(ctx.getRuntime(), typeId)}; + public IRubyObject[] toPackerProcTypeIdPair(ThreadContext ctx) { + return new IRubyObject[] {packerProc, RubyFixnum.newFixnum(ctx.getRuntime(), typeId)}; } } } diff --git a/ext/java/org/msgpack/jruby/Factory.java b/ext/java/org/msgpack/jruby/Factory.java index 99c14b01..e5f5d0be 100644 --- a/ext/java/org/msgpack/jruby/Factory.java +++ b/ext/java/org/msgpack/jruby/Factory.java @@ -22,13 +22,13 @@ @JRubyClass(name="MessagePack::Factory") public class Factory extends RubyObject { - private Ruby runtime; - private ExtensionRegistry packerExtensionRegistry; - private Unpacker.ExtensionRegistry unpackerExtensionRegistry; + private final Ruby runtime; + private final ExtensionRegistry extensionRegistry; public Factory(Ruby runtime, RubyClass type) { super(runtime, type); this.runtime = runtime; + this.extensionRegistry = new ExtensionRegistry(); } static class FactoryAllocator implements ObjectAllocator { @@ -37,43 +37,31 @@ public IRubyObject allocate(Ruby runtime, RubyClass type) { } } + public ExtensionRegistry extensionRegistry() { + return extensionRegistry.dup(); + } + @JRubyMethod(name = "initialize") public IRubyObject initialize(ThreadContext ctx) { - this.packerExtensionRegistry = new ExtensionRegistry(); - this.unpackerExtensionRegistry = new Unpacker.ExtensionRegistry(ctx.getRuntime()); return this; } - public ExtensionRegistry packerRegistry() { - return this.packerExtensionRegistry.dup(); - } - - public Unpacker.ExtensionRegistry unpackerRegistry() { - return this.unpackerExtensionRegistry.dup(); - } - @JRubyMethod(name = "packer", optional = 1) public Packer packer(ThreadContext ctx, IRubyObject[] args) { - return Packer.newPacker(ctx, packerRegistry(), args); + return Packer.newPacker(ctx, extensionRegistry(), args); } @JRubyMethod(name = "unpacker", optional = 1) public Unpacker unpacker(ThreadContext ctx, IRubyObject[] args) { - return Unpacker.newUnpacker(ctx, unpackerRegistry(), args); + return Unpacker.newUnpacker(ctx, extensionRegistry(), args); } @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE) public IRubyObject registeredTypesInternal(ThreadContext ctx) { - Unpacker.ExtensionRegistry reg = unpackerRegistry(); - RubyHash mapping = RubyHash.newHash(ctx.getRuntime()); - for (int i = 0; i < 256; i++) { - if (reg.array[i] != null) { - mapping.fastASet(RubyFixnum.newFixnum(ctx.getRuntime(), i - 128), reg.array[i]); - } - } - - IRubyObject[] ary = { packerExtensionRegistry.toRubyHash(ctx), mapping }; - return RubyArray.newArray(ctx.getRuntime(), ary); + return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] { + extensionRegistry.toInternalPackerRegistry(ctx), + extensionRegistry.toInternalUnpackerRegistry(ctx) + }); } @JRubyMethod(name = "register_type", required = 2, optional = 1) @@ -122,8 +110,7 @@ public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args) { } } - this.packerExtensionRegistry.put(extClass, (int) typeId, packerProc, packerArg); - this.unpackerExtensionRegistry.put(extClass, (int) typeId, unpackerProc, unpackerArg); + extensionRegistry.put(extClass, (int) typeId, packerProc, packerArg, unpackerProc, unpackerArg); return runtime.getNil(); } diff --git a/ext/java/org/msgpack/jruby/MessagePackLibrary.java b/ext/java/org/msgpack/jruby/MessagePackLibrary.java index ade115f9..78ae1e34 100644 --- a/ext/java/org/msgpack/jruby/MessagePackLibrary.java +++ b/ext/java/org/msgpack/jruby/MessagePackLibrary.java @@ -117,7 +117,7 @@ public static IRubyObject pack(ThreadContext ctx, IRubyObject recv, IRubyObject[ @JRubyMethod(module = true, required = 1, optional = 1, alias = {"load"}) public static IRubyObject unpack(ThreadContext ctx, IRubyObject recv, IRubyObject[] args) { - Unpacker.ExtensionRegistry registry = MessagePackLibrary.defaultFactory.unpackerRegistry(); + ExtensionRegistry registry = MessagePackLibrary.defaultFactory.extensionRegistry(); boolean symbolizeKeys = false; if (args.length > 1 && !args[args.length - 1].isNil()) { RubyHash hash = args[args.length - 1].convertToHash(); diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index 3fd47537..aa10ed14 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -64,7 +64,7 @@ public IRubyObject isCompatibilityMode(ThreadContext ctx) { @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE) public IRubyObject registeredTypesInternal(ThreadContext ctx) { - return registry.toRubyHash(ctx); + return registry.toInternalPackerRegistry(ctx); } @JRubyMethod(name = "register_type", required = 2, optional = 1) @@ -98,7 +98,7 @@ public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args, final Blo } RubyClass extClass = (RubyClass) klass; - registry.put(extClass, (int) typeId, proc, arg); + registry.put(extClass, (int) typeId, proc, arg, null, null); return runtime.getNil(); } diff --git a/ext/java/org/msgpack/jruby/Unpacker.java b/ext/java/org/msgpack/jruby/Unpacker.java index 716a8b75..f2a622e7 100644 --- a/ext/java/org/msgpack/jruby/Unpacker.java +++ b/ext/java/org/msgpack/jruby/Unpacker.java @@ -26,7 +26,8 @@ @JRubyClass(name="MessagePack::Unpacker") public class Unpacker extends RubyObject { - public ExtensionRegistry registry; + private final ExtensionRegistry registry; + private IRubyObject stream; private IRubyObject data; private Decoder decoder; @@ -35,7 +36,12 @@ public class Unpacker extends RubyObject { private boolean allowUnknownExt; public Unpacker(Ruby runtime, RubyClass type) { + this(runtime, type, new ExtensionRegistry()); + } + + public Unpacker(Ruby runtime, RubyClass type, ExtensionRegistry registry) { super(runtime, type); + this.registry = registry; this.underflowErrorClass = runtime.getModule("MessagePack").getClass("UnderflowError"); } @@ -45,39 +51,6 @@ public IRubyObject allocate(Ruby runtime, RubyClass klass) { } } - static class ExtensionRegistry { - private Ruby runtime; - public RubyArray[] array; - - public ExtensionRegistry(Ruby runtime) { - this.runtime = runtime; - this.array = new RubyArray[256]; - } - - public ExtensionRegistry dup() { - ExtensionRegistry copy = new ExtensionRegistry(runtime); - copy.array = Arrays.copyOf(array, 256); - return copy; - } - - public void put(RubyClass klass, int typeId, IRubyObject proc, IRubyObject arg) { - IRubyObject k = klass; - if (k == null) { - k = runtime.getNil(); - } - RubyArray e = RubyArray.newArray(runtime, new IRubyObject[] { k, proc, arg }); - array[typeId + 128] = e; - } - - public IRubyObject lookup(int type) { - RubyArray e = array[type + 128]; - if (e == null) { - return null; - } - return e.entry(1); - } - } - @JRubyMethod(name = "initialize", optional = 1, visibility = PRIVATE) public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { symbolizeKeys = false; @@ -97,18 +70,12 @@ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { setStream(ctx, args[0]); } } - registry = new ExtensionRegistry(ctx.getRuntime()); return this; } - public void setExtensionRegistry(ExtensionRegistry registry) { - this.registry = registry; - } - public static Unpacker newUnpacker(ThreadContext ctx, ExtensionRegistry extRegistry, IRubyObject[] args) { - Unpacker unpacker = new Unpacker(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Unpacker")); + Unpacker unpacker = new Unpacker(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Unpacker"), extRegistry); unpacker.initialize(ctx, args); - unpacker.setExtensionRegistry(extRegistry); return unpacker; } @@ -124,13 +91,7 @@ public IRubyObject isAllowUnknownExt(ThreadContext ctx) { @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE) public IRubyObject registeredTypesInternal(ThreadContext ctx) { - RubyHash mapping = RubyHash.newHash(ctx.getRuntime()); - for (int i = 0; i < 256; i++) { - if (registry.array[i] != null) { - mapping.fastASet(RubyFixnum.newFixnum(ctx.getRuntime(), i - 128), registry.array[i].dup()); - } - } - return mapping; + return registry.toInternalUnpackerRegistry(ctx); } @JRubyMethod(name = "register_type", required = 1, optional = 2) @@ -163,7 +124,7 @@ public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args, final Blo throw runtime.newRangeError(String.format("integer %d too big to convert to `signed char'", typeId)); } - registry.put(extClass, (int) typeId, proc, arg); + registry.put(extClass, (int) typeId, null, null, proc, arg); return runtime.getNil(); } From 6ee1b67837173219a455fe93af44c46cbcf1cbab Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Fri, 23 Oct 2015 19:50:10 +0900 Subject: [PATCH 38/39] ExtensionRegistry must return typeId for Packer and class for Unpacker * class is lookup key for Packer (and also key for Hash from registered_types_internal) * typeId is lookup key for Unpacker (and also key for Hash from registered_types_internal) --- ext/java/org/msgpack/jruby/ExtensionRegistry.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/java/org/msgpack/jruby/ExtensionRegistry.java b/ext/java/org/msgpack/jruby/ExtensionRegistry.java index aa49b607..40125167 100644 --- a/ext/java/org/msgpack/jruby/ExtensionRegistry.java +++ b/ext/java/org/msgpack/jruby/ExtensionRegistry.java @@ -144,11 +144,11 @@ public IRubyObject getUnpackerProc() { } public RubyArray toPackerTuple(ThreadContext ctx) { - return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {cls, packerProc, packerArg}); + return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {RubyFixnum.newFixnum(ctx.getRuntime(), typeId), packerProc, packerArg}); } public RubyArray toUnpackerTuple(ThreadContext ctx) { - return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {RubyFixnum.newFixnum(ctx.getRuntime(), typeId), unpackerProc, unpackerArg}); + return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {cls, unpackerProc, unpackerArg}); } public IRubyObject[] toPackerProcTypeIdPair(ThreadContext ctx) { From 654de1b304314d5f16a928778b53b27965c7752d Mon Sep 17 00:00:00 2001 From: TAGOMORI Satoshi Date: Fri, 23 Oct 2015 19:52:25 +0900 Subject: [PATCH 39/39] List of registered ext types must be listed by typeId, not class Because ext types for unpacker may be registered without class (just only with proc) --- ext/java/org/msgpack/jruby/ExtensionRegistry.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/java/org/msgpack/jruby/ExtensionRegistry.java b/ext/java/org/msgpack/jruby/ExtensionRegistry.java index 40125167..2ffa4be3 100644 --- a/ext/java/org/msgpack/jruby/ExtensionRegistry.java +++ b/ext/java/org/msgpack/jruby/ExtensionRegistry.java @@ -48,9 +48,10 @@ public IRubyObject toInternalPackerRegistry(ThreadContext ctx) { public IRubyObject toInternalUnpackerRegistry(ThreadContext ctx) { RubyHash hash = RubyHash.newHash(ctx.getRuntime()); - for (ExtensionEntry entry : extensionsByClass.values()) { - IRubyObject typeId = RubyFixnum.newFixnum(ctx.getRuntime(), entry.getTypeId()); - if (entry.hasUnpacker()) { + for (int typeIdIndex = 0 ; typeIdIndex < 256 ; typeIdIndex++) { + ExtensionEntry entry = extensionsByTypeId[typeIdIndex]; + if (entry != null && entry.hasUnpacker()) { + IRubyObject typeId = RubyFixnum.newFixnum(ctx.getRuntime(), typeIdIndex - 128); hash.put(typeId, entry.toUnpackerTuple(ctx)); } }