diff --git a/ext/java/org/msgpack/jruby/Buffer.java b/ext/java/org/msgpack/jruby/Buffer.java index e6c4d14c..3372f90e 100644 --- a/ext/java/org/msgpack/jruby/Buffer.java +++ b/ext/java/org/msgpack/jruby/Buffer.java @@ -21,7 +21,6 @@ @JRubyClass(name="MessagePack::Buffer") public class Buffer extends RubyObject { - private RubyHash options; private IRubyObject io; private ByteBuffer buffer; private boolean writeMode; @@ -46,9 +45,6 @@ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { if (args[0].respondsTo("read") && args[0].respondsTo("write")) { this.io = args[0]; } - if (args[args.length - 1] instanceof RubyHash) { - this.options = (RubyHash) args[args.length - 1]; - } } this.buffer = ByteBuffer.allocate(CACHE_LINE_SIZE - ARRAY_HEADER_SIZE); this.writeMode = true; diff --git a/ext/java/org/msgpack/jruby/Encoder.java b/ext/java/org/msgpack/jruby/Encoder.java index 6f94c6d9..ac9fda05 100644 --- a/ext/java/org/msgpack/jruby/Encoder.java +++ b/ext/java/org/msgpack/jruby/Encoder.java @@ -34,14 +34,16 @@ public class Encoder { private final Ruby runtime; private final Encoding binaryEncoding; private final Encoding utf8Encoding; + private final boolean compatibilityMode; private ByteBuffer buffer; - public Encoder(Ruby runtime) { + public Encoder(Ruby runtime, boolean compatibilityMode) { 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; } private void ensureRemainingCapacity(int c) { @@ -190,7 +192,7 @@ private void appendFloat(RubyFloat object) { private void appendString(RubyString object) { Encoding encoding = object.getEncoding(); - boolean binary = encoding == binaryEncoding; + boolean binary = !compatibilityMode && encoding == binaryEncoding; if (encoding != utf8Encoding && encoding != binaryEncoding) { object = (RubyString) ((RubyString) object).encode(runtime.getCurrentContext(), runtime.getEncodingService().getEncoding(utf8Encoding)); } @@ -199,7 +201,7 @@ private void appendString(RubyString object) { if (length < 32 && !binary) { ensureRemainingCapacity(1 + length); buffer.put((byte) (length | FIXSTR)); - } else if (length <= 0xff) { + } else if (length <= 0xff && !compatibilityMode) { ensureRemainingCapacity(2 + length); buffer.put(binary ? BIN8 : STR8); buffer.put((byte) length); diff --git a/ext/java/org/msgpack/jruby/Packer.java b/ext/java/org/msgpack/jruby/Packer.java index 890ef9ef..394db049 100644 --- a/ext/java/org/msgpack/jruby/Packer.java +++ b/ext/java/org/msgpack/jruby/Packer.java @@ -32,7 +32,12 @@ public IRubyObject allocate(Ruby runtime, RubyClass type) { @JRubyMethod(name = "initialize", optional = 2) public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) { - this.encoder = new Encoder(ctx.getRuntime()); + 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(); + } + this.encoder = new Encoder(ctx.getRuntime(), compatibilityMode); this.buffer = new Buffer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Buffer")); this.buffer.initialize(ctx, args); return this; diff --git a/ext/msgpack/packer.h b/ext/msgpack/packer.h index aae6620d..8d152096 100644 --- a/ext/msgpack/packer.h +++ b/ext/msgpack/packer.h @@ -32,6 +32,7 @@ struct msgpack_packer_t { VALUE io; ID io_write_all_method; + bool compatibility_mode; ID to_msgpack_method; VALUE to_msgpack_arg; @@ -270,7 +271,7 @@ static inline void msgpack_packer_write_raw_header(msgpack_packer_t* pk, unsigne msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 1); unsigned char h = 0xa0 | (uint8_t) n; msgpack_buffer_write_1(PACKER_BUFFER_(pk), h); - } else if(n < 256) { + } else if(n < 256 && !pk->compatibility_mode) { msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 2); unsigned char be = (uint8_t) n; msgpack_buffer_write_byte_and_data(PACKER_BUFFER_(pk), 0xd9, (const void*)&be, 1); @@ -368,7 +369,7 @@ static inline void msgpack_packer_write_string_value(msgpack_packer_t* pk, VALUE #ifdef COMPAT_HAVE_ENCODING int encindex = ENCODING_GET(v); - if(msgpack_packer_is_binary(v, encindex)) { + if(msgpack_packer_is_binary(v, encindex) && !pk->compatibility_mode) { /* write ASCII-8BIT string using Binary type */ msgpack_packer_write_bin_header(pk, (unsigned int)len); msgpack_buffer_append_string(PACKER_BUFFER_(pk), v); diff --git a/ext/msgpack/packer_class.c b/ext/msgpack/packer_class.c index 7352f61b..26848505 100644 --- a/ext/msgpack/packer_class.c +++ b/ext/msgpack/packer_class.c @@ -221,6 +221,11 @@ VALUE MessagePack_pack(int argc, VALUE* argv) VALUE self = Packer_alloc(cMessagePack_Packer); PACKER(self, pk); + + if (options != Qnil) { + pk->compatibility_mode = RTEST(rb_hash_aref(options, ID2SYM(rb_intern("compatibility_mode")))); + } + //msgpack_packer_reset(s_packer); //msgpack_buffer_reset_io(PACKER_BUFFER_(s_packer)); diff --git a/spec/cruby/packer_spec.rb b/spec/cruby/packer_spec.rb index 40cb6b0f..d5619b63 100644 --- a/spec/cruby/packer_spec.rb +++ b/spec/cruby/packer_spec.rb @@ -116,5 +116,19 @@ def to_msgpack(pk=nil) 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") + 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/jruby/msgpack_spec.rb b/spec/jruby/msgpack_spec.rb index 6893d65f..e8d0610f 100644 --- a/spec/jruby/msgpack_spec.rb +++ b/spec/jruby/msgpack_spec.rb @@ -36,9 +36,15 @@ def asciienc(str) ['negative floats', -2.1, "\xCB\xC0\x00\xCC\xCC\xCC\xCC\xCC\xCD"] ], 'strings' => [ - ['strings', utf8enc('hello world'), "\xABhello world"], + ['tiny strings', utf8enc('hello world'), "\xABhello world"], + ['short strings', utf8enc('hello' * 5), "\xB9hellohellohellohellohello"], ['empty strings', utf8enc(''), "\xA0"] ], + 'binary strings' => [ + ['tiny strings', asciienc('hello world'), "\xC4\vhello world"], + ['short strings', asciienc('hello' * 5), "\xC4\x19hellohellohellohellohello"], + ['empty strings', asciienc(''), "\xC4\x00"] + ], 'arrays' => [ ['empty arrays', [], "\x90"], ['arrays with strings', [utf8enc("hello"), utf8enc("world")], "\x92\xA5hello\xA5world"], @@ -139,4 +145,18 @@ def asciienc(str) packed.index("w\xC3\xA5rld").should_not be_nil end 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") + 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