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.
Files changed (41) hide show
  1. data/.gitignore +4 -0
  2. data/.specopts +1 -0
  3. data/.yardopts +1 -0
  4. data/ChangeLog.md +8 -0
  5. data/Gemfile +24 -0
  6. data/LICENSE.txt +23 -0
  7. data/README.md +91 -0
  8. data/Rakefile +39 -0
  9. data/VERSION +1 -0
  10. data/ffi-msgpack.gemspec +101 -0
  11. data/lib/ffi/msgpack.rb +3 -0
  12. data/lib/ffi/msgpack/exceptions/parse_error.rb +6 -0
  13. data/lib/ffi/msgpack/extensions.rb +8 -0
  14. data/lib/ffi/msgpack/extensions/array.rb +7 -0
  15. data/lib/ffi/msgpack/extensions/false_class.rb +7 -0
  16. data/lib/ffi/msgpack/extensions/float.rb +7 -0
  17. data/lib/ffi/msgpack/extensions/hash.rb +7 -0
  18. data/lib/ffi/msgpack/extensions/integer.rb +7 -0
  19. data/lib/ffi/msgpack/extensions/nil_class.rb +7 -0
  20. data/lib/ffi/msgpack/extensions/string.rb +7 -0
  21. data/lib/ffi/msgpack/extensions/true_class.rb +7 -0
  22. data/lib/ffi/msgpack/msg_array.rb +35 -0
  23. data/lib/ffi/msgpack/msg_key_value.rb +33 -0
  24. data/lib/ffi/msgpack/msg_map.rb +41 -0
  25. data/lib/ffi/msgpack/msg_object.rb +276 -0
  26. data/lib/ffi/msgpack/msg_object_union.rb +20 -0
  27. data/lib/ffi/msgpack/msg_raw.rb +42 -0
  28. data/lib/ffi/msgpack/msgpack.rb +127 -0
  29. data/lib/ffi/msgpack/packable.rb +20 -0
  30. data/lib/ffi/msgpack/packer.rb +153 -0
  31. data/lib/ffi/msgpack/types.rb +30 -0
  32. data/lib/ffi/msgpack/unpacker.rb +248 -0
  33. data/lib/ffi/msgpack/zone.rb +18 -0
  34. data/lib/ffi/msgpack/zone_chunk_list.rb +13 -0
  35. data/lib/ffi/msgpack/zone_finalizer.rb +14 -0
  36. data/lib/ffi/msgpack/zone_finalizer_array.rb +13 -0
  37. data/spec/msg_object_spec.rb +193 -0
  38. data/spec/packer_spec.rb +62 -0
  39. data/spec/spec_helper.rb +15 -0
  40. data/spec/unpacker_spec.rb +69 -0
  41. 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