msgpack 0.6.0pre1-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.travis.yml +26 -0
- data/ChangeLog +117 -0
- data/Dockerfile +30 -0
- data/Gemfile +4 -0
- data/LICENSE +177 -0
- data/README.rdoc +129 -0
- data/Rakefile +114 -0
- data/bench/pack.rb +23 -0
- data/bench/pack_log.rb +33 -0
- data/bench/pack_log_long.rb +65 -0
- data/bench/run.sh +14 -0
- data/bench/run_long.sh +35 -0
- data/bench/unpack.rb +21 -0
- data/bench/unpack_log.rb +34 -0
- data/bench/unpack_log_long.rb +67 -0
- data/cross-build.sh +9 -0
- data/doclib/msgpack/buffer.rb +193 -0
- data/doclib/msgpack/core_ext.rb +101 -0
- data/doclib/msgpack/error.rb +14 -0
- data/doclib/msgpack/packer.rb +134 -0
- data/doclib/msgpack/unpacker.rb +146 -0
- data/doclib/msgpack.rb +77 -0
- data/ext/java/org/msgpack/jruby/Buffer.java +221 -0
- data/ext/java/org/msgpack/jruby/Decoder.java +201 -0
- data/ext/java/org/msgpack/jruby/Encoder.java +308 -0
- data/ext/java/org/msgpack/jruby/ExtensionValue.java +136 -0
- data/ext/java/org/msgpack/jruby/MessagePackLibrary.java +107 -0
- data/ext/java/org/msgpack/jruby/Packer.java +78 -0
- data/ext/java/org/msgpack/jruby/Types.java +37 -0
- data/ext/java/org/msgpack/jruby/Unpacker.java +170 -0
- data/ext/msgpack/buffer.c +695 -0
- data/ext/msgpack/buffer.h +447 -0
- data/ext/msgpack/buffer_class.c +507 -0
- data/ext/msgpack/buffer_class.h +32 -0
- data/ext/msgpack/compat.h +113 -0
- data/ext/msgpack/core_ext.c +129 -0
- data/ext/msgpack/core_ext.h +26 -0
- data/ext/msgpack/extconf.rb +28 -0
- data/ext/msgpack/packer.c +168 -0
- data/ext/msgpack/packer.h +441 -0
- data/ext/msgpack/packer_class.c +302 -0
- data/ext/msgpack/packer_class.h +30 -0
- data/ext/msgpack/rbinit.c +33 -0
- data/ext/msgpack/rmem.c +94 -0
- data/ext/msgpack/rmem.h +109 -0
- data/ext/msgpack/sysdep.h +115 -0
- data/ext/msgpack/sysdep_endian.h +50 -0
- data/ext/msgpack/sysdep_types.h +46 -0
- data/ext/msgpack/unpacker.c +771 -0
- data/ext/msgpack/unpacker.h +122 -0
- data/ext/msgpack/unpacker_class.c +405 -0
- data/ext/msgpack/unpacker_class.h +32 -0
- data/lib/msgpack/msgpack.so +0 -0
- data/lib/msgpack/version.rb +3 -0
- data/lib/msgpack.rb +13 -0
- data/msgpack.gemspec +31 -0
- data/msgpack.org.md +46 -0
- data/spec/cases.json +1 -0
- data/spec/cases.msg +0 -0
- data/spec/cases_compact.msg +0 -0
- data/spec/cases_spec.rb +39 -0
- data/spec/cruby/buffer_io_spec.rb +256 -0
- data/spec/cruby/buffer_packer.rb +29 -0
- data/spec/cruby/buffer_spec.rb +572 -0
- data/spec/cruby/buffer_unpacker.rb +19 -0
- data/spec/cruby/packer_spec.rb +120 -0
- data/spec/cruby/unpacker_spec.rb +305 -0
- data/spec/format_spec.rb +282 -0
- data/spec/jruby/benchmarks/shootout_bm.rb +73 -0
- data/spec/jruby/benchmarks/symbolize_keys_bm.rb +25 -0
- data/spec/jruby/msgpack/unpacker_spec.rb +290 -0
- data/spec/jruby/msgpack_spec.rb +142 -0
- data/spec/pack_spec.rb +67 -0
- data/spec/random_compat.rb +24 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/unpack_spec.rb +60 -0
- metadata +209 -0
@@ -0,0 +1,146 @@
|
|
1
|
+
module MessagePack
|
2
|
+
|
3
|
+
#
|
4
|
+
# MessagePack::Unpacker is a class to deserialize objects.
|
5
|
+
#
|
6
|
+
class Unpacker
|
7
|
+
#
|
8
|
+
# Creates a MessagePack::Unpacker instance.
|
9
|
+
#
|
10
|
+
# @overload initialize(options={})
|
11
|
+
# @param options [Hash]
|
12
|
+
#
|
13
|
+
# @overload initialize(io, options={})
|
14
|
+
# @param io [IO]
|
15
|
+
# @param options [Hash]
|
16
|
+
# This unpacker reads data from the _io_ to fill the internal buffer.
|
17
|
+
# _io_ must respond to readpartial(length [,string]) or read(length [,string]) method.
|
18
|
+
#
|
19
|
+
# Supported options:
|
20
|
+
#
|
21
|
+
# * *:symbolize_keys* deserialize keys of Hash objects as Symbol instead of String
|
22
|
+
#
|
23
|
+
# See also Buffer#initialize for other options.
|
24
|
+
#
|
25
|
+
def initialize(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Internal buffer
|
30
|
+
#
|
31
|
+
# @return [MessagePack::Buffer]
|
32
|
+
#
|
33
|
+
attr_reader :buffer
|
34
|
+
|
35
|
+
#
|
36
|
+
# Deserializes an object from the io or internal buffer and returns it.
|
37
|
+
#
|
38
|
+
# This method reads data from io into the internal buffer and deserializes an object
|
39
|
+
# from the buffer. It repeats reading data from the io until enough data is available
|
40
|
+
# to deserialize at least one object. After deserializing one object, unused data is
|
41
|
+
# left in the internal buffer.
|
42
|
+
#
|
43
|
+
# If there're not enough data to deserialize one object, this method raises EOFError.
|
44
|
+
# If data format is invalid, this method raises MessagePack::MalformedFormatError.
|
45
|
+
# If the object nests too deeply, this method raises MessagePack::StackError.
|
46
|
+
#
|
47
|
+
# @return [Object] deserialized object
|
48
|
+
#
|
49
|
+
def read
|
50
|
+
end
|
51
|
+
|
52
|
+
alias unpack read
|
53
|
+
|
54
|
+
#
|
55
|
+
# Deserializes an object and ignores it. This method is faster than _read_.
|
56
|
+
#
|
57
|
+
# This method could raise the same errors with _read_.
|
58
|
+
#
|
59
|
+
# @return nil
|
60
|
+
#
|
61
|
+
def skip
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Deserializes a nil value if it exists and returns _true_.
|
66
|
+
# Otherwise, if a byte exists but the byte doesn't represent nil value,
|
67
|
+
# returns _false_.
|
68
|
+
#
|
69
|
+
# If there're not enough data, this method raises EOFError.
|
70
|
+
#
|
71
|
+
# @return [Boolean]
|
72
|
+
#
|
73
|
+
def skip_nil
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Read a header of an array and returns its size.
|
78
|
+
# It converts a serialized array into a stream of elements.
|
79
|
+
#
|
80
|
+
# If the serialized object is not an array, it raises MessagePack::TypeError.
|
81
|
+
# If there're not enough data, this method raises EOFError.
|
82
|
+
#
|
83
|
+
# @return [Integer] size of the array
|
84
|
+
#
|
85
|
+
def read_array_header
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Reads a header of an map and returns its size.
|
90
|
+
# It converts a serialized map into a stream of key-value pairs.
|
91
|
+
#
|
92
|
+
# If the serialized object is not a map, it raises MessagePack::TypeError.
|
93
|
+
# If there're not enough data, this method raises EOFError.
|
94
|
+
#
|
95
|
+
# @return [Integer] size of the map
|
96
|
+
#
|
97
|
+
def read_map_header
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# Appends data into the internal buffer.
|
102
|
+
# This method is equivalent to unpacker.buffer.append(data).
|
103
|
+
#
|
104
|
+
# @param data [String]
|
105
|
+
# @return [Unpacker] self
|
106
|
+
#
|
107
|
+
def feed(data)
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Repeats to deserialize objects.
|
112
|
+
#
|
113
|
+
# It repeats until the io or internal buffer does not include any complete objects.
|
114
|
+
#
|
115
|
+
# If the an IO is set, it repeats to read data from the IO when the buffer
|
116
|
+
# becomes empty until the IO raises EOFError.
|
117
|
+
#
|
118
|
+
# This method could raise same errors with _read_ excepting EOFError.
|
119
|
+
#
|
120
|
+
# @yieldparam object [Object] deserialized object
|
121
|
+
# @return nil
|
122
|
+
#
|
123
|
+
def each(&block)
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Appends data into the internal buffer and repeats to deserialize objects.
|
128
|
+
# This method is equivalent to unpacker.feed(data) && unpacker.each { ... }.
|
129
|
+
#
|
130
|
+
# @param data [String]
|
131
|
+
# @yieldparam object [Object] deserialized object
|
132
|
+
# @return nil
|
133
|
+
#
|
134
|
+
def feed_each(data, &block)
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# Clears the internal buffer and resets deserialization state of the unpacker.
|
139
|
+
#
|
140
|
+
# @return nil
|
141
|
+
#
|
142
|
+
def reset
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
data/doclib/msgpack.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
module MessagePack
|
3
|
+
#
|
4
|
+
# Serializes an object into an IO or String.
|
5
|
+
#
|
6
|
+
# @overload dump(obj, options={})
|
7
|
+
# @param obj [Object] object to be serialized
|
8
|
+
# @param options [Hash]
|
9
|
+
# @return [String] serialized data
|
10
|
+
#
|
11
|
+
# @overload dump(obj, io, options={})
|
12
|
+
# @param obj [Object] object to be serialized
|
13
|
+
# @param io [IO]
|
14
|
+
# @param options [Hash]
|
15
|
+
# @return [IO]
|
16
|
+
#
|
17
|
+
# See Packer#initialize for supported options.
|
18
|
+
#
|
19
|
+
def self.dump(obj)
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Serializes an object into an IO or String. Alias of dump.
|
24
|
+
#
|
25
|
+
# @overload pack(obj, options={})
|
26
|
+
# @param obj [Object] object to be serialized
|
27
|
+
# @param options [Hash]
|
28
|
+
# @return [String] serialized data
|
29
|
+
#
|
30
|
+
# @overload pack(obj, io, options={})
|
31
|
+
# @param obj [Object] object to be serialized
|
32
|
+
# @param io [IO]
|
33
|
+
# @param options [Hash]
|
34
|
+
# @return [IO]
|
35
|
+
#
|
36
|
+
# See Packer#initialize for supported options.
|
37
|
+
#
|
38
|
+
def self.pack(obj)
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Deserializes an object from an IO or String.
|
43
|
+
#
|
44
|
+
# @overload load(string, options={})
|
45
|
+
# @param string [String] data to deserialize
|
46
|
+
# @param options [Hash]
|
47
|
+
#
|
48
|
+
# @overload load(io, options={})
|
49
|
+
# @param io [IO]
|
50
|
+
# @param options [Hash]
|
51
|
+
#
|
52
|
+
# @return [Object] deserialized object
|
53
|
+
#
|
54
|
+
# See Unpacker#initialize for supported options.
|
55
|
+
#
|
56
|
+
def self.load(src, options={})
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Deserializes an object from an IO or String. Alias of load.
|
61
|
+
#
|
62
|
+
# @overload unpack(string, options={})
|
63
|
+
# @param string [String] data to deserialize
|
64
|
+
# @param options [Hash]
|
65
|
+
#
|
66
|
+
# @overload unpack(io, options={})
|
67
|
+
# @param io [IO]
|
68
|
+
# @param options [Hash]
|
69
|
+
#
|
70
|
+
# @return [Object] deserialized object
|
71
|
+
#
|
72
|
+
# See Unpacker#initialize for supported options.
|
73
|
+
#
|
74
|
+
def self.unpack(src, options={})
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,221 @@
|
|
1
|
+
package org.msgpack.jruby;
|
2
|
+
|
3
|
+
|
4
|
+
import java.nio.ByteBuffer;
|
5
|
+
|
6
|
+
import org.jruby.Ruby;
|
7
|
+
import org.jruby.RubyClass;
|
8
|
+
import org.jruby.RubyObject;
|
9
|
+
import org.jruby.RubyHash;
|
10
|
+
import org.jruby.RubyIO;
|
11
|
+
import org.jruby.RubyInteger;
|
12
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
13
|
+
import org.jruby.anno.JRubyClass;
|
14
|
+
import org.jruby.anno.JRubyMethod;
|
15
|
+
import org.jruby.runtime.ThreadContext;
|
16
|
+
import org.jruby.runtime.ObjectAllocator;
|
17
|
+
import org.jruby.util.ByteList;
|
18
|
+
|
19
|
+
import org.jcodings.Encoding;
|
20
|
+
|
21
|
+
|
22
|
+
@JRubyClass(name="MessagePack::Buffer")
|
23
|
+
public class Buffer extends RubyObject {
|
24
|
+
private RubyHash options;
|
25
|
+
private IRubyObject io;
|
26
|
+
private ByteBuffer buffer;
|
27
|
+
private boolean writeMode;
|
28
|
+
private Encoding binaryEncoding;
|
29
|
+
|
30
|
+
private static final int CACHE_LINE_SIZE = 64;
|
31
|
+
private static final int ARRAY_HEADER_SIZE = 24;
|
32
|
+
|
33
|
+
public Buffer(Ruby runtime, RubyClass type) {
|
34
|
+
super(runtime, type);
|
35
|
+
}
|
36
|
+
|
37
|
+
static class BufferAllocator implements ObjectAllocator {
|
38
|
+
public IRubyObject allocate(Ruby runtime, RubyClass type) {
|
39
|
+
return new Buffer(runtime, type);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
@JRubyMethod(name = "initialize", optional = 2)
|
44
|
+
public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
|
45
|
+
if (args.length > 0) {
|
46
|
+
if (args[0].respondsTo("read") && args[0].respondsTo("write")) {
|
47
|
+
this.io = args[0];
|
48
|
+
}
|
49
|
+
if (args[args.length - 1] instanceof RubyHash) {
|
50
|
+
this.options = (RubyHash) args[args.length - 1];
|
51
|
+
}
|
52
|
+
}
|
53
|
+
this.buffer = ByteBuffer.allocate(CACHE_LINE_SIZE - ARRAY_HEADER_SIZE);
|
54
|
+
this.writeMode = true;
|
55
|
+
this.binaryEncoding = ctx.getRuntime().getEncodingService().getAscii8bitEncoding();
|
56
|
+
return this;
|
57
|
+
}
|
58
|
+
|
59
|
+
private void ensureRemainingCapacity(int c) {
|
60
|
+
if (!writeMode) {
|
61
|
+
buffer.compact();
|
62
|
+
writeMode = true;
|
63
|
+
}
|
64
|
+
if (buffer.remaining() < c) {
|
65
|
+
int newLength = Math.max(buffer.capacity() + (buffer.capacity() >> 1), buffer.capacity() + c);
|
66
|
+
newLength += CACHE_LINE_SIZE - ((ARRAY_HEADER_SIZE + newLength) % CACHE_LINE_SIZE);
|
67
|
+
buffer = ByteBuffer.allocate(newLength).put(buffer.array(), 0, buffer.position());
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
private void ensureReadMode() {
|
72
|
+
if (writeMode) {
|
73
|
+
buffer.flip();
|
74
|
+
writeMode = false;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
private int rawSize() {
|
79
|
+
if (writeMode) {
|
80
|
+
return buffer.position();
|
81
|
+
} else {
|
82
|
+
return buffer.limit() - buffer.position();
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
@JRubyMethod(name = "clear")
|
87
|
+
public IRubyObject clear(ThreadContext ctx) {
|
88
|
+
buffer.clear();
|
89
|
+
return ctx.getRuntime().getNil();
|
90
|
+
}
|
91
|
+
|
92
|
+
@JRubyMethod(name = "size")
|
93
|
+
public IRubyObject size(ThreadContext ctx) {
|
94
|
+
return ctx.getRuntime().newFixnum(rawSize());
|
95
|
+
}
|
96
|
+
|
97
|
+
@JRubyMethod(name = "empty?")
|
98
|
+
public IRubyObject isEmpty(ThreadContext ctx) {
|
99
|
+
return rawSize() == 0 ? ctx.getRuntime().getTrue() : ctx.getRuntime().getFalse();
|
100
|
+
}
|
101
|
+
|
102
|
+
private IRubyObject bufferWrite(ThreadContext ctx, IRubyObject str) {
|
103
|
+
ByteList bytes = str.asString().getByteList();
|
104
|
+
int length = bytes.length();
|
105
|
+
ensureRemainingCapacity(length);
|
106
|
+
buffer.put(bytes.unsafeBytes(), bytes.begin(), length);
|
107
|
+
return ctx.getRuntime().newFixnum(length);
|
108
|
+
|
109
|
+
}
|
110
|
+
|
111
|
+
@JRubyMethod(name = "write", alias = {"<<"})
|
112
|
+
public IRubyObject write(ThreadContext ctx, IRubyObject str) {
|
113
|
+
if (io == null) {
|
114
|
+
return bufferWrite(ctx, str);
|
115
|
+
} else {
|
116
|
+
return io.callMethod(ctx, "write", str);
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
private void feed(ThreadContext ctx) {
|
121
|
+
if (io != null) {
|
122
|
+
bufferWrite(ctx, io.callMethod(ctx, "read"));
|
123
|
+
}
|
124
|
+
}
|
125
|
+
|
126
|
+
private IRubyObject readCommon(ThreadContext ctx, IRubyObject[] args, boolean raiseOnUnderflow) {
|
127
|
+
feed(ctx);
|
128
|
+
int length = rawSize();
|
129
|
+
if (args != null && args.length == 1) {
|
130
|
+
length = (int) args[0].convertToInteger().getLongValue();
|
131
|
+
}
|
132
|
+
if (raiseOnUnderflow && rawSize() < length) {
|
133
|
+
throw ctx.getRuntime().newEOFError();
|
134
|
+
}
|
135
|
+
int readLength = Math.min(length, rawSize());
|
136
|
+
if (readLength == 0 && length > 0) {
|
137
|
+
return ctx.getRuntime().getNil();
|
138
|
+
} else if (readLength == 0) {
|
139
|
+
return ctx.getRuntime().newString();
|
140
|
+
} else {
|
141
|
+
ensureReadMode();
|
142
|
+
byte[] bytes = new byte[readLength];
|
143
|
+
buffer.get(bytes);
|
144
|
+
ByteList byteList = new ByteList(bytes, binaryEncoding);
|
145
|
+
return ctx.getRuntime().newString(byteList);
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
@JRubyMethod(name = "read", optional = 1)
|
150
|
+
public IRubyObject read(ThreadContext ctx, IRubyObject[] args) {
|
151
|
+
return readCommon(ctx, args, false);
|
152
|
+
}
|
153
|
+
|
154
|
+
@JRubyMethod(name = "read_all", optional = 1)
|
155
|
+
public IRubyObject readAll(ThreadContext ctx, IRubyObject[] args) {
|
156
|
+
return readCommon(ctx, args, true);
|
157
|
+
}
|
158
|
+
|
159
|
+
private IRubyObject skipCommon(ThreadContext ctx, IRubyObject _length, boolean raiseOnUnderflow) {
|
160
|
+
feed(ctx);
|
161
|
+
int length = (int) _length.convertToInteger().getLongValue();
|
162
|
+
if (raiseOnUnderflow && rawSize() < length) {
|
163
|
+
throw ctx.getRuntime().newEOFError();
|
164
|
+
}
|
165
|
+
ensureReadMode();
|
166
|
+
int skipLength = Math.min(length, rawSize());
|
167
|
+
buffer.position(buffer.position() + skipLength);
|
168
|
+
return ctx.getRuntime().newFixnum(skipLength);
|
169
|
+
}
|
170
|
+
|
171
|
+
@JRubyMethod(name = "skip")
|
172
|
+
public IRubyObject skip(ThreadContext ctx, IRubyObject length) {
|
173
|
+
return skipCommon(ctx, length, false);
|
174
|
+
}
|
175
|
+
|
176
|
+
@JRubyMethod(name = "skip_all")
|
177
|
+
public IRubyObject skipAll(ThreadContext ctx, IRubyObject length) {
|
178
|
+
return skipCommon(ctx, length, true);
|
179
|
+
}
|
180
|
+
|
181
|
+
@JRubyMethod(name = "to_s", alias = {"to_str"})
|
182
|
+
public IRubyObject toS(ThreadContext ctx) {
|
183
|
+
ensureReadMode();
|
184
|
+
int length = buffer.limit() - buffer.position();
|
185
|
+
ByteList str = new ByteList(buffer.array(), buffer.position(), length, binaryEncoding, true);
|
186
|
+
return ctx.getRuntime().newString(str);
|
187
|
+
}
|
188
|
+
|
189
|
+
@JRubyMethod(name = "to_a")
|
190
|
+
public IRubyObject toA(ThreadContext ctx) {
|
191
|
+
return ctx.getRuntime().newArray(toS(ctx));
|
192
|
+
}
|
193
|
+
|
194
|
+
@JRubyMethod(name = "io")
|
195
|
+
public IRubyObject getIo(ThreadContext ctx) {
|
196
|
+
return io == null ? ctx.getRuntime().getNil() : io;
|
197
|
+
}
|
198
|
+
|
199
|
+
@JRubyMethod(name = "flush")
|
200
|
+
public IRubyObject flush(ThreadContext ctx) {
|
201
|
+
if (io == null) {
|
202
|
+
return ctx.getRuntime().getNil();
|
203
|
+
} else {
|
204
|
+
return io.callMethod(ctx, "flush");
|
205
|
+
}
|
206
|
+
}
|
207
|
+
|
208
|
+
@JRubyMethod(name = "close")
|
209
|
+
public IRubyObject close(ThreadContext ctx) {
|
210
|
+
if (io == null) {
|
211
|
+
return ctx.getRuntime().getNil();
|
212
|
+
} else {
|
213
|
+
return io.callMethod(ctx, "close");
|
214
|
+
}
|
215
|
+
}
|
216
|
+
|
217
|
+
@JRubyMethod(name = "write_to")
|
218
|
+
public IRubyObject writeTo(ThreadContext ctx, IRubyObject io) {
|
219
|
+
return io.callMethod(ctx, "write", readCommon(ctx, null, false));
|
220
|
+
}
|
221
|
+
}
|
@@ -0,0 +1,201 @@
|
|
1
|
+
package org.msgpack.jruby;
|
2
|
+
|
3
|
+
|
4
|
+
import java.math.BigInteger;
|
5
|
+
import java.nio.ByteBuffer;
|
6
|
+
import java.nio.BufferUnderflowException;
|
7
|
+
import java.util.Iterator;
|
8
|
+
import java.util.Arrays;
|
9
|
+
|
10
|
+
import org.jruby.Ruby;
|
11
|
+
import org.jruby.RubyObject;
|
12
|
+
import org.jruby.RubyClass;
|
13
|
+
import org.jruby.RubyBignum;
|
14
|
+
import org.jruby.RubyString;
|
15
|
+
import org.jruby.RubyHash;
|
16
|
+
import org.jruby.exceptions.RaiseException;
|
17
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
18
|
+
import org.jruby.util.ByteList;
|
19
|
+
|
20
|
+
import org.jcodings.Encoding;
|
21
|
+
import org.jcodings.specific.UTF8Encoding;
|
22
|
+
|
23
|
+
import static org.msgpack.jruby.Types.*;
|
24
|
+
|
25
|
+
|
26
|
+
public class Decoder implements Iterator<IRubyObject> {
|
27
|
+
private final Ruby runtime;
|
28
|
+
private final Encoding binaryEncoding;
|
29
|
+
private final Encoding utf8Encoding;
|
30
|
+
private final RubyClass unpackErrorClass;
|
31
|
+
private final RubyClass underflowErrorClass;
|
32
|
+
|
33
|
+
private ByteBuffer buffer;
|
34
|
+
private boolean symbolizeKeys;
|
35
|
+
|
36
|
+
public Decoder(Ruby runtime) {
|
37
|
+
this(runtime, new byte[] {}, 0, 0);
|
38
|
+
}
|
39
|
+
|
40
|
+
public Decoder(Ruby runtime, byte[] bytes) {
|
41
|
+
this(runtime, bytes, 0, bytes.length);
|
42
|
+
}
|
43
|
+
|
44
|
+
public Decoder(Ruby runtime, byte[] bytes, int offset, int length) {
|
45
|
+
this.runtime = runtime;
|
46
|
+
this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding();
|
47
|
+
this.utf8Encoding = UTF8Encoding.INSTANCE;
|
48
|
+
this.unpackErrorClass = runtime.getModule("MessagePack").getClass("UnpackError");
|
49
|
+
this.underflowErrorClass = runtime.getModule("MessagePack").getClass("UnderflowError");
|
50
|
+
feed(bytes, offset, length);
|
51
|
+
}
|
52
|
+
|
53
|
+
public void symbolizeKeys(boolean symbolize) {
|
54
|
+
this.symbolizeKeys = symbolize;
|
55
|
+
}
|
56
|
+
|
57
|
+
public void feed(byte[] bytes) {
|
58
|
+
feed(bytes, 0, bytes.length);
|
59
|
+
}
|
60
|
+
|
61
|
+
public void feed(byte[] bytes, int offset, int length) {
|
62
|
+
if (buffer == null) {
|
63
|
+
buffer = ByteBuffer.wrap(bytes, offset, length);
|
64
|
+
} else {
|
65
|
+
ByteBuffer newBuffer = ByteBuffer.allocate(buffer.remaining() + length);
|
66
|
+
newBuffer.put(buffer);
|
67
|
+
newBuffer.put(bytes, offset, length);
|
68
|
+
newBuffer.flip();
|
69
|
+
buffer = newBuffer;
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
public void reset() {
|
74
|
+
buffer.rewind();
|
75
|
+
}
|
76
|
+
|
77
|
+
public int offset() {
|
78
|
+
return buffer.position();
|
79
|
+
}
|
80
|
+
|
81
|
+
private IRubyObject consumeUnsignedLong() {
|
82
|
+
long value = buffer.getLong();
|
83
|
+
if (value < 0) {
|
84
|
+
return RubyBignum.newBignum(runtime, BigInteger.valueOf(value & ((1L<<63)-1)).setBit(63));
|
85
|
+
} else {
|
86
|
+
return runtime.newFixnum(value);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
private IRubyObject consumeString(int size, Encoding encoding) {
|
91
|
+
byte[] bytes = readBytes(size);
|
92
|
+
ByteList byteList = new ByteList(bytes, encoding);
|
93
|
+
return runtime.newString(byteList);
|
94
|
+
}
|
95
|
+
|
96
|
+
private IRubyObject consumeArray(int size) {
|
97
|
+
IRubyObject[] elements = new IRubyObject[size];
|
98
|
+
for (int i = 0; i < size; i++) {
|
99
|
+
elements[i] = next();
|
100
|
+
}
|
101
|
+
return runtime.newArray(elements);
|
102
|
+
}
|
103
|
+
|
104
|
+
private IRubyObject consumeHash(int size) {
|
105
|
+
RubyHash hash = RubyHash.newHash(runtime);
|
106
|
+
for (int i = 0; i < size; i++) {
|
107
|
+
IRubyObject key = next();
|
108
|
+
if (this.symbolizeKeys && key instanceof RubyString) {
|
109
|
+
key = ((RubyString) key).intern();
|
110
|
+
}
|
111
|
+
hash.fastASet(key, next());
|
112
|
+
}
|
113
|
+
return hash;
|
114
|
+
}
|
115
|
+
|
116
|
+
private IRubyObject consumeExtension(int size) {
|
117
|
+
int type = buffer.get();
|
118
|
+
byte[] payload = readBytes(size);
|
119
|
+
return ExtensionValue.newExtensionValue(runtime, type, payload);
|
120
|
+
}
|
121
|
+
|
122
|
+
private byte[] readBytes(int size) {
|
123
|
+
byte[] payload = new byte[size];
|
124
|
+
buffer.get(payload);
|
125
|
+
return payload;
|
126
|
+
}
|
127
|
+
|
128
|
+
@Override
|
129
|
+
public void remove() {
|
130
|
+
throw new UnsupportedOperationException();
|
131
|
+
}
|
132
|
+
|
133
|
+
@Override
|
134
|
+
public boolean hasNext() {
|
135
|
+
return buffer.remaining() > 0;
|
136
|
+
}
|
137
|
+
|
138
|
+
@Override
|
139
|
+
public IRubyObject next() {
|
140
|
+
int position = buffer.position();
|
141
|
+
try {
|
142
|
+
byte b = buffer.get();
|
143
|
+
outer: switch ((b >> 4) & 0xf) {
|
144
|
+
case 0x8: return consumeHash(b & 0x0f);
|
145
|
+
case 0x9: return consumeArray(b & 0x0f);
|
146
|
+
case 0xa:
|
147
|
+
case 0xb: return consumeString(b & 0x1f, utf8Encoding);
|
148
|
+
case 0xc:
|
149
|
+
switch (b) {
|
150
|
+
case NIL: return runtime.getNil();
|
151
|
+
case FALSE: return runtime.getFalse();
|
152
|
+
case TRUE: return runtime.getTrue();
|
153
|
+
case BIN8: return consumeString(buffer.get() & 0xff, binaryEncoding);
|
154
|
+
case BIN16: return consumeString(buffer.getShort() & 0xffff, binaryEncoding);
|
155
|
+
case BIN32: return consumeString(buffer.getInt(), binaryEncoding);
|
156
|
+
case VAREXT8: return consumeExtension(buffer.get());
|
157
|
+
case VAREXT16: return consumeExtension(buffer.getShort());
|
158
|
+
case VAREXT32: return consumeExtension(buffer.getInt());
|
159
|
+
case FLOAT32: return runtime.newFloat(buffer.getFloat());
|
160
|
+
case FLOAT64: return runtime.newFloat(buffer.getDouble());
|
161
|
+
case UINT8: return runtime.newFixnum(buffer.get() & 0xffL);
|
162
|
+
case UINT16: return runtime.newFixnum(buffer.getShort() & 0xffffL);
|
163
|
+
case UINT32: return runtime.newFixnum(buffer.getInt() & 0xffffffffL);
|
164
|
+
case UINT64: return consumeUnsignedLong();
|
165
|
+
default: break outer;
|
166
|
+
}
|
167
|
+
case 0xd:
|
168
|
+
switch (b) {
|
169
|
+
case INT8: return runtime.newFixnum(buffer.get());
|
170
|
+
case INT16: return runtime.newFixnum(buffer.getShort());
|
171
|
+
case INT32: return runtime.newFixnum(buffer.getInt());
|
172
|
+
case INT64: return runtime.newFixnum(buffer.getLong());
|
173
|
+
case FIXEXT1: return consumeExtension(1);
|
174
|
+
case FIXEXT2: return consumeExtension(2);
|
175
|
+
case FIXEXT4: return consumeExtension(4);
|
176
|
+
case FIXEXT8: return consumeExtension(8);
|
177
|
+
case FIXEXT16: return consumeExtension(16);
|
178
|
+
case STR8: return consumeString(buffer.get() & 0xff, utf8Encoding);
|
179
|
+
case STR16: return consumeString(buffer.getShort() & 0xffff, utf8Encoding);
|
180
|
+
case STR32: return consumeString(buffer.getInt(), utf8Encoding);
|
181
|
+
case ARY16: return consumeArray(buffer.getShort() & 0xffff);
|
182
|
+
case ARY32: return consumeArray(buffer.getInt());
|
183
|
+
case MAP16: return consumeHash(buffer.getShort() & 0xffff);
|
184
|
+
case MAP32: return consumeHash(buffer.getInt());
|
185
|
+
default: break outer;
|
186
|
+
}
|
187
|
+
case 0xe:
|
188
|
+
case 0xf: return runtime.newFixnum((0x1f & b) - 0x20);
|
189
|
+
default: return runtime.newFixnum(b);
|
190
|
+
}
|
191
|
+
buffer.position(position);
|
192
|
+
throw runtime.newRaiseException(unpackErrorClass, "Illegal byte sequence");
|
193
|
+
} catch (RaiseException re) {
|
194
|
+
buffer.position(position);
|
195
|
+
throw re;
|
196
|
+
} catch (BufferUnderflowException bue) {
|
197
|
+
buffer.position(position);
|
198
|
+
throw runtime.newRaiseException(underflowErrorClass, "Not enough bytes available");
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}
|