ffi-msgpack 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.specopts +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +8 -0
- data/Gemfile +24 -0
- data/LICENSE.txt +23 -0
- data/README.md +91 -0
- data/Rakefile +39 -0
- data/VERSION +1 -0
- data/ffi-msgpack.gemspec +101 -0
- data/lib/ffi/msgpack.rb +3 -0
- data/lib/ffi/msgpack/exceptions/parse_error.rb +6 -0
- data/lib/ffi/msgpack/extensions.rb +8 -0
- data/lib/ffi/msgpack/extensions/array.rb +7 -0
- data/lib/ffi/msgpack/extensions/false_class.rb +7 -0
- data/lib/ffi/msgpack/extensions/float.rb +7 -0
- data/lib/ffi/msgpack/extensions/hash.rb +7 -0
- data/lib/ffi/msgpack/extensions/integer.rb +7 -0
- data/lib/ffi/msgpack/extensions/nil_class.rb +7 -0
- data/lib/ffi/msgpack/extensions/string.rb +7 -0
- data/lib/ffi/msgpack/extensions/true_class.rb +7 -0
- data/lib/ffi/msgpack/msg_array.rb +35 -0
- data/lib/ffi/msgpack/msg_key_value.rb +33 -0
- data/lib/ffi/msgpack/msg_map.rb +41 -0
- data/lib/ffi/msgpack/msg_object.rb +276 -0
- data/lib/ffi/msgpack/msg_object_union.rb +20 -0
- data/lib/ffi/msgpack/msg_raw.rb +42 -0
- data/lib/ffi/msgpack/msgpack.rb +127 -0
- data/lib/ffi/msgpack/packable.rb +20 -0
- data/lib/ffi/msgpack/packer.rb +153 -0
- data/lib/ffi/msgpack/types.rb +30 -0
- data/lib/ffi/msgpack/unpacker.rb +248 -0
- data/lib/ffi/msgpack/zone.rb +18 -0
- data/lib/ffi/msgpack/zone_chunk_list.rb +13 -0
- data/lib/ffi/msgpack/zone_finalizer.rb +14 -0
- data/lib/ffi/msgpack/zone_finalizer_array.rb +13 -0
- data/spec/msg_object_spec.rb +193 -0
- data/spec/packer_spec.rb +62 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/unpacker_spec.rb +69 -0
- metadata +190 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'ffi/msgpack/packer'
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
module MsgPack
|
5
|
+
module Packable
|
6
|
+
#
|
7
|
+
# Packs a Ruby Object.
|
8
|
+
#
|
9
|
+
# @return [String]
|
10
|
+
# The packed Object.
|
11
|
+
#
|
12
|
+
def to_msgpack
|
13
|
+
packer = Packer.create
|
14
|
+
packer << obj
|
15
|
+
|
16
|
+
return packer.to_s
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'ffi/msgpack/types'
|
2
|
+
require 'ffi/msgpack/msgpack'
|
3
|
+
|
4
|
+
module FFI
|
5
|
+
module MsgPack
|
6
|
+
class Packer < FFI::Struct
|
7
|
+
|
8
|
+
layout :data, :pointer,
|
9
|
+
:callback, :msgpack_packer_write
|
10
|
+
|
11
|
+
# The optional buffer to write packed Msg Objects into.
|
12
|
+
attr_accessor :buffer
|
13
|
+
|
14
|
+
# The total length of the buffer
|
15
|
+
attr_accessor :total
|
16
|
+
|
17
|
+
#
|
18
|
+
# Creates a new packer.
|
19
|
+
#
|
20
|
+
# @param [#<<] buffer
|
21
|
+
# Optional buffer to append packed Msg Objects to.
|
22
|
+
#
|
23
|
+
# @yield [packed(,length)]
|
24
|
+
# If a block is given, it will be used as the callback to write
|
25
|
+
# the packed data.
|
26
|
+
#
|
27
|
+
# @yieldparam [String] packed
|
28
|
+
# The packed bytes representing a Msg Object.
|
29
|
+
#
|
30
|
+
# @yieldparam [Integer] length
|
31
|
+
# The length of the packed data.
|
32
|
+
#
|
33
|
+
# @return [Packer]
|
34
|
+
# The new packer.
|
35
|
+
#
|
36
|
+
def Packer.create(buffer=nil,&block)
|
37
|
+
packer = Packer.new()
|
38
|
+
|
39
|
+
# zero the memory
|
40
|
+
packer[:data] = nil
|
41
|
+
packer[:callback] = nil
|
42
|
+
|
43
|
+
# zero the total
|
44
|
+
packer.total = 0
|
45
|
+
|
46
|
+
if block
|
47
|
+
# disable the buffer
|
48
|
+
packer.buffer = nil
|
49
|
+
|
50
|
+
# custom callback
|
51
|
+
packer.callback(&block)
|
52
|
+
else
|
53
|
+
# set the buffer
|
54
|
+
packer.buffer = (buffer || '')
|
55
|
+
|
56
|
+
# setup the default callback
|
57
|
+
packer.callback do |packed,length|
|
58
|
+
packer.buffer << packed
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
return packer
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# Sets the write callback for the packer.
|
67
|
+
#
|
68
|
+
# @yield [packed(,length)]
|
69
|
+
# If a block is given, it will be used as the callback to write
|
70
|
+
# the packed data.
|
71
|
+
#
|
72
|
+
# @yieldparam [String] packed
|
73
|
+
# The packed bytes representing a Msg Object.
|
74
|
+
#
|
75
|
+
# @yieldparam [Integer] length
|
76
|
+
# The length of the packed data.
|
77
|
+
#
|
78
|
+
# @return [Proc]
|
79
|
+
# The new callback.
|
80
|
+
#
|
81
|
+
def callback(&block)
|
82
|
+
self[:callback] = Proc.new do |data_ptr,packed_ptr,length|
|
83
|
+
@total += length
|
84
|
+
|
85
|
+
packed = packed_ptr.get_bytes(0,length)
|
86
|
+
|
87
|
+
if block.arity == 2
|
88
|
+
block.call(packed,length)
|
89
|
+
else
|
90
|
+
block.call(packed)
|
91
|
+
end
|
92
|
+
|
93
|
+
0 # return 0 to indicate success
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
# Packs a Msg Object.
|
99
|
+
#
|
100
|
+
# @param [MsgObject] msg
|
101
|
+
# The Msg Object to pack.
|
102
|
+
#
|
103
|
+
# @return [Integer]
|
104
|
+
# Returns 0 on a successful write, and -1 if an error occurred.
|
105
|
+
#
|
106
|
+
def pack(msg)
|
107
|
+
MsgPack.msgpack_pack_object(self,msg)
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Packs a Ruby object.
|
112
|
+
#
|
113
|
+
# @param [Hash, Array, String, Symbol, Integer, Float, true, false, nil] value
|
114
|
+
# The Ruby object to pack.
|
115
|
+
#
|
116
|
+
# @return [Packer]
|
117
|
+
# The packer.
|
118
|
+
#
|
119
|
+
def <<(value)
|
120
|
+
pack(MsgObject.new_object(value))
|
121
|
+
return self
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
# The contents of the buffer.
|
126
|
+
#
|
127
|
+
# @return [String, nil]
|
128
|
+
# The contents of the buffer, or `nil` if only a callback is being
|
129
|
+
# used to write the packed data.
|
130
|
+
#
|
131
|
+
def to_s
|
132
|
+
@buffer.to_s if @buffer
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Inspects the packer.
|
137
|
+
#
|
138
|
+
# @return [String]
|
139
|
+
# The inspected packer.
|
140
|
+
#
|
141
|
+
def inspect
|
142
|
+
addr = ('0x%x' % self.pointer.address)
|
143
|
+
|
144
|
+
if @buffer
|
145
|
+
"#<#{self.class}:#{addr}: #{@buffer.inspect}>"
|
146
|
+
else
|
147
|
+
"#<#{self.class}:#{addr}>"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
module MsgPack
|
5
|
+
extend FFI::Library
|
6
|
+
|
7
|
+
typedef :pointer, :FILE
|
8
|
+
|
9
|
+
enum :msgpack_unpack_return, [
|
10
|
+
:success, 2,
|
11
|
+
:extra_bytes, 1,
|
12
|
+
:continue, 0,
|
13
|
+
:parse_error, -1
|
14
|
+
]
|
15
|
+
|
16
|
+
callback :msgpack_packer_write, [:pointer, :pointer, :uint], :int
|
17
|
+
|
18
|
+
enum :msgpack_object_type, [
|
19
|
+
:nil, 0x01,
|
20
|
+
:boolean, 0x02,
|
21
|
+
:positive_integer, 0x03,
|
22
|
+
:negative_integer, 0x04,
|
23
|
+
:double, 0x05,
|
24
|
+
:raw, 0x06,
|
25
|
+
:array, 0x07,
|
26
|
+
:map, 0x08
|
27
|
+
]
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
require 'ffi/msgpack/exceptions/parse_error'
|
2
|
+
require 'ffi/msgpack/types'
|
3
|
+
require 'ffi/msgpack/msgpack'
|
4
|
+
|
5
|
+
module FFI
|
6
|
+
module MsgPack
|
7
|
+
class Unpacker < FFI::Struct
|
8
|
+
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
# Default chunk-size to expand the buffer by
|
12
|
+
CHUNK_SIZE = 1024
|
13
|
+
|
14
|
+
# The default size of the unpacker buffer
|
15
|
+
DEFAULT_SIZE = CHUNK_SIZE * 4
|
16
|
+
|
17
|
+
# The chunk-size to expand the buffer by
|
18
|
+
attr_accessor :chunk_size
|
19
|
+
|
20
|
+
# The optional stream to read packed data from
|
21
|
+
attr_accessor :stream
|
22
|
+
|
23
|
+
layout :buffer, :pointer,
|
24
|
+
:used, :size_t,
|
25
|
+
:free, :size_t,
|
26
|
+
:off, :size_t,
|
27
|
+
:parsed, :size_t,
|
28
|
+
:z, :pointer,
|
29
|
+
:initial_buffer_size, :size_t,
|
30
|
+
:ctx, :pointer
|
31
|
+
|
32
|
+
#
|
33
|
+
# Initializes a new unpacker object.
|
34
|
+
#
|
35
|
+
def initialize(*arguments)
|
36
|
+
super(*arguments)
|
37
|
+
|
38
|
+
@chunk_size = CHUNK_SIZE
|
39
|
+
@stream = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Creates a new unpacker object.
|
44
|
+
#
|
45
|
+
# @param [Integer] size
|
46
|
+
# The buffer size of the unpacker.
|
47
|
+
#
|
48
|
+
# @return [Unpacker]
|
49
|
+
# The new unpacker.
|
50
|
+
#
|
51
|
+
def Unpacker.create(size=DEFAULT_SIZE)
|
52
|
+
Unpacker.new(MsgPack.msgpack_unpacker_new(size))
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Destroys a previously allocated unpacker object.
|
57
|
+
#
|
58
|
+
# @param [FFI::Pointer] ptr
|
59
|
+
# The pointer to the allocated unpacker.
|
60
|
+
#
|
61
|
+
def Unpacker.release(ptr)
|
62
|
+
MsgPack.msgpack_unpacker_free(ptr)
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# Writes packed data into the buffer of the unpacker.
|
67
|
+
#
|
68
|
+
# @param [String] packed
|
69
|
+
# The packed data.
|
70
|
+
#
|
71
|
+
# @return [Unpacker]
|
72
|
+
# The unpacker.
|
73
|
+
#
|
74
|
+
def <<(packed)
|
75
|
+
# make sure we have space in the buffer
|
76
|
+
reserve_buffer(packed.length)
|
77
|
+
|
78
|
+
# copy in the bytes
|
79
|
+
self[:buffer].put_bytes(buffer_offset,packed)
|
80
|
+
|
81
|
+
# advace the buffer position
|
82
|
+
buffer_consumed!(packed.length)
|
83
|
+
return self
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# Reads packed data from a stream into the buffer of the unpacker.
|
88
|
+
#
|
89
|
+
# @param [IO] io
|
90
|
+
# The stream to read the packed data from.
|
91
|
+
#
|
92
|
+
# @return [Boolean]
|
93
|
+
# Specifies whether data was read from the stream, or if the stream
|
94
|
+
# is empty.
|
95
|
+
#
|
96
|
+
def read(io)
|
97
|
+
reserve_buffer(@chunk_size)
|
98
|
+
result = io.read(buffer_capacity)
|
99
|
+
|
100
|
+
unless (result.nil? || result.empty?)
|
101
|
+
self << result
|
102
|
+
return true
|
103
|
+
else
|
104
|
+
return false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Enumerates over each Msg Object from the buffer in the unpacker.
|
110
|
+
#
|
111
|
+
# If {#stream} is set, packed data will be read from it, when the
|
112
|
+
# buffer of the unpacker is fully unpacked.
|
113
|
+
#
|
114
|
+
# @yield [obj]
|
115
|
+
# The given block will be passed each Msg Object.
|
116
|
+
#
|
117
|
+
# @yieldparam [MsgObject] obj
|
118
|
+
# An unpacked Msg Object.
|
119
|
+
#
|
120
|
+
# @return [Unpacker]
|
121
|
+
# The unpacker.
|
122
|
+
#
|
123
|
+
def each_object
|
124
|
+
loop do
|
125
|
+
ret = MsgPack.msgpack_unpacker_execute(self)
|
126
|
+
|
127
|
+
if ret > 0
|
128
|
+
# copy out the next Msg Object and release it's zone
|
129
|
+
obj = MsgPack.msgpack_unpacker_data(self)
|
130
|
+
zone = MsgPack.msgpack_unpacker_release_zone(self)
|
131
|
+
|
132
|
+
# reset the unpacker
|
133
|
+
MsgPack.msgpack_unpacker_reset(self)
|
134
|
+
|
135
|
+
yield obj
|
136
|
+
|
137
|
+
# free the zone now that we are done with it
|
138
|
+
MsgPack.msgpack_zone_free(zone)
|
139
|
+
elsif ret < 0
|
140
|
+
raise(ParseError,"a parse error occurred",caller)
|
141
|
+
else
|
142
|
+
unless (@stream && read(@stream))
|
143
|
+
break
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
return self
|
149
|
+
end
|
150
|
+
|
151
|
+
#
|
152
|
+
# Enumerates over each Msg Object from the buffer in the unpacker.
|
153
|
+
#
|
154
|
+
# @yield [obj]
|
155
|
+
# The given block will be passed each unpacked Ruby Object, from
|
156
|
+
# the buffer of the unpacker.
|
157
|
+
#
|
158
|
+
# @yieldparam [nil, true, false, Integer, Float, String, Array, Hash] obj
|
159
|
+
# A Ruby Object unpacked from the buffer of the unpacker.
|
160
|
+
#
|
161
|
+
# @return [Enumerator, Unpacker]
|
162
|
+
# If no block is given, an enumerator will be returned.
|
163
|
+
#
|
164
|
+
def each
|
165
|
+
return enum_for(:each) unless block_given?
|
166
|
+
|
167
|
+
each_object do |obj|
|
168
|
+
yield obj.to_ruby
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
protected
|
173
|
+
|
174
|
+
#
|
175
|
+
# Reserves space in the buffer.
|
176
|
+
#
|
177
|
+
# @param [Integer] size
|
178
|
+
# The number of bytes to reserve.
|
179
|
+
#
|
180
|
+
# @return [Boolean]
|
181
|
+
# Specifies whether the size has been successfully reserved.
|
182
|
+
#
|
183
|
+
def reserve_buffer(size)
|
184
|
+
if self[:free] >= size
|
185
|
+
true
|
186
|
+
else
|
187
|
+
MsgPack.msgpack_unpacker_expand_buffer(self,size)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
#
|
192
|
+
# The offset to empty space in the buffer.
|
193
|
+
#
|
194
|
+
# @return [Integer]
|
195
|
+
# The number of bytes within the buffer.
|
196
|
+
#
|
197
|
+
def buffer_offset
|
198
|
+
self[:used]
|
199
|
+
end
|
200
|
+
|
201
|
+
#
|
202
|
+
# The remaining space of the buffer.
|
203
|
+
#
|
204
|
+
# @return [Integer]
|
205
|
+
# The number of bytes free in the buffer.
|
206
|
+
#
|
207
|
+
def buffer_capacity
|
208
|
+
self[:free]
|
209
|
+
end
|
210
|
+
|
211
|
+
#
|
212
|
+
# Consums space in the buffer.
|
213
|
+
#
|
214
|
+
# @param [Integer] size
|
215
|
+
# The number of bytes to be consumed.
|
216
|
+
#
|
217
|
+
# @return [nil]
|
218
|
+
#
|
219
|
+
def buffer_consumed!(size)
|
220
|
+
self[:used] += size
|
221
|
+
self[:free] -= size
|
222
|
+
|
223
|
+
return nil
|
224
|
+
end
|
225
|
+
|
226
|
+
#
|
227
|
+
# The size of the unparsed message in the buffer.
|
228
|
+
#
|
229
|
+
# @return [Integer]
|
230
|
+
# The number of bytes that are unparsed.
|
231
|
+
#
|
232
|
+
def message_size
|
233
|
+
self[:parsed] - self[:off] + self[:used]
|
234
|
+
end
|
235
|
+
|
236
|
+
#
|
237
|
+
# The number of bytes that have been parsed in the buffer.
|
238
|
+
#
|
239
|
+
# @return [Integer]
|
240
|
+
# The number of parsed bytes.
|
241
|
+
#
|
242
|
+
def parsed_size
|
243
|
+
self[:parsed]
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|