msgpack 0.7.0dev1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e934ddd7840719b64ca248c14b5cb3e81ed4c0fd
4
- data.tar.gz: 2bd6ef14c3c873241be2911fe0666dc5526e9351
3
+ metadata.gz: 08b929d16e3f5eb48b0de6a10041092be2510361
4
+ data.tar.gz: 34c606ea1c703c22f94d1295b5648f4430fd69ae
5
5
  SHA512:
6
- metadata.gz: 857b591765e1b4bb0a0efac427e729e278168eee8bdfdd47bc9b1a86f30edfdecedc4a81626709da5d21fb25988485c243532d3d87d9c3cde1b9a47c30c2624b
7
- data.tar.gz: a616c639a3c3cc8029d1bc93d8821f60ad85799995016e7cf579a0875380de21b598a0dbad608db728fc8301ce993453db1d7605ae189e51af2aa04f2e712624
6
+ metadata.gz: 4ec69a5b4e904359b7c1d1a15f37ee2e57fa5e340fbab0e658cf2d476d736d41100cd350ce580f52afbbb88721640be3bd120583adfd6f179aa1b3afa4395142
7
+ data.tar.gz: 63bf4dd13deaa7c767bbc1f6c50170548cb6819ebb2ca87282939f6cd88da078820af2453634dcb0e4c3d056073807f34ac86623c5b75ec89ea416ed8fa6f7c1
data/.gitignore CHANGED
@@ -11,6 +11,7 @@ Gemfile*
11
11
  pkg
12
12
  test/debug.log
13
13
  *~
14
+ *.swp
14
15
  /rdoc
15
16
  tmp
16
17
  .classpath
@@ -24,3 +24,5 @@ matrix:
24
24
  allow_failures:
25
25
  - rvm: ruby-head
26
26
  - rvm: jruby-head
27
+ - rvm: jruby-19mode
28
+ os: osx
data/ChangeLog CHANGED
@@ -1,3 +1,11 @@
1
+ 2015-10-24 version 0.7.0:
2
+
3
+ * Add extention types support.
4
+ * Fix to share almost all test cases between CRuby and JRuby implementations.
5
+ * Fixed JRuby implementation to raise UnknownExtTypeError for unregistered ext type ids
6
+ instead to generate MessagePack::ExtensionValue instances.
7
+ (Specify `allow_unknown_ext: true` as unpacker option for v0.6.x behavior.)
8
+
1
9
  2015-07-22 version 0.6.2:
2
10
 
3
11
  * Fix release workflow: Ruby 2.1 and 2.2 are supported for Windows (2.0 is omitted)
@@ -36,7 +36,6 @@ or build msgpack-ruby and install:
36
36
  * Exchange objects between software components written in different languages
37
37
  * You'll need a flexible but efficient format so that components exchange objects while keeping compatibility
38
38
 
39
-
40
39
  = Portability
41
40
 
42
41
  MessagePack for Ruby should run on x86, ARM, PowerPC, SPARC and other CPU architectures.
@@ -92,6 +91,32 @@ or event-driven style which works well with EventMachine:
92
91
 
