ffi-msgpack 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|