ffi-msgpack 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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