msgpack 0.4.7 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/.gitignore +17 -0
  2. data/ChangeLog +47 -0
  3. data/README.rdoc +102 -0
  4. data/Rakefile +88 -0
  5. data/doclib/msgpack.rb +55 -0
  6. data/doclib/msgpack/buffer.rb +193 -0
  7. data/doclib/msgpack/core_ext.rb +101 -0
  8. data/doclib/msgpack/error.rb +14 -0
  9. data/doclib/msgpack/packer.rb +131 -0
  10. data/doclib/msgpack/unpacker.rb +130 -0
  11. data/ext/msgpack/buffer.c +679 -0
  12. data/ext/msgpack/buffer.h +442 -0
  13. data/ext/msgpack/buffer_class.c +507 -0
  14. data/ext/msgpack/buffer_class.h +32 -0
  15. data/ext/msgpack/compat.h +112 -0
  16. data/ext/msgpack/core_ext.c +129 -0
  17. data/ext/{pack.h → msgpack/core_ext.h} +7 -7
  18. data/ext/msgpack/extconf.rb +17 -0
  19. data/ext/msgpack/packer.c +137 -0
  20. data/ext/msgpack/packer.h +319 -0
  21. data/ext/msgpack/packer_class.c +285 -0
  22. data/ext/{unpack.h → msgpack/packer_class.h} +11 -7
  23. data/ext/msgpack/rbinit.c +33 -0
  24. data/ext/msgpack/rmem.c +110 -0
  25. data/ext/msgpack/rmem.h +100 -0
  26. data/ext/msgpack/sysdep.h +115 -0
  27. data/ext/msgpack/sysdep_endian.h +50 -0
  28. data/ext/msgpack/sysdep_types.h +46 -0
  29. data/ext/msgpack/unpacker.c +669 -0
  30. data/ext/msgpack/unpacker.h +112 -0
  31. data/ext/msgpack/unpacker_class.c +376 -0
  32. data/{msgpack/pack_define.h → ext/msgpack/unpacker_class.h} +12 -8
  33. data/lib/msgpack.rb +10 -0
  34. data/{ext → lib/msgpack}/version.rb +1 -1
  35. data/msgpack.gemspec +25 -0
  36. data/spec/buffer_io_spec.rb +237 -0
  37. data/spec/buffer_spec.rb +572 -0
  38. data/{test → spec}/cases.json +0 -0
  39. data/{test/cases.mpac → spec/cases.msg} +0 -0
  40. data/{test/cases_compact.mpac → spec/cases_compact.msg} +0 -0
  41. data/spec/cases_spec.rb +39 -0
  42. data/spec/format_spec.rb +225 -0
  43. data/spec/packer_spec.rb +127 -0
  44. data/spec/random_compat.rb +24 -0
  45. data/spec/spec_helper.rb +21 -0
  46. data/spec/unpacker_spec.rb +128 -0
  47. metadata +171 -34
  48. data/ext/compat.h +0 -99
  49. data/ext/extconf.rb +0 -7
  50. data/ext/pack.c +0 -314
  51. data/ext/rbinit.c +0 -66
  52. data/ext/unpack.c +0 -1001
  53. data/msgpack/pack_template.h +0 -771
  54. data/msgpack/sysdep.h +0 -195
  55. data/msgpack/unpack_define.h +0 -93
  56. data/msgpack/unpack_template.h +0 -413
  57. data/test/test_cases.rb +0 -46
  58. data/test/test_encoding.rb +0 -68
  59. data/test/test_helper.rb +0 -10
  60. data/test/test_pack_unpack.rb +0 -308