93
92
  See {API reference}[http://ruby.msgpack.org/MessagePack/Unpacker.html] for details.
94
93
 
94
+ = Extension Types
95
+
96
+ Packer and Unpacker support {Extension types of MessagePack}[https://github.com/msgpack/msgpack/blob/master/spec.md#types-extension-type].
97
+
98
+ # register how to serialize custom class at first
99
+ pk = MessagePack::Packer.new(io)
100
+ pk.register_type(0x01, MyClass1, :to_msgpack_ext) # equal to pk.register_type(0x01, MyClass)
101
+ pk.register_type(0x02, MyClass2){|obj| obj.how_to_serialize() } # blocks also available
102
+
103
+ # almost same API for unpacker
104
+ uk = MessagePack::Unpacker.new()
105
+ uk.register_type(0x01, MyClass1, :from_msgpack_ext)
106
+ uk.register_type(0x02){|data| MyClass2.create_from_serialized_data(data) }
107
+
108
+ MessagePack::Factory is to create packer and unpacker which have same extention types.
109
+
110
+ factory = MessagePack::Factory.new
111
+ factory.register_type(0x01, MyClass1) # same with next line
112
+ factory.register_type(0x01, MyClass1, packer: :to_msgpack_ext, unpacker: :from_msgpack_ext)
113
+ pk = factory.packer(options_for_packer)
114
+ uk = factory.unpacker(options_for_unpacker)
115
+
116
+ For *MessagePack.pack* and *MessagePack.unpack*, default packer/unpacker refer *MessagePack::DefaultFactory*. Call *MessagePack::DefaultFactory.register_type* to enable types process globally.
117
+
118
+ MessagePack::DefaultFactory.register_type(0x03, MyClass3)
119
+ MessagePack.unpack(data_with_ext_typeid_03) #=> MyClass3 instance
95
120
 
96
121
  = Buffer API
97
122
 
@@ -129,9 +154,18 @@ MessagePack mingw32/64 rubygems build process uses {rake-compiler-dock}[https://
129
154
 
130
155
  Once this step successes, target gems exist in pkg/msgpack-*-{x86,x64}-mingw32.gem.
131
156
 
157
+ == Updating documents
158
+
159
+ Online documents (http://ruby.msgpack.org) is generated from gh-pages branch.
160
+ Following commands update documents in gh-pages branch:
161
+
162
+ bundle exec rake doc
163
+ git checkout gh-pages
164
+ cp doc/* ./ -a
165
+
132
166
  = Copyright
133
167
 
134
168
  Author:: Sadayuki Furuhashi <frsyuki@gmail.com>
135
- Copyright:: Copyright (c) 2008-2013 Sadayuki Furuhashi
169
+ Copyright:: Copyright (c) 2008-2015 Sadayuki Furuhashi
136
170
  License:: Apache License, Version 2.0
137
171
 
@@ -12,6 +12,7 @@ import org.jruby.RubyObject;
12
12
  import org.jruby.RubyClass;
13
13
  import org.jruby.RubyBignum;
14
14
  import org.jruby.RubyString;
15
+ import org.jruby.RubyArray;
15
16
  import org.jruby.RubyHash;
16
17
  import org.jruby.exceptions.RaiseException;
17
18
  import org.jruby.runtime.builtin.IRubyObject;
@@ -29,33 +30,58 @@ public class Decoder implements Iterator<IRubyObject> {
29
30
  private final Encoding utf8Encoding;
30
31
  private final RubyClass unpackErrorClass;
31
32
  private final RubyClass underflowErrorClass;
33
+ private final RubyClass malformedFormatErrorClass;
34
+ private final RubyClass stackErrorClass;
32
35
  private final RubyClass unexpectedTypeErrorClass;
36
+ private final RubyClass unknownExtTypeErrorClass;
33
37
 
38
+ private ExtensionRegistry registry;
34
39
  private ByteBuffer buffer;
35
40
  private boolean symbolizeKeys;
41
+ private boolean allowUnknownExt;
36
42
 
37
43
  public Decoder(Ruby runtime) {
38
- this(runtime, new byte[] {}, 0, 0);
44
+ this(runtime, null, new byte[] {}, 0, 0, false, false);
45
+ }
46
+
47
+ public Decoder(Ruby runtime, ExtensionRegistry registry) {
48
+ this(runtime, registry, new byte[] {}, 0, 0, false, false);
39
49
  }
40
50
 
41
51
  public Decoder(Ruby runtime, byte[] bytes) {
42
- this(runtime, bytes, 0, bytes.length);
52
+ this(runtime, null, bytes, 0, bytes.length, false, false);
53
+ }
54
+
55
+ public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes) {
56
+ this(runtime, registry, bytes, 0, bytes.length, false, false);
57
+ }
58
+
59
+ public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes, boolean symbolizeKeys, boolean allowUnknownExt) {
60
+ this(runtime, registry, bytes, 0, bytes.length, symbolizeKeys, allowUnknownExt);
61
+ }
62
+
63
+ public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes, int offset, int length) {
64
+ this(runtime, registry, bytes, offset, length, false, false);
43
65
  }
44
66
 
45
- public Decoder(Ruby runtime, byte[] bytes, int offset, int length) {
67
+ public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes, int offset, int length, boolean symbolizeKeys, boolean allowUnknownExt) {
46
68
  this.runtime = runtime;
69
+ this.registry = registry;
70
+ this.symbolizeKeys = symbolizeKeys;
71
+ this.allowUnknownExt = allowUnknownExt;
47
72
  this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding();
48
73
  this.utf8Encoding = UTF8Encoding.INSTANCE;
49
74
  this.unpackErrorClass = runtime.getModule("MessagePack").getClass("UnpackError");
50
75
  this.underflowErrorClass = runtime.getModule("MessagePack").getClass("UnderflowError");
76
+ this.malformedFormatErrorClass = runtime.getModule("MessagePack").getClass("MalformedFormatError");
77
+ this.stackErrorClass = runtime.getModule("MessagePack").getClass("StackError");
51
78
  this.unexpectedTypeErrorClass = runtime.getModule("MessagePack").getClass("UnexpectedTypeError");
79
+ this.unknownExtTypeErrorClass = runtime.getModule("MessagePack").getClass("UnknownExtTypeError");
80
+ this.symbolizeKeys = symbolizeKeys;
81
+ this.allowUnknownExt = allowUnknownExt;
52
82
  feed(bytes, offset, length);
53
83
  }
54
84
 
55
- public void symbolizeKeys(boolean symbolize) {
56
- this.symbolizeKeys = symbolize;
57
- }
58
-
59
85
  public void feed(byte[] bytes) {
60
86
  feed(bytes, 0, bytes.length);
61
87
  }
@@ -73,7 +99,7 @@ public class Decoder implements Iterator<IRubyObject> {
73
99
  }
74
100
 
75
101
  public void reset() {
76
- buffer.rewind();
102
+ buffer = null;
77
103
  }
78
104
 
79
105
  public int offset() {
@@ -118,7 +144,20 @@ public class Decoder implements Iterator<IRubyObject> {
118
144
  private IRubyObject consumeExtension(int size) {
119
145
  int type = buffer.get();
120
146
  byte[] payload = readBytes(size);
121
- return ExtensionValue.newExtensionValue(runtime, type, payload);
147
+
148
+ if (registry != null) {
149
+ IRubyObject proc = registry.lookupUnpackerByTypeId(type);
150
+ if (proc != null) {
151
+ ByteList byteList = new ByteList(payload, runtime.getEncodingService().getAscii8bitEncoding());
152
+ return proc.callMethod(runtime.getCurrentContext(), "call", runtime.newString(byteList));
153
+ }
154
+ }
155
+
156
+ if (this.allowUnknownExt) {
157
+ return ExtensionValue.newExtensionValue(runtime, type, payload);
158
+ }
159
+
160
+ throw runtime.newRaiseException(unknownExtTypeErrorClass, "unexpected extension type");
122
161
  }
123
162
 
124
163
  private byte[] readBytes(int size) {
@@ -142,11 +181,11 @@ public class Decoder implements Iterator<IRubyObject> {
142
181
  try {
143
182
  byte b = buffer.get();
144
183
  if ((b & 0xf0) == 0x90) {
145
- return runtime.newFixnum(b & 0x0f);
184
+ return runtime.newFixnum(b & 0x0f);
146
185
  } else if (b == ARY16) {
147
- return runtime.newFixnum(buffer.getShort() & 0xffff);
186
+ return runtime.newFixnum(buffer.getShort() & 0xffff);
148
187
  } else if (b == ARY32) {
149
- return runtime.newFixnum(buffer.getInt());
188
+ return runtime.newFixnum(buffer.getInt());
150
189
  }
151
190
  throw runtime.newRaiseException(unexpectedTypeErrorClass, "unexpected type");
152
191
  } catch (RaiseException re) {
@@ -163,11 +202,11 @@ public class Decoder implements Iterator<IRubyObject> {
163
202
  try {
164
203
  byte b = buffer.get();
165
204
  if ((b & 0xf0) == 0x80) {
166
- return runtime.newFixnum(b & 0x0f);
205
+ return runtime.newFixnum(b & 0x0f);
167
206
  } else if (b == MAP16) {
168
- return runtime.newFixnum(buffer.getShort() & 0xffff);
207
+ return runtime.newFixnum(buffer.getShort() & 0xffff);
169
208
  } else if (b == MAP32) {
170
- return runtime.newFixnum(buffer.getInt());
209
+ return runtime.newFixnum(buffer.getInt());
171
210
  }
172
211
  throw runtime.newRaiseException(unexpectedTypeErrorClass, "unexpected type");
173
212
  } catch (RaiseException re) {
@@ -233,7 +272,7 @@ public class Decoder implements Iterator<IRubyObject> {
233
272
  default: return runtime.newFixnum(b);
234
273
  }
235
274
  buffer.position(position);
236
- throw runtime.newRaiseException(unpackErrorClass, "Illegal byte sequence");
275
+ throw runtime.newRaiseException(malformedFormatErrorClass, "Illegal byte sequence");
237
276
  } catch (RaiseException re) {
238
277
  buffer.position(position);
239
278
  throw re;
@@ -34,15 +34,21 @@ public class Encoder {
34
34
  private final Encoding binaryEncoding;
35
35
  private final Encoding utf8Encoding;
36
36
  private final boolean compatibilityMode;
37
+ private final ExtensionRegistry registry;
37
38
 
38
39
  private ByteBuffer buffer;
39
40
 
40
- public Encoder(Ruby runtime, boolean compatibilityMode) {
41
+ public Encoder(Ruby runtime, boolean compatibilityMode, ExtensionRegistry registry) {
41
42
  this.runtime = runtime;
42
43
  this.buffer = ByteBuffer.allocate(CACHE_LINE_SIZE - ARRAY_HEADER_SIZE);
43
44
  this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding();
44
45
  this.utf8Encoding = UTF8Encoding.INSTANCE;
45
46
  this.compatibilityMode = compatibilityMode;
47
+ this.registry = registry;
48
+ }
49
+
50
+ public boolean isCompatibilityMode() {
51
+ return compatibilityMode;
46
52
  }
47
53
 
48
54
  private void ensureRemainingCapacity(int c) {
@@ -107,7 +113,7 @@ public class Encoder {
107
113
  } else if (object instanceof ExtensionValue) {
108
114
  appendExtensionValue((ExtensionValue) object);
109
115
  } else {
110
- appendCustom(object, destination);
116
+ appendOther(object, destination);
111
117
  }
112
118
  }
113
119
 
@@ -295,12 +301,7 @@ public class Encoder {
295
301
  }
296
302
  }
297
303
 
298
- private void appendExtensionValue(ExtensionValue object) {
299
- long type = ((RubyFixnum)object.get_type()).getLongValue();
300
- if (type < -128 || type > 127) {
301
- throw object.getRuntime().newRangeError(String.format("integer %d too big to convert to `signed char'", type));
302
- }
303
- ByteList payloadBytes = ((RubyString)object.payload()).getByteList();
304
+ private void appendExt(int type, ByteList payloadBytes) {
304
305
  int payloadSize = payloadBytes.length();
305
306
  int outputSize = 0;
306
307
  boolean fixSize = payloadSize == 1 || payloadSize == 2 || payloadSize == 4 || payloadSize == 8 || payloadSize == 16;
@@ -338,6 +339,28 @@ public class Encoder {
338
339
  buffer.put(payloadBytes.unsafeBytes(), payloadBytes.begin(), payloadSize);
339
340
  }
340
341
 
342
+ private void appendExtensionValue(ExtensionValue object) {
343
+ long type = ((RubyFixnum)object.get_type()).getLongValue();
344
+ if (type < -128 || type > 127) {
345
+ throw object.getRuntime().newRangeError(String.format("integer %d too big to convert to `signed char'", type));
346
+ }
347
+ ByteList payloadBytes = ((RubyString)object.payload()).getByteList();
348
+ appendExt((int) type, payloadBytes);
349
+ }
350
+
351
+ private void appendOther(IRubyObject object, IRubyObject destination) {
352
+ if (registry != null) {
353
+ IRubyObject[] pair = registry.lookupPackerByClass(object.getType());
354
+ if (pair != null) {
355
+ RubyString bytes = pair[0].callMethod(runtime.getCurrentContext(), "call", object).asString();
356
+ int type = (int) ((RubyFixnum) pair[1]).getLongValue();
357
+ appendExt(type, bytes.getByteList());
358
+ return;
359
+ }
360
+ }
361
+ appendCustom(object, destination);
362
+ }
363
+
341
364
  private void appendCustom(IRubyObject object, IRubyObject destination) {
342
365
  if (destination == null) {
343
366
  IRubyObject result = object.callMethod(runtime.getCurrentContext(), "to_msgpack");
@@ -0,0 +1,159 @@
1
+ package org.msgpack.jruby;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyHash;
5
+ import org.jruby.RubyArray;
6
+ import org.jruby.RubyClass;
7
+ import org.jruby.RubyFixnum;
8
+ import org.jruby.runtime.ThreadContext;
9
+ import org.jruby.runtime.builtin.IRubyObject;
10
+
11
+ import java.util.Map;
12
+ import java.util.HashMap;
13
+
14
+ public class ExtensionRegistry {
15
+ private final Map<RubyClass, ExtensionEntry> extensionsByClass;
16
+ private final Map<RubyClass, ExtensionEntry> extensionsByAncestor;
17
+ private final ExtensionEntry[] extensionsByTypeId;
18
+
19
+ public ExtensionRegistry() {
20
+ this(new HashMap<RubyClass, ExtensionEntry>());
21
+ }
22
+
23
+ private ExtensionRegistry(Map<RubyClass, ExtensionEntry> extensionsByClass) {
24
+ this.extensionsByClass = new HashMap<RubyClass, ExtensionEntry>(extensionsByClass);
25
+ this.extensionsByAncestor = new HashMap<RubyClass, ExtensionEntry>();
26
+ this.extensionsByTypeId = new ExtensionEntry[256];
27
+ for (ExtensionEntry entry : extensionsByClass.values()) {
28
+ if (entry.hasUnpacker()) {
29
+ extensionsByTypeId[entry.getTypeId() + 128] = entry;
30
+ }
31
+ }
32
+ }
33
+
34
+ public ExtensionRegistry dup() {
35
+ return new ExtensionRegistry(extensionsByClass);
36
+ }
37
+
38
+ public IRubyObject toInternalPackerRegistry(ThreadContext ctx) {
39
+ RubyHash hash = RubyHash.newHash(ctx.getRuntime());
40
+ for (RubyClass extensionClass : extensionsByClass.keySet()) {
41
+ ExtensionEntry entry = extensionsByClass.get(extensionClass);
42
+ if (entry.hasPacker()) {
43
+ hash.put(extensionClass, entry.toPackerTuple(ctx));
44
+ }
45
+ }
46
+ return hash;
47
+ }
48
+
49
+ public IRubyObject toInternalUnpackerRegistry(ThreadContext ctx) {
50
+ RubyHash hash = RubyHash.newHash(ctx.getRuntime());
51
+ for (int typeIdIndex = 0 ; typeIdIndex < 256 ; typeIdIndex++) {
52
+ ExtensionEntry entry = extensionsByTypeId[typeIdIndex];
53
+ if (entry != null && entry.hasUnpacker()) {
54
+ IRubyObject typeId = RubyFixnum.newFixnum(ctx.getRuntime(), typeIdIndex - 128);
55
+ hash.put(typeId, entry.toUnpackerTuple(ctx));
56
+ }
57
+ }
58
+ return hash;
59
+ }
60
+
61
+ public void put(RubyClass cls, int typeId, IRubyObject packerProc, IRubyObject packerArg, IRubyObject unpackerProc, IRubyObject unpackerArg) {
62
+ ExtensionEntry entry = new ExtensionEntry(cls, typeId, packerProc, packerArg, unpackerProc, unpackerArg);
63
+ extensionsByClass.put(cls, entry);
64
+ extensionsByTypeId[typeId + 128] = entry;
65
+ extensionsByAncestor.clear();
66
+ }
67
+
68
+ public IRubyObject lookupUnpackerByTypeId(int typeId) {
69
+ ExtensionEntry e = extensionsByTypeId[typeId + 128];
70
+ if (e != null && e.hasUnpacker()) {
71
+ return e.getUnpackerProc();
72
+ } else {
73
+ return null;
74
+ }
75
+ }
76
+
77
+ public IRubyObject[] lookupPackerByClass(RubyClass cls) {
78
+ ExtensionEntry e = extensionsByClass.get(cls);
79
+ if (e == null) {
80
+ e = extensionsByAncestor.get(cls);
81
+ }
82
+ if (e == null) {
83
+ e = findEntryByClassOrAncestor(cls);
84
+ if (e != null) {
85
+ extensionsByAncestor.put(e.getExtensionClass(), e);
86
+ }
87
+ }
88
+ if (e != null && e.hasPacker()) {
89
+ return e.toPackerProcTypeIdPair(cls.getRuntime().getCurrentContext());
90
+ } else {
91
+ return null;
92
+ }
93
+ }
94
+
95
+ private ExtensionEntry findEntryByClassOrAncestor(final RubyClass cls) {
96
+ ThreadContext ctx = cls.getRuntime().getCurrentContext();
97
+ for (RubyClass extensionClass : extensionsByClass.keySet()) {
98
+ RubyArray ancestors = (RubyArray) cls.callMethod(ctx, "ancestors");
99
+ if (ancestors.callMethod(ctx, "include?", extensionClass).isTrue()) {
100
+ return extensionsByClass.get(extensionClass);
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+
106
+ private static class ExtensionEntry {
107
+ private final RubyClass cls;
108
+ private final int typeId;
109
+ private final IRubyObject packerProc;
110
+ private final IRubyObject packerArg;
111
+ private final IRubyObject unpackerProc;
112
+ private final IRubyObject unpackerArg;
113
+
114
+ public ExtensionEntry(RubyClass cls, int typeId, IRubyObject packerProc, IRubyObject packerArg, IRubyObject unpackerProc, IRubyObject unpackerArg) {
115
+ this.cls = cls;
116
+ this.typeId = typeId;
117
+ this.packerProc = packerProc;
118
+ this.packerArg = packerArg;
119
+ this.unpackerProc = unpackerProc;
120
+ this.unpackerArg = unpackerArg;
121
+ }
122
+
123
+ public RubyClass getExtensionClass() {
124
+ return cls;
125
+ }
126
+
127
+ public int getTypeId() {
128
+ return typeId;
129
+ }
130
+
131
+ public boolean hasPacker() {
132
+ return packerProc != null;
133
+ }
134
+
135
+ public boolean hasUnpacker() {
136
+ return unpackerProc != null;
137
+ }
138
+
139
+ public IRubyObject getPackerProc() {
140
+ return packerProc;
141
+ }
142
+
143
+ public IRubyObject getUnpackerProc() {
144
+ return unpackerProc;
145
+ }
146
+
147
+ public RubyArray toPackerTuple(ThreadContext ctx) {
148
+ return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {RubyFixnum.newFixnum(ctx.getRuntime(), typeId), packerProc, packerArg});
149
+ }
150
+
151
+ public RubyArray toUnpackerTuple(ThreadContext ctx) {
152
+ return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {cls, unpackerProc, unpackerArg});
153
+ }
154
+
155
+ public IRubyObject[] toPackerProcTypeIdPair(ThreadContext ctx) {
156
+ return new IRubyObject[] {packerProc, RubyFixnum.newFixnum(ctx.getRuntime(), typeId)};
157
+ }
158
+ }
159
+ }