msgpack 1.2.6 → 1.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +56 -0
  3. data/.gitignore +3 -1
  4. data/.rubocop.yml +4 -1
  5. data/ChangeLog +59 -0
  6. data/Gemfile +3 -0
  7. data/README.md +242 -0
  8. data/Rakefile +3 -8
  9. data/doclib/msgpack/factory.rb +1 -0
  10. data/doclib/msgpack/packer.rb +20 -0
  11. data/doclib/msgpack/time.rb +22 -0
  12. data/doclib/msgpack/timestamp.rb +44 -0
  13. data/doclib/msgpack.rb +2 -2
  14. data/ext/java/org/msgpack/jruby/Buffer.java +21 -16
  15. data/ext/java/org/msgpack/jruby/Decoder.java +29 -10
  16. data/ext/java/org/msgpack/jruby/Encoder.java +38 -19
  17. data/ext/java/org/msgpack/jruby/ExtensionRegistry.java +9 -9
  18. data/ext/java/org/msgpack/jruby/ExtensionValue.java +5 -8
  19. data/ext/java/org/msgpack/jruby/Factory.java +8 -3
  20. data/ext/java/org/msgpack/jruby/Packer.java +31 -8
  21. data/ext/java/org/msgpack/jruby/Unpacker.java +40 -27
  22. data/ext/msgpack/buffer.c +4 -16
  23. data/ext/msgpack/buffer.h +60 -5
  24. data/ext/msgpack/compat.h +1 -12
  25. data/ext/msgpack/extconf.rb +39 -7
  26. data/ext/msgpack/factory_class.c +10 -5
  27. data/ext/msgpack/packer.c +18 -5
  28. data/ext/msgpack/packer.h +0 -16
  29. data/ext/msgpack/packer_class.c +21 -9
  30. data/ext/msgpack/packer_ext_registry.c +0 -22
  31. data/ext/msgpack/unpacker.c +41 -49
  32. data/ext/msgpack/unpacker.h +8 -0
  33. data/ext/msgpack/unpacker_class.c +23 -13
  34. data/lib/msgpack/symbol.rb +14 -4
  35. data/lib/msgpack/time.rb +29 -0
  36. data/lib/msgpack/timestamp.rb +76 -0
  37. data/lib/msgpack/version.rb +4 -7
  38. data/lib/msgpack.rb +8 -10
  39. data/msgpack.gemspec +3 -7
  40. data/spec/cruby/buffer_spec.rb +6 -1
  41. data/spec/factory_spec.rb +17 -0
  42. data/spec/msgpack_spec.rb +44 -1
  43. data/spec/packer_spec.rb +54 -0
  44. data/spec/spec_helper.rb +27 -0
  45. data/spec/timestamp_spec.rb +161 -0
  46. data/spec/unpacker_spec.rb +113 -1
  47. metadata +19 -51
  48. data/.travis.yml +0 -41
  49. data/README.rdoc +0 -201
@@ -38,36 +38,38 @@ public class Decoder implements Iterator<IRubyObject> {
38
38
  private ExtensionRegistry registry;
39
39
  private ByteBuffer buffer;
40
40
  private boolean symbolizeKeys;
41
+ private boolean freeze;
41
42
  private boolean allowUnknownExt;
42
43
 
43
44
  public Decoder(Ruby runtime) {
44
- this(runtime, null, new byte[] {}, 0, 0, false, false);
45
+ this(runtime, null, new byte[] {}, 0, 0, false, false, false);
45
46
  }
46
47
 
47
48
  public Decoder(Ruby runtime, ExtensionRegistry registry) {
48
- this(runtime, registry, new byte[] {}, 0, 0, false, false);
49
+ this(runtime, registry, new byte[] {}, 0, 0, false, false, false);
49
50
  }
50
51
 
51
52
  public Decoder(Ruby runtime, byte[] bytes) {
52
- this(runtime, null, bytes, 0, bytes.length, false, false);
53
+ this(runtime, null, bytes, 0, bytes.length, false, false, false);
53
54
  }
54
55
 
55
56
  public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes) {
56
- this(runtime, registry, bytes, 0, bytes.length, false, false);
57
+ this(runtime, registry, bytes, 0, bytes.length, false, false, false);
57
58
  }