@@ -0,0 +1,101 @@
1
+
2
+ class NilClass
3
+ #
4
+ # Same as MessagePack.to_msgpack(self[, io]).
5
+ #
6
+ # @return [String] serialized data
7
+ #
8
+ def to_msgpack(io=nil)
9
+ end
10
+ end
11
+
12
+ class TrueClass
13
+ #
14
+ # Same as MessagePack.to_msgpack(self[, io]).
15
+ #
16
+ # @return [String] serialized data
17
+ #
18
+ def to_msgpack(io=nil)
19
+ end
20
+ end
21
+
22
+ class FalseClass
23
+ #
24
+ # Same as MessagePack.to_msgpack(self[, io]).
25
+ #
26
+ # @return [String] serialized data
27
+ #
28
+ def to_msgpack(io=nil)
29
+ end
30
+ end
31
+
32
+ class Fixnum < Integer
33
+ #
34
+ # Same as MessagePack.to_msgpack(self[, io]).
35
+ #
36
+ # @return [String] serialized data
37
+ #
38
+ def to_msgpack(io=nil)
39
+ end
40
+ end
41
+
42
+ class Bignum < Integer
43
+ #
44
+ # Same as MessagePack.to_msgpack(self[, io]).
45
+ #
46
+ # @return [String] serialized data
47
+ #
48
+ def to_msgpack(io=nil)
49
+ end
50
+ end
51
+
52
+ class Float < Numeric
53
+ #
54
+ # Same as MessagePack.to_msgpack(self[, io]).
55
+ #
56
+ # @return [String] serialized data
57
+ #
58
+ def to_msgpack(io=nil)
59
+ end
60
+ end
61
+
62
+ class String
63
+ #
64
+ # Same as MessagePack.to_msgpack(self[, io]).
65
+ #
66
+ # @return [String] serialized data
67
+ #
68
+ def to_msgpack(io=nil)
69
+ end
70
+ end
71
+
72
+ class Array
73
+ #
74
+ # Same as MessagePack.to_msgpack(self[, io]).
75
+ #
76
+ # @return [String] serialized data
77
+ #
78
+ def to_msgpack(io=nil)
79
+ end
80
+ end
81
+
82
+ class Hash
83
+ #
84
+ # Same as MessagePack.to_msgpack(self[, io]).
85
+ #
86
+ # @return [String] serialized data
87
+ #
88
+ def to_msgpack(io=nil)
89
+ end
90
+ end
91
+
92
+ class Symbol
93
+ #
94
+ # Same as MessagePack.to_msgpack(self[, io]).
95
+ #
96
+ # @return [String] serialized data
97
+ #
98
+ def to_msgpack(io=nil)
99
+ end
100
+ end
101
+
@@ -0,0 +1,14 @@
1
+ module MessagePack
2
+
3
+ class UnpackError < StandardError
4
+ end
5
+
6
+ class MalformedFormatError < UnpackError
7
+ end
8
+
9
+ class StackError < UnpackError
10
+ end
11
+
12
+ class TypeError < StandardError
13
+ end
14
+ end
@@ -0,0 +1,131 @@
1
+ module MessagePack
2
+
3
+ #
4
+ # MessagePack::Packer is an interface to serialize objects into an internal buffer,
5
+ # which is a MessagePack::Buffer.
6
+ #
7
+ class Packer
8
+ #
9
+ # Creates a MessagePack::Packer instance.
10
+ # See Buffer#initialize for supported options.
11
+ #
12
+ # @overload initialize(options={})
13
+ # @param options [Hash]
14
+ #
15
+ # @overload initialize(io, options={})
16
+ # @param io [IO]
17
+ # @param options [Hash]
18
+ # This packer writes serialzied objects into the IO when the internal buffer is filled.
19
+ # _io_ must respond to write(string) or append(string) method.
20
+ #
21
+ def initialize(*args)
22
+ end
23
+
24
+ #
25
+ # Internal buffer
26
+ #
27
+ # @return MessagePack::Unpacker
28
+ #
29
+ attr_reader :buffer
30
+
31
+ #
32
+ # Serializes an object into internal buffer.
33
+ #
34
+ # If it could not serialize the object, it raises
35
+ # NoMethodError: undefined method `to_msgpack' for #<the_object>.
36
+ #
37
+ # @param obj [Object] object to serialize
38
+ # @return [Packer] self
39
+ #
40
+ def write(obj)
41
+ end
42
+
43
+ alias pack write
44
+
45
+ #
46
+ # Serializes a nil object. Same as write(nil).
47
+ #
48
+ def write_nil
49
+ end
50
+
51
+ #
52
+ # Write a header of an array whose size is _n_.
53
+ # For example, write_array_header(1).write(true) is same as write([ true ]).
54
+ #
55
+ # @return [Packer] self
56
+ #
57
+ def write_array_header(size)
58
+ end
59
+
60
+ #
61
+ # Write a header of an map whose size is _n_.
62
+ # For example, write_map_header(1).write('key').write(true) is same as write('key'=>true).
63
+ #
64
+ # @return [Packer] self
65
+ #
66
+ def write_map_header(size)
67
+ end
68
+
69
+ #
70
+ # Flushes data in the internal buffer to the internal IO. Same as _buffer.flush.
71
+ # If internal IO is not set, it doesn nothing.
72
+ #
73
+ # @return [Packer] self
74
+ #
75
+ def flush
76
+ end
77
+
78
+ #
79
+ # Makes the internal buffer empty. Same as _buffer.clear_.
80
+ #
81
+ # @return nil
82
+ #
83
+ def clear
84
+ end
85
+
86
+ #
87
+ # Returns size of the internal buffer. Same as buffer.size.
88
+ #
89
+ # @return [Integer]
90
+ #
91
+ def size
92
+ end
93
+
94
+ #
95
+ # Returns _true_ if the internal buffer is empty. Same as buffer.empty?.
96
+ # This method is slightly faster than _size_.
97
+ #
98
+ # @return [Boolean]
99
+ #
100
+ def empty?
101
+ end
102
+
103
+ #
104
+ # Returns all data in the buffer as a string. Same as buffer.to_str.
105
+ #
106
+ # @return [String]
107
+ #
108
+ def to_str
109
+ end
110
+
111
+ #
112
+ # Returns content of the internal buffer as an array of strings. Same as buffer.to_a.
113
+ # This method is faster than _to_str_.
114
+ #
115
+ # @return [Array] array of strings
116
+ #
117
+ def to_a
118
+ end
119
+
120
+ #
121
+ # Writes all of data in the internal buffer into the given IO. Same as buffer.write_to(io).
122
+ # This method consumes and removes data from the internal buffer.
123
+ # _io_ must respond to write(data) method.
124
+ #
125
+ # @param io [IO]
126
+ # @return [Integer] byte size of written data
127
+ #
128
+ def write_to(io)
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,130 @@
1
+ module MessagePack
2
+
3
+ #
4
+ # MessagePack::Unpacker is an interface to deserialize objects from an internal buffer,
5
+ # which is a MessagePack::Buffer.
6
+ #
7
+ class Unpacker
8
+ #
9
+ # Creates a MessagePack::Unpacker instance.
10
+ #
11
+ # @overload initialize(options={})
12
+ # @param options [Hash]
13
+ #
14
+ # @overload initialize(io, options={})
15
+ # @param io [IO]
16
+ # @param options [Hash]
17
+ # This unpacker reads data from the _io_ to fill the internal buffer.
18
+ # _io_ must respond to readpartial(length [,string]) or read(length [,string]) method.
19
+ #
20
+ # See Buffer#initialize for supported options.
21
+ #
22
+ def initialize(*args)
23
+ end
24
+
25
+ #
26
+ # Internal buffer
27
+ #
28
+ # @return [MessagePack::Unpacker]
29
+ #
30
+ attr_reader :buffer
31
+
32
+ #
33
+ # Deserializes an object from internal buffer and returns it.
34
+ #
35
+ # If there're not enough buffer, this method raises EOFError.
36
+ # If data format is invalid, this method raises MessagePack::MalformedFormatError.
37
+ # If the stack is too deep, this method raises MessagePack::StackError.
38
+ #
39
+ # @return [Object] deserialized object
40
+ #
41
+ def read
42
+ end
43
+
44
+ alias unpack read
45
+
46
+ #
47
+ # Deserializes an object and ignores it. This method is faster than _read_.
48
+ #
49
+ # This method could raise same errors with _read_.
50
+ #
51
+ # @return nil
52
+ #
53
+ def skip
54
+ end
55
+
56
+ #
57
+ # Deserializes a nil value if it exists and returns _true_.
58
+ # Otherwise, if a byte exists but the byte doesn't represent nil value,
59
+ # returns _false_.
60
+ #
61
+ # If there're not enough buffer, this method raises EOFError.
62
+ #
63
+ # @return [Boolean]
64
+ #
65
+ def skip_nil
66
+ end
67
+
68
+ #
69
+ # Read a header of an array and returns its size.
70
+ # It converts a serialized array into a stream of elements.
71
+ #
72
+ # If the serialized object is not an array, it raises MessagePack::TypeError.
73
+ # If there're not enough buffer, this method raises EOFError.
74
+ #
75
+ # @return [Integer] size of the array
76
+ #
77
+ def read_array_header
78
+ end
79
+
80
+ #
81
+ # Read a header of an map and returns its size.
82
+ # It converts a serialized map into a stream of key-value pairs.
83
+ #
84
+ # If the serialized object is not a map, it raises MessagePack::TypeError.
85
+ # If there're not enough buffer, this method raises EOFError.
86
+ #
87
+ # @return [Integer] size of the map
88
+ #
89
+ def read_map_header
90
+ end
91
+
92
+ #
93
+ # Appends data into the internal buffer.
94
+ # This method calls buffer.append(data).
95
+ #
96
+ # @param data [String]
97
+ # @return [Unpacker] self
98
+ #
99
+ def feed(data)
100
+ end
101
+
102
+ #
103
+ # Repeats to deserialize objects.
104
+ #
105
+ # It repeats until the internal buffer does not include any complete objects.
106
+ #
107
+ # If the an IO is set, it repeats to read data from the IO when the buffer
108
+ # becomes empty until the IO raises EOFError.
109
+ #
110
+ # This method could raise same errors with _read_ excepting EOFError.
111
+ #
112
+ # @yieldparam object [Object] deserialized object
113
+ # @return nil
114
+ #
115
+ def each(&block)
116
+ end
117
+
118
+ #
119
+ # Appends data into the internal buffer and repeats to deserialize objects.
120
+ # This method is equals to feed(data) && each.
121
+ #
122
+ # @param data [String]
123
+ # @yieldparam object [Object] deserialized object
124
+ # @return nil
125
+ #
126
+ def feed_each(data, &block)
127
+ end
128
+ end
129
+
130
+ end
@@ -0,0 +1,679 @@
1
+ /*
2
+ * MessagePack for Ruby
3
+ *
4
+ * Copyright (C) 2008-2012 FURUHASHI Sadayuki
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ #include "buffer.h"
20
+ #include "rmem.h"
21
+
22
+ #ifdef COMPAT_HAVE_ENCODING /* see compat.h*/
23
+ int s_enc_ascii8bit;
24
+ #endif
25
+
26
+ #ifdef RUBY_VM
27
+ #define HAVE_RB_STR_REPLACE
28
+ #endif
29
+
30
+ #ifndef HAVE_RB_STR_REPLACE
31
+ static ID s_replace;
32
+ #endif
33
+
34
+ #ifndef DISABLE_RMEM
35
+ static msgpack_rmem_t s_rmem;
36
+ #endif
37
+
38
+ void msgpack_buffer_static_init()
39
+ {
40
+ #ifndef DISABLE_RMEM
41
+ msgpack_rmem_init(&s_rmem);
42
+ #endif
43
+ #ifndef HAVE_RB_STR_REPLACE
44
+ s_replace = rb_intern("replace");
45
+ #endif
46
+
47
+ #ifdef COMPAT_HAVE_ENCODING
48
+ s_enc_ascii8bit = rb_ascii8bit_encindex();
49
+ #endif
50
+ }
51
+
52
+ void msgpack_buffer_static_destroy()
53
+ {
54
+ #ifndef DISABLE_RMEM
55
+ msgpack_rmem_destroy(&s_rmem);
56
+ #endif
57
+ }
58
+
59
+ void msgpack_buffer_init(msgpack_buffer_t* b)
60
+ {
61
+ memset(b, 0, sizeof(msgpack_buffer_t));
62
+
63
+ b->head = &b->tail;
64
+ b->write_reference_threshold = MSGPACK_BUFFER_STRING_WRITE_REFERENCE_DEFAULT;
65
+ b->read_reference_threshold = MSGPACK_BUFFER_STRING_READ_REFERENCE_DEFAULT;
66
+ b->io_buffer_size = MSGPACK_BUFFER_IO_BUFFER_SIZE_DEFAULT;
67
+ b->io = Qnil;
68
+ b->io_buffer = Qnil;
69
+ }
70
+
71
+ static void _msgpack_buffer_chunk_destroy(msgpack_buffer_chunk_t* c)
72
+ {
73
+ if(c->mem != NULL) {
74
+ #ifndef DISABLE_RMEM
75
+ if(!msgpack_rmem_free(&s_rmem, c->mem)) {
76
+ free(c->mem);
77
+ }
78
+ /* no needs to update rmem_owner because chunks will not be
79
+ * free()ed and thus *rmem_owner = NULL is always valid. */
80
+ #else
81
+ free(c->mem);
82
+ #endif
83
+ }
84
+ c->first = NULL;
85
+ c->last = NULL;
86
+ c->mem = NULL;
87
+ }
88
+
89
+ void msgpack_buffer_destroy(msgpack_buffer_t* b)
90
+ {
91
+ /* head is always available */
92
+ msgpack_buffer_chunk_t* c = b->head;
93
+ while(c != &b->tail) {
94
+ msgpack_buffer_chunk_t* n = c->next;
95
+ _msgpack_buffer_chunk_destroy(c);
96
+ free(c);
97
+ c = n;
98
+ }
99
+ _msgpack_buffer_chunk_destroy(c);
100
+
101
+ c = b->free_list;
102
+ while(c != NULL) {
103
+ msgpack_buffer_chunk_t* n = c->next;
104
+ free(c);
105
+ c = n;
106
+ }
107
+ }
108
+
109
+ void msgpack_buffer_mark(msgpack_buffer_t* b)
110
+ {
111
+ /* head is always available */
112
+ msgpack_buffer_chunk_t* c = b->head;
113
+ while(c != &b->tail) {
114
+ rb_gc_mark(c->mapped_string);
115
+ c = c->next;
116
+ }
117
+ rb_gc_mark(c->mapped_string);
118
+
119
+ rb_gc_mark(b->io);
120
+ rb_gc_mark(b->io_buffer);
121
+
122
+ rb_gc_mark(b->owner);
123
+ }
124
+
125
+ bool _msgpack_buffer_shift_chunk(msgpack_buffer_t* b)
126
+ {
127
+ _msgpack_buffer_chunk_destroy(b->head);
128
+
129
+ if(b->head == &b->tail) {
130
+ /* list becomes empty. don't add head to free_list
131
+ * because head should be always available */
132
+ b->tail_buffer_end = NULL;
133
+ b->read_buffer = NULL;
134
+ return false;
135
+ }
136
+
137
+ /* add head to free_list */
138
+ msgpack_buffer_chunk_t* next_head = b->head->next;
139
+ b->head->next = b->free_list;
140
+ b->free_list = b->head;
141
+
142
+ b->head = next_head;
143
+ b->read_buffer = next_head->first;
144
+
145
+ return true;
146
+ }
147
+
148
+ void msgpack_buffer_clear(msgpack_buffer_t* b)
149
+ {
150
+ while(_msgpack_buffer_shift_chunk(b)) {
151
+ ;
152
+ }
153
+ }
154
+
155
+ size_t msgpack_buffer_read_to_string_nonblock(msgpack_buffer_t* b, VALUE string, size_t length)
156
+ {
157
+ size_t avail = msgpack_buffer_top_readable_size(b);
158
+
159
+ #ifndef DISABLE_BUFFER_READ_REFERENCE_OPTIMIZE
160
+ /* optimize */
161
+ if(length <= avail && RSTRING_LEN(string) == 0 &&
162
+ b->head->mapped_string != NO_MAPPED_STRING &&
163
+ length >= b->read_reference_threshold) {
164
+ VALUE s = _msgpack_buffer_refer_head_mapped_string(b, length);
165
+ #ifndef HAVE_RB_STR_REPLACE
166
+ /* TODO MRI 1.8 */
167
+ rb_funcall(string, s_replace, 1, s);
168
+ #else
169
+ rb_str_replace(string, s);
170
+ #endif
171
+ _msgpack_buffer_consumed(b, length);
172
+ return length;
173
+ }
174
+ #endif
175
+
176
+ size_t const length_orig = length;
177
+
178
+ while(true) {
179
+ if(length <= avail) {
180
+ rb_str_buf_cat(string, b->read_buffer, length);
181
+ _msgpack_buffer_consumed(b, length);
182
+ return length_orig;
183
+ }
184
+
185
+ rb_str_buf_cat(string, b->read_buffer, avail);
186
+ length -= avail;
187
+
188
+ if(!_msgpack_buffer_shift_chunk(b)) {
189
+ return length_orig - length;
190
+ }
191
+
192
+ avail = msgpack_buffer_top_readable_size(b);
193
+ }
194
+ }
195
+
196
+ size_t msgpack_buffer_read_nonblock(msgpack_buffer_t* b, char* buffer, size_t length)
197
+ {
198
+ /* buffer == NULL means skip */
199
+ size_t const length_orig = length;
200
+
201
+ while(true) {
202
+ size_t avail = msgpack_buffer_top_readable_size(b);
203
+
204
+ if(length <= avail) {
205
+ if(buffer != NULL) {
206
+ memcpy(buffer, b->read_buffer, length);
207
+ }
208
+ _msgpack_buffer_consumed(b, length);
209
+ return length_orig;
210
+ }
211
+
212
+ if(buffer != NULL) {
213
+ memcpy(buffer, b->read_buffer, avail);
214
+ buffer += avail;
215
+ }
216
+ length -= avail;
217
+
218
+ if(!_msgpack_buffer_shift_chunk(b)) {
219
+ return length_orig - length;
220
+ }
221
+ }
222
+ }
223
+
224
+ size_t msgpack_buffer_all_readable_size(const msgpack_buffer_t* b)
225
+ {
226
+ size_t sz = msgpack_buffer_top_readable_size(b);
227
+
228
+ if(b->head == &b->tail) {
229
+ return sz;
230
+ }
231
+
232
+ msgpack_buffer_chunk_t* c = b->head->next;
233
+
234
+ while(true) {
235
+ sz += c->last - c->first;
236
+ if(c == &b->tail) {
237
+ return sz;
238
+ }
239
+ c = c->next;
240
+ }
241
+ }
242
+
243
+ bool _msgpack_buffer_read_all2(msgpack_buffer_t* b, char* buffer, size_t length)
244
+ {
245
+ if(!msgpack_buffer_ensure_readable(b, length)) {
246
+ return false;
247
+ }
248
+
249
+ msgpack_buffer_read_nonblock(b, buffer, length);
250
+ return true;
251
+ }
252
+
253
+
254
+ static inline msgpack_buffer_chunk_t* _msgpack_buffer_alloc_new_chunk(msgpack_buffer_t* b)
255
+ {
256
+ msgpack_buffer_chunk_t* reuse = b->free_list;
257
+ if(reuse == NULL) {
258
+ return malloc(sizeof(msgpack_buffer_chunk_t));
259
+ }
260
+ b->free_list = b->free_list->next;
261
+ return reuse;
262
+ }
263
+
264
+ static inline void _msgpack_buffer_add_new_chunk(msgpack_buffer_t* b)
265
+ {
266
+ if(b->head == &b->tail) {
267
+ if(b->tail.first == NULL) {
268
+ /* empty buffer */
269
+ return;
270
+ }
271
+
272
+ msgpack_buffer_chunk_t* nc = _msgpack_buffer_alloc_new_chunk(b);
273
+
274
+ *nc = b->tail;
275
+ b->head = nc;
276
+ nc->next = &b->tail;
277
+
278
+ } else {
279
+ /* search node before tail */
280
+ msgpack_buffer_chunk_t* before_tail = b->head;
281
+ while(before_tail->next != &b->tail) {
282
+ before_tail = before_tail->next;
283
+ }
284
+
285
+ msgpack_buffer_chunk_t* nc = _msgpack_buffer_alloc_new_chunk(b);
286
+
287
+ #ifndef DISABLE_RMEM
288
+ #ifndef DISABLE_RMEM_REUSE_INTERNAL_FRAGMENT
289
+ if(b->rmem_owner == &b->tail.mem) {
290
+ /* reuse unused rmem */
291
+ size_t unused = b->tail_buffer_end - b->tail.last;
292
+ b->rmem_last -= unused;
293
+ }
294
+ #endif
295
+ #endif
296
+
297
+ /* rebuild tail */
298
+ *nc = b->tail;
299
+ before_tail->next = nc;
300
+ nc->next = &b->tail;
301
+ }
302
+ }
303
+
304
+ static inline void _msgpack_buffer_append_reference(msgpack_buffer_t* b, VALUE string)
305
+ {
306
+ VALUE mapped_string = rb_str_dup(string);
307
+ #ifdef COMPAT_HAVE_ENCODING
308
+ ENCODING_SET(mapped_string, s_enc_ascii8bit);
309
+ #endif
310
+
311
+ _msgpack_buffer_add_new_chunk(b);
312
+
313
+ char* data = RSTRING_PTR(string);
314
+ size_t length = RSTRING_LEN(string);
315
+
316
+ b->tail.first = (char*) data;
317
+ b->tail.last = (char*) data + length;
318
+ b->tail.mapped_string = mapped_string;
319
+ b->tail.mem = NULL;
320
+
321
+ /* msgpack_buffer_writable_size should return 0 for mapped chunk */
322
+ b->tail_buffer_end = b->tail.last;
323
+
324
+ /* consider read_buffer */
325
+ if(b->head == &b->tail) {
326
+ b->read_buffer = b->tail.first;
327
+ }
328
+ }
329
+
330
+ void _msgpack_buffer_append_long_string(msgpack_buffer_t* b, VALUE string)
331
+ {
332
+ size_t length = RSTRING_LEN(string);
333
+
334
+ if(b->io != Qnil) {
335
+ msgpack_buffer_flush(b);
336
+ rb_funcall(b->io, b->io_write_all_method, 1, string);
337
+
338
+ } else if(!STR_DUP_LIKELY_DOES_COPY(string)) {
339
+ _msgpack_buffer_append_reference(b, string);
340
+
341
+ } else {
342
+ msgpack_buffer_append(b, RSTRING_PTR(string), length);
343
+ }
344
+ }
345
+
346
+ static inline void* _msgpack_buffer_chunk_malloc(
347
+ msgpack_buffer_t* b, msgpack_buffer_chunk_t* c,
348
+ size_t required_size, size_t* allocated_size)
349
+ {
350
+ #ifndef DISABLE_RMEM
351
+ if(required_size <= MSGPACK_RMEM_PAGE_SIZE) {
352
+ #ifndef DISABLE_RMEM_REUSE_INTERNAL_FRAGMENT
353
+ if((size_t)(b->rmem_end - b->rmem_last) < required_size) {
354
+ #endif
355
+ /* alloc new rmem page */
356
+ *allocated_size = MSGPACK_RMEM_PAGE_SIZE;
357
+ char* buffer = msgpack_rmem_alloc(&s_rmem);
358
+ c->mem = buffer;
359
+
360
+ /* update rmem owner */
361
+ b->rmem_owner = &c->mem;
362
+ b->rmem_last = b->rmem_end = buffer + MSGPACK_RMEM_PAGE_SIZE;
363
+
364
+ return buffer;
365
+
366
+ #ifndef DISABLE_RMEM_REUSE_INTERNAL_FRAGMENT
367
+ } else {
368
+ /* reuse unused rmem */
369
+ *allocated_size = (size_t)(b->rmem_end - b->rmem_last);
370
+ char* buffer = b->rmem_last;
371
+ b->rmem_last = b->rmem_end;
372
+
373
+ /* update rmem owner */
374
+ c->mem = *b->rmem_owner;
375
+ *b->rmem_owner = NULL;
376
+ b->rmem_owner = &c->mem;
377
+
378
+ return buffer;
379
+ }
380
+ #endif
381
+ }
382
+ #else
383
+ if(required_size < 72) {
384
+ required_size = 72;
385
+ }
386
+ #endif
387
+
388
+ // TODO alignment?
389
+ *allocated_size = required_size;
390
+ void* mem = malloc(required_size);
391
+ c->mem = mem;
392
+ return mem;
393
+ }
394
+
395
+ static inline void* _msgpack_buffer_chunk_realloc(
396
+ msgpack_buffer_t* b, msgpack_buffer_chunk_t* c,
397
+ void* mem, size_t required_size, size_t* current_size)
398
+ {
399
+ if(mem == NULL) {
400
+ return _msgpack_buffer_chunk_malloc(b, c, required_size, current_size);
401
+ }
402
+
403
+ size_t next_size = *current_size * 2;
404
+ while(next_size < required_size) {
405
+ next_size *= 2;
406
+ }
407
+ *current_size = next_size;
408
+ mem = realloc(mem, next_size);
409
+
410
+ c->mem = mem;
411
+ return mem;
412
+ }
413
+
414
+ void _msgpack_buffer_expand(msgpack_buffer_t* b, const char* data, size_t length, bool flush_to_io)
415
+ {
416
+ if(flush_to_io && b->io != Qnil) {
417
+ msgpack_buffer_flush(b);
418
+ if(msgpack_buffer_writable_size(b) >= length) {
419
+ /* data == NULL means ensure_writable */
420
+ if(data != NULL) {
421
+ size_t tail_avail = msgpack_buffer_writable_size(b);
422
+ memcpy(b->tail.last, data, length);
423
+ b->tail.last += tail_avail;
424
+ }
425
+ return;
426
+ }
427
+ }
428
+
429
+ /* data == NULL means ensure_writable */
430
+ if(data != NULL) {
431
+ size_t tail_avail = msgpack_buffer_writable_size(b);
432
+ memcpy(b->tail.last, data, tail_avail);
433
+ b->tail.last += tail_avail;
434
+ data += tail_avail;
435
+ length -= tail_avail;
436
+ }
437
+
438
+ size_t capacity = b->tail.last - b->tail.first;
439
+
440
+ /* can't realloc mapped chunk or rmem page */
441
+ if(b->tail.mapped_string != NO_MAPPED_STRING
442
+ #ifndef DISABLE_RMEM
443
+ || capacity <= MSGPACK_RMEM_PAGE_SIZE
444
+ #endif
445
+ ) {
446
+ /* allocate new chunk */
447
+ _msgpack_buffer_add_new_chunk(b);
448
+
449
+ char* mem = _msgpack_buffer_chunk_malloc(b, &b->tail, length, &capacity);
450
+
451
+ char* last = mem;
452
+ if(data != NULL) {
453
+ memcpy(mem, data, length);
454
+ last += length;
455
+ }
456
+
457
+ /* rebuild tail chunk */
458
+ b->tail.first = mem;
459
+ b->tail.last = last;
460
+ b->tail.mapped_string = NO_MAPPED_STRING;
461
+ b->tail_buffer_end = mem + capacity;
462
+
463
+ /* consider read_buffer */
464
+ if(b->head == &b->tail) {
465
+ b->read_buffer = b->tail.first;
466
+ }
467
+
468
+ } else {
469
+ /* realloc malloc()ed chunk or NULL */
470
+ size_t tail_filled = b->tail.last - b->tail.first;
471
+ char* mem = _msgpack_buffer_chunk_realloc(b, &b->tail,
472
+ b->tail.first, tail_filled+length, &capacity);
473
+
474
+ char* last = mem + tail_filled;
475
+ if(data != NULL) {
476
+ memcpy(last, data, length);
477
+ last += length;
478
+ }
479
+
480
+ /* consider read_buffer */
481
+ if(b->head == &b->tail) {
482
+ size_t read_offset = b->read_buffer - b->head->first;
483
+ b->read_buffer = mem + read_offset;
484
+ }
485
+
486
+ /* rebuild tail chunk */
487
+ b->tail.first = mem;
488
+ b->tail.last = last;
489
+ b->tail_buffer_end = mem + capacity;
490
+ }
491
+ }
492
+
493
+ static inline VALUE _msgpack_buffer_head_chunk_as_string(msgpack_buffer_t* b)
494
+ {
495
+ size_t length = b->head->last - b->read_buffer;
496
+ if(length == 0) {
497
+ return rb_str_buf_new(0);
498
+ }
499
+
500
+ if(b->head->mapped_string != NO_MAPPED_STRING) {
501
+ return _msgpack_buffer_refer_head_mapped_string(b, length);
502
+ }
503
+
504
+ return rb_str_new(b->read_buffer, length);
505
+ }
506
+
507
+ static inline VALUE _msgpack_buffer_chunk_as_string(msgpack_buffer_chunk_t* c)
508
+ {
509
+ size_t chunk_size = c->last - c->first;
510
+ if(chunk_size == 0) {
511
+ return rb_str_buf_new(0);
512
+ }
513
+
514
+ if(c->mapped_string != NO_MAPPED_STRING) {
515
+ return rb_str_dup(c->mapped_string);
516
+ }
517
+
518
+ return rb_str_new(c->first, chunk_size);
519
+ }
520
+
521
+ VALUE msgpack_buffer_all_as_string(msgpack_buffer_t* b)
522
+ {
523
+ if(b->head == &b->tail) {
524
+ return _msgpack_buffer_head_chunk_as_string(b);
525
+ }
526
+
527
+ size_t length = msgpack_buffer_all_readable_size(b);
528
+ VALUE string = rb_str_new(NULL, length);
529
+ char* buffer = RSTRING_PTR(string);
530
+
531
+ size_t avail = msgpack_buffer_top_readable_size(b);
532
+ memcpy(buffer, b->read_buffer, avail);
533
+ buffer += avail;
534
+ length -= avail;
535
+
536
+ msgpack_buffer_chunk_t* c = b->head->next;
537
+
538
+ while(true) {
539
+ avail = c->last - c->first;
540
+ memcpy(buffer, c->first, avail);
541
+
542
+ if(length <= avail) {
543
+ return string;
544
+ }
545
+ buffer += avail;
546
+ length -= avail;
547
+
548
+ c = c->next;
549
+ }
550
+ }
551
+
552
+ VALUE msgpack_buffer_all_as_string_array(msgpack_buffer_t* b)
553
+ {
554
+ if(b->head == &b->tail) {
555
+ VALUE s = msgpack_buffer_all_as_string(b);
556
+ VALUE ary = rb_ary_new3(1, s);
557
+ return ary;
558
+ }
559
+
560
+ /* TODO optimize ary construction */
561
+ VALUE ary = rb_ary_new();
562
+
563
+ VALUE s = _msgpack_buffer_head_chunk_as_string(b);
564
+ rb_ary_push(ary, s);
565
+
566
+ msgpack_buffer_chunk_t* c = b->head->next;
567
+
568
+ while(true) {
569
+ s = _msgpack_buffer_chunk_as_string(c);
570
+ rb_ary_push(ary, s);
571
+ if(c == &b->tail) {
572
+ return ary;
573
+ }
574
+ c = c->next;
575
+ }
576
+
577
+ return ary;
578
+ }
579
+
580
+ size_t msgpack_buffer_flush_to_io(msgpack_buffer_t* b, VALUE io, ID write_method, bool consume)
581
+ {
582
+ if(msgpack_buffer_top_readable_size(b) == 0) {
583
+ return 0;
584
+ }
585
+
586
+ VALUE s = _msgpack_buffer_head_chunk_as_string(b);
587
+ rb_funcall(io, write_method, 1, s);
588
+ size_t sz = RSTRING_LEN(s);
589
+
590
+ if(consume) {
591
+ while(_msgpack_buffer_shift_chunk(b)) {
592
+ s = _msgpack_buffer_chunk_as_string(b->head);
593
+ rb_funcall(io, write_method, 1, s);
594
+ sz += RSTRING_LEN(s);
595
+ }
596
+ return sz;
597
+
598
+ } else {
599
+ if(b->head == &b->tail) {
600
+ return sz;
601
+ }
602
+ msgpack_buffer_chunk_t* c = b->head->next;
603
+ while(true) {
604
+ s = _msgpack_buffer_chunk_as_string(c);
605
+ rb_funcall(io, write_method, 1, s);
606
+ sz += RSTRING_LEN(s);
607
+ if(c == &b->tail) {
608
+ return sz;
609
+ }
610
+ c = c->next;
611
+ }
612
+ }
613
+ }
614
+
615
+ size_t _msgpack_buffer_feed_from_io(msgpack_buffer_t* b)
616
+ {
617
+ if(b->io_buffer == Qnil) {
618
+ b->io_buffer = rb_funcall(b->io, b->io_partial_read_method, 1, LONG2FIX(b->io_buffer_size));
619
+ if(b->io_buffer == Qnil) {
620
+ rb_raise(rb_eEOFError, "IO reached end of file");
621
+ }
622
+ StringValue(b->io_buffer);
623
+ } else {
624
+ VALUE ret = rb_funcall(b->io, b->io_partial_read_method, 2, LONG2FIX(b->io_buffer_size), b->io_buffer);
625
+ if(ret == Qnil) {
626
+ rb_raise(rb_eEOFError, "IO reached end of file");
627
+ }
628
+ }
629
+
630
+ size_t len = RSTRING_LEN(b->io_buffer);
631
+ if(len == 0) {
632
+ rb_raise(rb_eEOFError, "IO reached end of file");
633
+ }
634
+
635
+ /* TODO zero-copy optimize? */
636
+ msgpack_buffer_append_nonblock(b, RSTRING_PTR(b->io_buffer), len);
637
+
638
+ return len;
639
+ }
640
+
641
+ size_t _msgpack_buffer_read_from_io_to_string(msgpack_buffer_t* b, VALUE string, size_t length)
642
+ {
643
+ if(RSTRING_LEN(string) == 0) {
644
+ /* direct read */
645
+ VALUE ret = rb_funcall(b->io, b->io_partial_read_method, 2, LONG2FIX(length), string);
646
+ if(ret == Qnil) {
647
+ return 0;
648
+ }
649
+ return RSTRING_LEN(string);
650
+ }
651
+
652
+ /* copy via io_buffer */
653
+ if(b->io_buffer == Qnil) {
654
+ b->io_buffer = rb_str_buf_new(0);
655
+ }
656
+
657
+ VALUE ret = rb_funcall(b->io, b->io_partial_read_method, 2, LONG2FIX(length), b->io_buffer);
658
+ if(ret == Qnil) {
659
+ return 0;
660
+ }
661
+ size_t rl = RSTRING_LEN(b->io_buffer);
662
+
663
+ rb_str_buf_cat(string, (const void*)RSTRING_PTR(b->io_buffer), rl);
664
+ return rl;
665
+ }
666
+
667
+ size_t _msgpack_buffer_skip_from_io(msgpack_buffer_t* b, size_t length)
668
+ {
669
+ if(b->io_buffer == Qnil) {
670
+ b->io_buffer = rb_str_buf_new(0);
671
+ }
672
+
673
+ VALUE ret = rb_funcall(b->io, b->io_partial_read_method, 2, LONG2FIX(length), b->io_buffer);
674
+ if(ret == Qnil) {
675
+ return 0;
676
+ }
677
+ return RSTRING_LEN(b->io_buffer);
678
+ }
679
+