58
59
 
59
- public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes, boolean symbolizeKeys, boolean allowUnknownExt) {
60
- this(runtime, registry, bytes, 0, bytes.length, symbolizeKeys, allowUnknownExt);
60
+ public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes, boolean symbolizeKeys, boolean freeze, boolean allowUnknownExt) {
61
+ this(runtime, registry, bytes, 0, bytes.length, symbolizeKeys, freeze, allowUnknownExt);
61
62
  }
62
63
 
63
64
  public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes, int offset, int length) {
64
- this(runtime, registry, bytes, offset, length, false, false);
65
+ this(runtime, registry, bytes, offset, length, false, false, false);
65
66
  }
66
67
 
67
- public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes, int offset, int length, boolean symbolizeKeys, boolean allowUnknownExt) {
68
+ public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes, int offset, int length, boolean symbolizeKeys, boolean freeze, boolean allowUnknownExt) {
68
69
  this.runtime = runtime;
69
70
  this.registry = registry;
70
71
  this.symbolizeKeys = symbolizeKeys;
72
+ this.freeze = freeze;
71
73
  this.allowUnknownExt = allowUnknownExt;
72
74
  this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding();
73
75
  this.utf8Encoding = UTF8Encoding.INSTANCE;
@@ -118,7 +120,11 @@ public class Decoder implements Iterator<IRubyObject> {
118
120
  private IRubyObject consumeString(int size, Encoding encoding) {
119
121
  byte[] bytes = readBytes(size);
120
122
  ByteList byteList = new ByteList(bytes, encoding);
121
- return runtime.newString(byteList);
123
+ RubyString string = runtime.newString(byteList);
124
+ if (this.freeze) {
125
+ string = runtime.freezeAndDedupString(string);
126
+ }
127
+ return string;
122
128
  }
123
129
 
124
130
  private IRubyObject consumeArray(int size) {
@@ -133,9 +139,14 @@ public class Decoder implements Iterator<IRubyObject> {
133
139
  RubyHash hash = RubyHash.newHash(runtime);
134
140
  for (int i = 0; i < size; i++) {
135
141
  IRubyObject key = next();
136
- if (this.symbolizeKeys && key instanceof RubyString) {
142
+ if (key instanceof RubyString) {
143
+ if (this.symbolizeKeys) {
137
144
  key = ((RubyString) key).intern();
145
+ } else {
146
+ key = runtime.freezeAndDedupString((RubyString) key);
147
+ }
138
148
  }
149
+
139
150
  hash.fastASet(key, next());
140
151
  }
141
152
  return hash;
@@ -220,6 +231,14 @@ public class Decoder implements Iterator<IRubyObject> {
220
231
 
221
232
  @Override
222
233
  public IRubyObject next() {
234
+ IRubyObject next = consumeNext();
235
+ if (freeze) {
236
+ next.setFrozen(true);
237
+ }
238
+ return next;
239
+ }
240
+
241
+ private IRubyObject consumeNext() {
223
242
  int position = buffer.position();
224
243
  try {
225
244
  byte b = buffer.get();
@@ -91,6 +91,11 @@ public class Encoder {
91
91
  return readRubyString();
92
92
  }
93
93
 
94
+ public IRubyObject encodeBinHeader(int size) {
95
+ appendStringHeader(size, true);
96
+ return readRubyString();
97
+ }
98
+
94
99
  public IRubyObject encodeFloat32(RubyNumeric numeric) {
95
100
  appendFloat32(numeric);
96
101
  return readRubyString();
@@ -114,7 +119,9 @@ public class Encoder {
114
119
  } else if (object instanceof RubyFloat) {
115
120
  appendFloat((RubyFloat) object);
116
121
  } else if (object instanceof RubyString) {
117
- appendString((RubyString) object);
122
+ if (object.getType() == runtime.getString() || !tryAppendWithExtTypeLookup(object)) {
123
+ appendString((RubyString) object);
124
+ }
118
125
  } else if (object instanceof RubySymbol) {
119
126
  if (hasSymbolExtType) {
120
127
  appendOther(object, destination);
@@ -122,9 +129,13 @@ public class Encoder {
122
129
  appendString(((RubySymbol) object).asString());
123
130
  }
124
131
  } else if (object instanceof RubyArray) {
125
- appendArray((RubyArray) object);
132
+ if (object.getType() == runtime.getArray() || !tryAppendWithExtTypeLookup(object)) {
133
+ appendArray((RubyArray) object);
134
+ }
126
135
  } else if (object instanceof RubyHash) {
127
- appendHash((RubyHash) object);
136
+ if (object.getType() == runtime.getHash() || !tryAppendWithExtTypeLookup(object)) {
137
+ appendHash((RubyHash) object);
138
+ }
128
139
  } else if (object instanceof ExtensionValue) {
129
140
  appendExtensionValue((ExtensionValue) object);
130
141
  } else {
@@ -148,7 +159,7 @@ public class Encoder {
148
159
  }
149
160
 
150
161
  private void appendInteger(RubyInteger object) {
151
- long value = ((RubyInteger) object).getLongValue();
162
+ long value = object.getLongValue();
152
163
  if (value < 0) {
153
164
  if (value < Short.MIN_VALUE) {
154
165
  if (value < Integer.MIN_VALUE) {
@@ -221,14 +232,7 @@ public class Encoder {
221
232
  buffer.putFloat(value);
222
233
  }
223
234
 
224
- private void appendString(RubyString object) {
225
- Encoding encoding = object.getEncoding();
226
- boolean binary = !compatibilityMode && encoding == binaryEncoding;
227
- if (encoding != utf8Encoding && encoding != binaryEncoding) {
228
- object = (RubyString) ((RubyString) object).encode(runtime.getCurrentContext(), runtime.getEncodingService().getEncoding(utf8Encoding));
229
- }
230
- ByteList bytes = object.getByteList();
231
- int length = bytes.length();
235
+ private void appendStringHeader(int length, boolean binary) {
232
236
  if (length < 32 && !binary) {
233
237
  ensureRemainingCapacity(1 + length);
234
238
  buffer.put((byte) (length | FIXSTR));
@@ -243,17 +247,28 @@ public class Encoder {
243
247
  } else {
244
248
  ensureRemainingCapacity(5 + length);
245
249
  buffer.put(binary ? BIN32 : STR32);
246
- buffer.putInt((int) length);
250
+ buffer.putInt(length);
247
251
  }
252
+ }
253
+
254
+ private void appendString(RubyString object) {
255
+ Encoding encoding = object.getEncoding();
256
+ boolean binary = !compatibilityMode && encoding == binaryEncoding;
257
+ if (encoding != utf8Encoding && encoding != binaryEncoding) {
258
+ object = (RubyString)(object).encode(runtime.getCurrentContext(), runtime.getEncodingService().getEncoding(utf8Encoding));
259
+ }
260
+ ByteList bytes = object.getByteList();
261
+ int length = bytes.length();
262
+ appendStringHeader(length, binary);
248
263
  buffer.put(bytes.unsafeBytes(), bytes.begin(), length);
249
264
  }
250
265
 
251
- private void appendArray(RubyArray object) {
266
+ private void appendArray(RubyArray<?> object) {
252
267
  appendArrayHeader(object);
253
268
  appendArrayElements(object);
254
269
  }
255
270
 
256
- private void appendArrayHeader(RubyArray object) {
271
+ private void appendArrayHeader(RubyArray<?> object) {
257
272
  appendArrayHeader(object.size());
258
273
  }
259
274
 
@@ -272,7 +287,7 @@ public class Encoder {
272
287
  }
273
288
  }
274
289
 
275
- private void appendArrayElements(RubyArray object) {
290
+ private void appendArrayElements(RubyArray<?> object) {
276
291
  int size = object.size();
277
292
  for (int i = 0; i < size; i++) {
278
293
  appendObject(object.eltOk(i));
@@ -374,7 +389,7 @@ public class Encoder {
374
389
  appendExt((int) type, payloadBytes);
375
390
  }
376
391
 
377
- private void appendOther(IRubyObject object, IRubyObject destination) {
392
+ private boolean tryAppendWithExtTypeLookup(IRubyObject object) {
378
393
  if (registry != null) {
379
394
  RubyModule lookupClass;
380
395
 
@@ -389,10 +404,14 @@ public class Encoder {
389
404
  RubyString bytes = pair[0].callMethod(runtime.getCurrentContext(), "call", object).asString();
390
405
  int type = (int) ((RubyFixnum) pair[1]).getLongValue();
391
406
  appendExt(type, bytes.getByteList());
392
- return;
407
+ return true;
393
408
  }
394
409
  }
395
- appendCustom(object, destination);
410
+ return false;
411
+ }
412
+
413
+ private void appendOther(IRubyObject object, IRubyObject destination) {
414
+ if (!tryAppendWithExtTypeLookup(object)) { appendCustom(object, destination); }
396
415
  }
397
416
 
398
417
  private void appendCustom(IRubyObject object, IRubyObject destination) {
@@ -37,7 +37,7 @@ public class ExtensionRegistry {
37
37
  }
38
38
 
39
39
  public IRubyObject toInternalPackerRegistry(ThreadContext ctx) {
40
- RubyHash hash = RubyHash.newHash(ctx.getRuntime());
40
+ RubyHash hash = RubyHash.newHash(ctx.runtime);
41
41
  for (RubyModule extensionModule : extensionsByModule.keySet()) {
42
42
  ExtensionEntry entry = extensionsByModule.get(extensionModule);
43
43
  if (entry.hasPacker()) {
@@ -48,11 +48,11 @@ public class ExtensionRegistry {
48
48
  }
49
49
 
50
50
  public IRubyObject toInternalUnpackerRegistry(ThreadContext ctx) {
51
- RubyHash hash = RubyHash.newHash(ctx.getRuntime());
51
+ RubyHash hash = RubyHash.newHash(ctx.runtime);
52
52
  for (int typeIdIndex = 0 ; typeIdIndex < 256 ; typeIdIndex++) {
53
53
  ExtensionEntry entry = extensionsByTypeId[typeIdIndex];
54
54
  if (entry != null && entry.hasUnpacker()) {
55
- IRubyObject typeId = RubyFixnum.newFixnum(ctx.getRuntime(), typeIdIndex - 128);
55
+ IRubyObject typeId = RubyFixnum.newFixnum(ctx.runtime, typeIdIndex - 128);
56
56
  hash.put(typeId, entry.toUnpackerTuple(ctx));
57
57
  }
58
58
  }
@@ -124,7 +124,7 @@ public class ExtensionRegistry {
124
124
  private ExtensionEntry findEntryByModuleOrAncestor(final RubyModule mod) {
125
125
  ThreadContext ctx = mod.getRuntime().getCurrentContext();
126
126
  for (RubyModule extensionModule : extensionsByModule.keySet()) {
127
- RubyArray ancestors = (RubyArray) mod.callMethod(ctx, "ancestors");
127
+ RubyArray<?> ancestors = (RubyArray)mod.callMethod(ctx, "ancestors");
128
128
  if (ancestors.callMethod(ctx, "include?", extensionModule).isTrue()) {
129
129
  return extensionsByModule.get(extensionModule);
130
130
  }
@@ -173,16 +173,16 @@ public class ExtensionRegistry {
173
173
  return unpackerProc;
174
174
  }
175
175
 
176
- public RubyArray toPackerTuple(ThreadContext ctx) {
177
- return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {RubyFixnum.newFixnum(ctx.getRuntime(), typeId), packerProc, packerArg});
176
+ public RubyArray<?> toPackerTuple(ThreadContext ctx) {
177
+ return ctx.runtime.newArray(new IRubyObject[] {ctx.runtime.newFixnum(typeId), packerProc, packerArg});
178
178
  }
179
179
 
180
- public RubyArray toUnpackerTuple(ThreadContext ctx) {
181
- return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {mod, unpackerProc, unpackerArg});
180
+ public RubyArray<?> toUnpackerTuple(ThreadContext ctx) {
181
+ return ctx.runtime.newArray(new IRubyObject[] {mod, unpackerProc, unpackerArg});
182
182
  }
183
183
 
184
184
  public IRubyObject[] toPackerProcTypeIdPair(ThreadContext ctx) {
185
- return new IRubyObject[] {packerProc, RubyFixnum.newFixnum(ctx.getRuntime(), typeId)};
185
+ return new IRubyObject[] {packerProc, ctx.runtime.newFixnum(typeId)};
186
186
  }
187
187
  }
188
188
  }
@@ -25,6 +25,7 @@ import static org.msgpack.jruby.Types.*;
25
25
 
26
26
  @JRubyClass(name="MessagePack::ExtensionValue")
27
27
  public class ExtensionValue extends RubyObject {
28
+ private static final long serialVersionUID = 8451274621449322492L;
28
29
  private final Encoding binaryEncoding;
29
30
 
30
31
  private RubyFixnum type;
@@ -77,12 +78,10 @@ public class ExtensionValue extends RubyObject {
77
78
  }
78
79
  if (o instanceof ExtensionValue) {
79
80
  ExtensionValue other = (ExtensionValue) o;
80
- if (!this.type.eql_p(other.type).isTrue())
81
+ if (!this.type.eql_p(other.type).isTrue()) {
81
82
  return runtime.getFalse();
82
- if (runtime.is1_8()) {
83
- return this.payload.str_eql_p(ctx, other.payload);
84
83
  } else {
85
- return this.payload.str_eql_p19(ctx, other.payload);
84
+ return this.payload.str_eql_p(ctx, other.payload);
86
85
  }
87
86
  }
88
87
  return runtime.getFalse();
@@ -96,12 +95,10 @@ public class ExtensionValue extends RubyObject {
96
95
  }
97
96
  if (o instanceof ExtensionValue) {
98
97
  ExtensionValue other = (ExtensionValue) o;
99
- if (!this.type.op_equal(ctx, other.type).isTrue())
98
+ if (!this.type.op_equal(ctx, other.type).isTrue()) {
100
99
  return runtime.getFalse();
101
- if (runtime.is1_8()) {
102
- return this.payload.op_equal(ctx, other.payload);
103
100
  } else {
104
- return this.payload.op_equal19(ctx, other.payload);
101
+ return this.payload.op_equal(ctx, other.payload);
105
102
  }
106
103
  }
107
104
  return runtime.getFalse();
@@ -23,6 +23,7 @@ import static org.jruby.runtime.Visibility.PRIVATE;
23
23
 
24
24
  @JRubyClass(name="MessagePack::Factory")
25
25
  public class Factory extends RubyObject {
26
+ private static final long serialVersionUID = 8441284623445322492L;
26
27
  private final Ruby runtime;
27
28
  private final ExtensionRegistry extensionRegistry;
28
29
  private boolean hasSymbolExtType;
@@ -54,14 +55,14 @@ public class Factory extends RubyObject {
54
55
  return Packer.newPacker(ctx, extensionRegistry(), hasSymbolExtType, args);
55
56
  }
56
57
 
57
- @JRubyMethod(name = "unpacker", optional = 1)
58
+ @JRubyMethod(name = "unpacker", optional = 2)
58
59
  public Unpacker unpacker(ThreadContext ctx, IRubyObject[] args) {
59
60
  return Unpacker.newUnpacker(ctx, extensionRegistry(), args);
60
61
  }
61
62
 
62
63
  @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE)
63
64
  public IRubyObject registeredTypesInternal(ThreadContext ctx) {
64
- return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {
65
+ return RubyArray.newArray(ctx.runtime, new IRubyObject[] {
65
66
  extensionRegistry.toInternalPackerRegistry(ctx),
66
67
  extensionRegistry.toInternalUnpackerRegistry(ctx)
67
68
  });
@@ -69,7 +70,7 @@ public class Factory extends RubyObject {
69
70
 
70
71
  @JRubyMethod(name = "register_type", required = 2, optional = 1)
71
72
  public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args) {
72
- Ruby runtime = ctx.getRuntime();
73
+ Ruby runtime = ctx.runtime;
73
74
  IRubyObject type = args[0];
74
75
  IRubyObject mod = args[1];
75
76
 
@@ -88,6 +89,10 @@ public class Factory extends RubyObject {
88
89
  RubyHash options = (RubyHash) args[args.length - 1];
89
90
  packerArg = options.fastARef(runtime.newSymbol("packer"));
90
91
  unpackerArg = options.fastARef(runtime.newSymbol("unpacker"));
92
+ IRubyObject optimizedSymbolsParsingArg = options.fastARef(runtime.newSymbol("optimized_symbols_parsing"));
93
+ if (optimizedSymbolsParsingArg != null && optimizedSymbolsParsingArg.isTrue()) {
94
+ throw runtime.newArgumentError("JRuby implementation does not support the optimized_symbols_parsing option");
95
+ }
91
96
  } else {
92
97
  throw runtime.newArgumentError(String.format("expected Hash but found %s.", args[args.length - 1].getType().getName()));
93
98
  }
@@ -21,14 +21,18 @@ import org.jruby.util.ByteList;
21
21
  import org.jruby.util.TypeConverter;
22
22
  import org.msgpack.jruby.ExtensionValue;
23
23
 
24
+ import org.jcodings.Encoding;
25
+
24
26
  import static org.jruby.runtime.Visibility.PRIVATE;
25
27
 
26
28
  @JRubyClass(name="MessagePack::Packer")
27
29
  public class Packer extends RubyObject {
30
+ private static final long serialVersionUID = 8451274621499362492L;
28
31
  public ExtensionRegistry registry;
29
32
  private Buffer buffer;
30
33
  private Encoder encoder;
31
34
  private boolean hasSymbolExtType;
35
+ private Encoding binaryEncoding;
32
36
 
33
37
  public Packer(Ruby runtime, RubyClass type, ExtensionRegistry registry, boolean hasSymbolExtType) {
34
38
  super(runtime, type);
@@ -45,9 +49,10 @@ public class Packer extends RubyObject {
45
49
  @JRubyMethod(name = "initialize", optional = 2)
46
50
  public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
47
51
  boolean compatibilityMode = false;
52
+ Ruby runtime = ctx.runtime;
48
53
  if (args.length > 0 && args[args.length - 1] instanceof RubyHash) {
49
54
  RubyHash options = (RubyHash) args[args.length - 1];
50
- IRubyObject mode = options.fastARef(ctx.getRuntime().newSymbol("compatibility_mode"));
55
+ IRubyObject mode = options.fastARef(runtime.newSymbol("compatibility_mode"));
51
56
  compatibilityMode = (mode != null) && mode.isTrue();
52
57
  }
53
58
  if (registry == null) {
@@ -55,21 +60,22 @@ public class Packer extends RubyObject {
55
60
  // registry is already initialized (and somthing might be registered) when newPacker from Factory
56
61
  this.registry = new ExtensionRegistry();
57
62
  }
58
- this.encoder = new Encoder(ctx.getRuntime(), compatibilityMode, registry, hasSymbolExtType);
59
- this.buffer = new Buffer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Buffer"));
63
+ this.encoder = new Encoder(runtime, compatibilityMode, registry, hasSymbolExtType);
64
+ this.buffer = new Buffer(runtime, runtime.getModule("MessagePack").getClass("Buffer"));
60
65
  this.buffer.initialize(ctx, args);
66
+ this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding();
61
67
  return this;
62
68
  }
63
69
 
64
70
  public static Packer newPacker(ThreadContext ctx, ExtensionRegistry extRegistry, boolean hasSymbolExtType, IRubyObject[] args) {
65
- Packer packer = new Packer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Packer"), extRegistry, hasSymbolExtType);
71
+ Packer packer = new Packer(ctx.runtime, ctx.runtime.getModule("MessagePack").getClass("Packer"), extRegistry, hasSymbolExtType);
66
72
  packer.initialize(ctx, args);
67
73
  return packer;
68
74
  }
69
75
 
70
76
  @JRubyMethod(name = "compatibility_mode?")
71
77
  public IRubyObject isCompatibilityMode(ThreadContext ctx) {
72
- return encoder.isCompatibilityMode() ? ctx.getRuntime().getTrue() : ctx.getRuntime().getFalse();
78
+ return encoder.isCompatibilityMode() ? ctx.runtime.getTrue() : ctx.runtime.getFalse();
73
79
  }
74
80
 
75
81
  @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE)
@@ -79,7 +85,7 @@ public class Packer extends RubyObject {
79
85
 
80
86
  @JRubyMethod(name = "register_type", required = 2, optional = 1)
81
87
  public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args, final Block block) {
82
- Ruby runtime = ctx.getRuntime();
88
+ Ruby runtime = ctx.runtime;
83
89
  IRubyObject type = args[0];
84
90
  IRubyObject mod = args[1];
85
91
 
@@ -141,6 +147,13 @@ public class Packer extends RubyObject {
141
147
  return write(ctx, obj);
142
148
  }
143
149
 
150
+ @JRubyMethod(name = "write_bin")
151
+ public IRubyObject writeBin(ThreadContext ctx, IRubyObject obj) {
152
+ checkType(ctx, obj, org.jruby.RubyString.class);
153
+ obj = ((org.jruby.RubyString) obj).encode(ctx, ctx.runtime.getEncodingService().getEncoding(binaryEncoding));
154
+ return write(ctx, obj);
155
+ }
156
+
144
157
  @JRubyMethod(name = "write_hash")
145
158
  public IRubyObject writeHash(ThreadContext ctx, IRubyObject obj) {
146
159
  checkType(ctx, obj, org.jruby.RubyHash.class);
@@ -171,12 +184,12 @@ public class Packer extends RubyObject {
171
184
 
172
185
  @JRubyMethod(name = "write_true")
173
186
  public IRubyObject writeTrue(ThreadContext ctx) {
174
- return write(ctx, ctx.getRuntime().getTrue());
187
+ return write(ctx, ctx.runtime.getTrue());
175
188
  }
176
189
 
177
190
  @JRubyMethod(name = "write_false")
178
191
  public IRubyObject writeFalse(ThreadContext ctx) {
179
- return write(ctx, ctx.getRuntime().getFalse());
192
+ return write(ctx, ctx.runtime.getFalse());
180
193
  }
181
194
 
182
195
  @JRubyMethod(name = "write_nil")
@@ -209,8 +222,18 @@ public class Packer extends RubyObject {
209
222
  return this;
210
223
  }
211
224
 
225
+ @JRubyMethod(name = "write_bin_header")
226
+ public IRubyObject writeBinHeader(ThreadContext ctx, IRubyObject size) {
227
+ int s = (int) size.convertToInteger().getLongValue();
228
+ buffer.write(ctx, encoder.encodeBinHeader(s));
229
+ return this;
230
+ }
231
+
212
232
  @JRubyMethod(name = "full_pack")
213
233
  public IRubyObject fullPack(ThreadContext ctx) {
234
+ if (buffer.hasIo()) {
235
+ return null;
236
+ }
214
237
  return toS(ctx);
215
238
  }
216
239