packsnap 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +17 -0
  2. data/ChangeLog +29 -0
  3. data/README.rdoc +26 -0
  4. data/Rakefile +150 -0
  5. data/doclib/msgpack.rb +54 -0
  6. data/doclib/packsnap/buffer.rb +177 -0
  7. data/doclib/packsnap/error.rb +14 -0
  8. data/doclib/packsnap/packer.rb +131 -0
  9. data/doclib/packsnap/unpacker.rb +130 -0
  10. data/ext/packsnap/buffer.cc +684 -0
  11. data/ext/packsnap/buffer.hh +439 -0
  12. data/ext/packsnap/buffer_class.cc +490 -0
  13. data/ext/packsnap/buffer_class.hh +32 -0
  14. data/ext/packsnap/compat.h +128 -0
  15. data/ext/packsnap/extconf.rb +94 -0
  16. data/ext/packsnap/packer.cc +137 -0
  17. data/ext/packsnap/packer.h +334 -0
  18. data/ext/packsnap/packer_class.cc +288 -0
  19. data/ext/packsnap/packer_class.hh +32 -0
  20. data/ext/packsnap/packsnap.h +4 -0
  21. data/ext/packsnap/rbinit.cc +52 -0
  22. data/ext/packsnap/rmem.cc +110 -0
  23. data/ext/packsnap/rmem.h +100 -0
  24. data/ext/packsnap/sysdep.h +112 -0
  25. data/ext/packsnap/sysdep_endian.h +50 -0
  26. data/ext/packsnap/sysdep_types.h +46 -0
  27. data/ext/packsnap/unpacker.cc +654 -0
  28. data/ext/packsnap/unpacker.hh +108 -0
  29. data/ext/packsnap/unpacker_class.cc +392 -0
  30. data/ext/packsnap/unpacker_class.hh +32 -0
  31. data/lib/packsnap.rb +12 -0
  32. data/lib/packsnap/version.rb +3 -0
  33. data/packsnap.gemspec +23 -0
  34. data/spec/buffer_io_spec.rb +228 -0
  35. data/spec/buffer_spec.rb +572 -0
  36. data/spec/cases.json +1 -0
  37. data/spec/cases.msg +0 -0
  38. data/spec/cases_compact.msg +0 -0
  39. data/spec/cases_spec.rb +39 -0
  40. data/spec/format_spec.rb +225 -0
  41. data/spec/packer_spec.rb +127 -0
  42. data/spec/random_compat.rb +24 -0
  43. data/spec/spec_helper.rb +21 -0
  44. data/spec/unpacker_spec.rb +128 -0
  45. metadata +183 -0
@@ -0,0 +1,14 @@
1
+ module Packsnap
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 Packsnap
2
+
3
+ #
4
+ # Packsnap::Packer is an interface to serialize objects into an internal buffer,
5
+ # which is a Packsnap::Buffer.
6
+ #
7
+ class Packer
8
+ #
9
+ # Creates a Packsnap::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 Packsnap::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 Packsnap
2
+
3
+ #
4
+ # Packsnap::Unpacker is an interface to deserialize objects from an internal buffer,
5
+ # which is a Packsnap::Buffer.
6
+ #
7
+ class Unpacker
8
+ #
9
+ # Creates a Packsnap::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 [Packsnap::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 Packsnap::MalformedFormatError.
37
+ # If the stack is too deep, this method raises Packsnap::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 Packsnap::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 Packsnap::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,684 @@
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 "snappy.h"
20
+ #include "buffer.hh"
21
+ #include "rmem.h"
22
+
23
+ #ifdef RUBY_VM
24
+ #define HAVE_RB_STR_REPLACE
25
+ #endif
26
+
27
+ #ifndef HAVE_RB_STR_REPLACE
28
+ static ID s_replace;
29
+ #endif
30
+
31
+ #ifndef DISABLE_RMEM
32
+ static msgpack_rmem_t s_rmem;
33
+ #endif
34
+
35
+ void msgpack_buffer_static_init()
36
+ {
37
+ #ifndef DISABLE_RMEM
38
+ msgpack_rmem_init(&s_rmem);
39
+ #endif
40
+ #ifndef HAVE_RB_STR_REPLACE
41
+ s_replace = rb_intern("replace");
42
+ #endif
43
+ }
44
+
45
+ void msgpack_buffer_static_destroy()
46
+ {
47
+ #ifndef DISABLE_RMEM
48
+ msgpack_rmem_destroy(&s_rmem);
49
+ #endif
50
+ }
51
+
52
+ void msgpack_buffer_init(msgpack_buffer_t* b)
53
+ {
54
+ memset(b, 0, sizeof(msgpack_buffer_t));
55
+
56
+ b->head = &b->tail;
57
+ b->write_reference_threshold = MSGPACK_BUFFER_STRING_WRITE_REFERENCE_DEFAULT;
58
+ b->read_reference_threshold = MSGPACK_BUFFER_STRING_READ_REFERENCE_DEFAULT;
59
+ b->io_buffer_size = MSGPACK_BUFFER_IO_BUFFER_SIZE_DEFAULT;
60
+ b->io = Qnil;
61
+ b->io_buffer = Qnil;
62
+ }
63
+
64
+ static void _msgpack_buffer_chunk_destroy(msgpack_buffer_chunk_t* c)
65
+ {
66
+ if(c->mem != NULL) {
67
+ #ifndef DISABLE_RMEM
68
+ if(!msgpack_rmem_free(&s_rmem, c->mem)) {
69
+ free(c->mem);
70
+ }
71
+ /* no needs to update rmem_owner because chunks will not be
72
+ * free()ed and thus *rmem_owner = NULL is always valid. */
73
+ #else
74
+ free(c->mem);
75
+ #endif
76
+ }
77
+ c->first = NULL;
78
+ c->last = NULL;
79
+ c->mem = NULL;
80
+ }
81
+
82
+ void msgpack_buffer_destroy(msgpack_buffer_t* b)
83
+ {
84
+ /* head is always available */
85
+ msgpack_buffer_chunk_t* c = b->head;
86
+ while(c != &b->tail) {
87
+ msgpack_buffer_chunk_t* n = c->next;
88
+ _msgpack_buffer_chunk_destroy(c);
89
+ free(c);
90
+ c = n;
91
+ }
92
+ _msgpack_buffer_chunk_destroy(c);
93
+
94
+ c = b->free_list;
95
+ while(c != NULL) {
96
+ msgpack_buffer_chunk_t* n = c->next;
97
+ free(c);
98
+ c = n;
99
+ }
100
+ }
101
+
102
+ void msgpack_buffer_mark(msgpack_buffer_t* b)
103
+ {
104
+ /* head is always available */
105
+ msgpack_buffer_chunk_t* c = b->head;
106
+ while(c != &b->tail) {
107
+ rb_gc_mark(c->mapped_string);
108
+ c = c->next;
109
+ }
110
+ rb_gc_mark(c->mapped_string);
111
+
112
+ rb_gc_mark(b->io);
113
+ rb_gc_mark(b->io_buffer);
114
+
115
+ rb_gc_mark(b->owner);
116
+ }
117
+
118
+ bool _msgpack_buffer_shift_chunk(msgpack_buffer_t* b)
119
+ {
120
+ _msgpack_buffer_chunk_destroy(b->head);
121
+
122
+ if(b->head == &b->tail) {
123
+ /* list becomes empty. don't add head to free_list
124
+ * because head should be always available */
125
+ b->tail_buffer_end = NULL;
126
+ b->read_buffer = NULL;
127
+ return false;
128
+ }
129
+
130
+ /* add head to free_list */
131
+ msgpack_buffer_chunk_t* next_head = b->head->next;
132
+ b->head->next = b->free_list;
133
+ b->free_list = b->head;
134
+
135
+ b->head = next_head;
136
+ b->read_buffer = next_head->first;
137
+
138
+ return true;
139
+ }
140
+
141
+ void msgpack_buffer_clear(msgpack_buffer_t* b)
142
+ {
143
+ while(_msgpack_buffer_shift_chunk(b)) {
144
+ ;
145
+ }
146
+ }
147
+
148
+ size_t msgpack_buffer_read_to_string_nonblock(msgpack_buffer_t* b, VALUE string, size_t length)
149
+ {
150
+ size_t avail = msgpack_buffer_top_readable_size(b);
151
+
152
+ #ifndef DISABLE_BUFFER_READ_REFERENCE_OPTIMIZE
153
+ /* optimize */
154
+ if(length <= avail && RSTRING_LEN(string) == 0 &&
155
+ b->head->mapped_string != NO_MAPPED_STRING &&
156
+ length >= b->read_reference_threshold) {
157
+ VALUE s = _msgpack_buffer_refer_head_mapped_string(b, length);
158
+ #ifndef HAVE_RB_STR_REPLACE
159
+ /* TODO MRI 1.8 */
160
+ rb_funcall(string, s_replace, 1, s);
161
+ #else
162
+ rb_str_replace(string, s);
163
+ #endif
164
+ _msgpack_buffer_consumed(b, length);
165
+ return length;
166
+ }
167
+ #endif
168
+
169
+ size_t const length_orig = length;
170
+
171
+ while(true) {
172
+ //printf("avail: %lu\n", avail);
173
+ //printf("length: %lu\n", length);
174
+ if(length <= avail) {
175
+ //printf("read_buffer: %p\n", b->read_buffer);
176
+ rb_str_buf_cat(string, b->read_buffer, length);
177
+ _msgpack_buffer_consumed(b, length);
178
+ return length_orig;
179
+ }
180
+
181
+ rb_str_buf_cat(string, b->read_buffer, avail);
182
+ length -= avail;
183
+
184
+ if(!_msgpack_buffer_shift_chunk(b)) {
185
+ return length_orig - length;
186
+ }
187
+
188
+ avail = msgpack_buffer_top_readable_size(b);
189
+ }
190
+ }
191
+
192
+ size_t msgpack_buffer_read_nonblock(msgpack_buffer_t* b, char* buffer, size_t length)
193
+ {
194
+ /* buffer == NULL means skip */
195
+ size_t const length_orig = length;
196
+
197
+ while(true) {
198
+ size_t avail = msgpack_buffer_top_readable_size(b);
199
+
200
+ if(length <= avail) {
201
+ if(buffer != NULL) {
202
+ memcpy(buffer, b->read_buffer, length);
203
+ }
204
+ _msgpack_buffer_consumed(b, length);
205
+ return length_orig;
206
+ }
207
+
208
+ if(buffer != NULL) {
209
+ memcpy(buffer, b->read_buffer, avail);
210
+ buffer += avail;
211
+ }
212
+ length -= avail;
213
+
214
+ if(!_msgpack_buffer_shift_chunk(b)) {
215
+ return length_orig - length;
216
+ }
217
+ }
218
+ }
219
+
220
+ size_t msgpack_buffer_all_readable_size(const msgpack_buffer_t* b)
221
+ {
222
+ size_t sz = msgpack_buffer_top_readable_size(b);
223
+
224
+ if(b->head == &b->tail) {
225
+ return sz;
226
+ }
227
+
228
+ msgpack_buffer_chunk_t* c = b->head->next;
229
+
230
+ while(true) {
231
+ sz += c->last - c->first;
232
+ if(c == &b->tail) {
233
+ return sz;
234
+ }
235
+ c = c->next;
236
+ }
237
+ }
238
+
239
+ bool _msgpack_buffer_read_all2(msgpack_buffer_t* b, char* buffer, size_t length)
240
+ {
241
+ if(!msgpack_buffer_ensure_readable(b, length)) {
242
+ return false;
243
+ }
244
+
245
+ msgpack_buffer_read_nonblock(b, buffer, length);
246
+ return true;
247
+ }
248
+
249
+
250
+ static inline msgpack_buffer_chunk_t* _msgpack_buffer_alloc_new_chunk(msgpack_buffer_t* b)
251
+ {
252
+ msgpack_buffer_chunk_t* reuse = b->free_list;
253
+ if(reuse == NULL) {
254
+ return (msgpack_buffer_chunk_t*)malloc(sizeof(msgpack_buffer_chunk_t));
255
+ }
256
+ b->free_list = b->free_list->next;
257
+ return reuse;
258
+ }
259
+
260
+ static inline void _msgpack_buffer_add_new_chunk(msgpack_buffer_t* b)
261
+ {
262
+ if(b->head == &b->tail) {
263
+ if(b->tail.first == NULL) {
264
+ /* empty buffer */
265
+ return;
266
+ }
267
+
268
+ msgpack_buffer_chunk_t* nc = _msgpack_buffer_alloc_new_chunk(b);
269
+
270
+ *nc = b->tail;
271
+ b->head = nc;
272
+ nc->next = &b->tail;
273
+
274
+ } else {
275
+ /* search node before tail */
276
+ msgpack_buffer_chunk_t* before_tail = b->head;
277
+ while(before_tail->next != &b->tail) {
278
+ before_tail = before_tail->next;
279
+ }
280
+
281
+ msgpack_buffer_chunk_t* nc = _msgpack_buffer_alloc_new_chunk(b);
282
+
283
+ #ifndef DISABLE_RMEM
284
+ #ifndef DISABLE_RMEM_REUSE_INTERNAL_FRAGMENT
285
+ if(b->rmem_owner == &b->tail.mem) {
286
+ /* reuse unused rmem */
287
+ size_t unused = b->tail_buffer_end - b->tail.last;
288
+ b->rmem_last -= unused;
289
+ }
290
+ #endif
291
+ #endif
292
+
293
+ /* rebuild tail */
294
+ *nc = b->tail;
295
+ before_tail->next = nc;
296
+ nc->next = &b->tail;
297
+ }
298
+ }
299
+
300
+ static inline void _msgpack_buffer_append_reference(msgpack_buffer_t* b, VALUE string)
301
+ {
302
+ VALUE mapped_string = rb_str_dup(string);
303
+ #ifdef COMPAT_HAVE_ENCODING
304
+ ENCODING_SET(mapped_string, s_enc_ascii8bit);
305
+ #endif
306
+
307
+ _msgpack_buffer_add_new_chunk(b);
308
+
309
+ char* data = RSTRING_PTR(string);
310
+ size_t length =RSTRING_LEN(string);
311
+
312
+ b->tail.first = (char*) data;
313
+ b->tail.last = (char*) data + length;
314
+ b->tail.mapped_string = mapped_string;
315
+ b->tail.mem = NULL;
316
+
317
+ /* msgpack_buffer_writable_size should return 0 for mapped chunk */
318
+ b->tail_buffer_end = b->tail.last;
319
+
320
+ /* consider read_buffer */
321
+ if(b->head == &b->tail) {
322
+ b->read_buffer = b->tail.first;
323
+ }
324
+ }
325
+
326
+ void _msgpack_buffer_append_long_string(msgpack_buffer_t* b, VALUE string)
327
+ {
328
+ size_t length = RSTRING_LEN(string);
329
+
330
+ if(b->io != Qnil) {
331
+ msgpack_buffer_flush(b);
332
+ rb_funcall(b->io, b->io_write_all_method, 1, string);
333
+
334
+ } else if(!STR_DUP_LIKELY_DOES_COPY(string)) {
335
+ _msgpack_buffer_append_reference(b, string);
336
+
337
+ } else {
338
+ msgpack_buffer_append(b, RSTRING_PTR(string), length);
339
+ }
340
+ }
341
+
342
+ static inline void* _msgpack_buffer_chunk_malloc(
343
+ msgpack_buffer_t* b, msgpack_buffer_chunk_t* c,
344
+ size_t required_size, size_t* allocated_size)
345
+ {
346
+ #ifndef DISABLE_RMEM
347
+ if(required_size <= MSGPACK_RMEM_PAGE_SIZE) {
348
+ #ifndef DISABLE_RMEM_REUSE_INTERNAL_FRAGMENT
349
+ if((size_t)(b->rmem_end - b->rmem_last) < required_size) {
350
+ #endif
351
+ /* alloc new rmem page */
352
+ *allocated_size = MSGPACK_RMEM_PAGE_SIZE;
353
+ char* buffer = (char*)msgpack_rmem_alloc(&s_rmem);
354
+ c->mem = buffer;
355
+
356
+ /* update rmem owner */
357
+ b->rmem_owner = &c->mem;
358
+ b->rmem_last = b->rmem_end = buffer + MSGPACK_RMEM_PAGE_SIZE;
359
+
360
+ return buffer;
361
+
362
+ #ifndef DISABLE_RMEM_REUSE_INTERNAL_FRAGMENT
363
+ } else {
364
+ /* reuse unused rmem */
365
+ *allocated_size = (size_t)(b->rmem_end - b->rmem_last);
366
+ char* buffer = b->rmem_last;
367
+ b->rmem_last = b->rmem_end;
368
+
369
+ /* update rmem owner */
370
+ c->mem = *b->rmem_owner;
371
+ *b->rmem_owner = NULL;
372
+ b->rmem_owner = &c->mem;
373
+
374
+ return buffer;
375
+ }
376
+ #endif
377
+ }
378
+ #else
379
+ if(required_size < 72) {
380
+ required_size = 72;
381
+ }
382
+ #endif
383
+
384
+ // TODO alignment?
385
+ *allocated_size = required_size;
386
+ void* mem = malloc(required_size);
387
+ c->mem = mem;
388
+ return mem;
389
+ }
390
+
391
+ static inline void* _msgpack_buffer_chunk_realloc(
392
+ msgpack_buffer_t* b, msgpack_buffer_chunk_t* c,
393
+ void* mem, size_t required_size, size_t* current_size)
394
+ {
395
+ if(mem == NULL) {
396
+ return _msgpack_buffer_chunk_malloc(b, c, required_size, current_size);
397
+ }
398
+
399
+ size_t next_size = *current_size * 2;
400
+ while(next_size < required_size) {
401
+ next_size *= 2;
402
+ }
403
+ *current_size = next_size;
404
+ mem = realloc(mem, next_size);
405
+
406
+ c->mem = mem;
407
+ return mem;
408
+ }
409
+
410
+ void _msgpack_buffer_expand(msgpack_buffer_t* b, const char* data, size_t length, bool flush_to_io)
411
+ {
412
+ if(flush_to_io && b->io != Qnil) {
413
+ msgpack_buffer_flush(b);
414
+ if(msgpack_buffer_writable_size(b) >= length) {
415
+ /* data == NULL means ensure_writable */
416
+ if(data != NULL) {
417
+ size_t tail_avail = msgpack_buffer_writable_size(b);
418
+ memcpy(b->tail.last, data, length);
419
+ b->tail.last += tail_avail;
420
+ }
421
+ return;
422
+ }
423
+ }
424
+
425
+ /* data == NULL means ensure_writable */
426
+ if(data != NULL) {
427
+ size_t tail_avail = msgpack_buffer_writable_size(b);
428
+ memcpy(b->tail.last, data, tail_avail);
429
+ b->tail.last += tail_avail;
430
+ data += tail_avail;
431
+ length -= tail_avail;
432
+ }
433
+
434
+ size_t capacity = b->tail.last - b->tail.first;
435
+
436
+ /* can't realloc mapped chunk or rmem page */
437
+ if(b->tail.mapped_string != NO_MAPPED_STRING
438
+ #ifndef DISABLE_RMEM
439
+ || capacity <= MSGPACK_RMEM_PAGE_SIZE
440
+ #endif
441
+ ) {
442
+ /* allocate new chunk */
443
+ _msgpack_buffer_add_new_chunk(b);
444
+
445
+ char* mem = (char*)_msgpack_buffer_chunk_malloc(b, &b->tail, length, &capacity);
446
+ //printf("tail malloc()ed capacity %lu\n", capacity);
447
+
448
+ char* last = mem;
449
+ if(data != NULL) {
450
+ memcpy(mem, data, length);
451
+ last += length;
452
+ }
453
+
454
+ /* rebuild tail chunk */
455
+ b->tail.first = mem;
456
+ b->tail.last = last;
457
+ b->tail.mapped_string = NO_MAPPED_STRING;
458
+ b->tail_buffer_end = mem + capacity;
459
+
460
+ /* consider read_buffer */
461
+ if(b->head == &b->tail) {
462
+ b->read_buffer = b->tail.first;
463
+ }
464
+
465
+ } else {
466
+ /* realloc malloc()ed chunk or NULL */
467
+ size_t tail_filled = b->tail.last - b->tail.first;
468
+ char* mem = (char*)_msgpack_buffer_chunk_realloc(b, &b->tail,
469
+ b->tail.first, tail_filled+length, &capacity);
470
+ //printf("tail realloc()ed capacity %lu filled=%lu\n", capacity, tail_filled);
471
+
472
+ char* last = mem + tail_filled;
473
+ if(data != NULL) {
474
+ memcpy(last, data, length);
475
+ last += length;
476
+ }
477
+
478
+ /* consider read_buffer */
479
+ if(b->head == &b->tail) {
480
+ size_t read_offset = b->read_buffer - b->head->first;
481
+ //printf("relink read_buffer read_offset %lu\n", read_offset);
482
+ b->read_buffer = mem + read_offset;
483
+ }
484
+
485
+ /* rebuild tail chunk */
486
+ b->tail.first = mem;
487
+ b->tail.last = last;
488
+ b->tail_buffer_end = mem + capacity;
489
+ //printf("alloced chunk size: %lu\n", msgpack_buffer_top_readable_size(b));
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
+ static VALUE
522
+ _msgpack_buffer_snappify_string(VALUE src)
523
+ {
524
+ VALUE dst;
525
+ size_t output_length;
526
+
527
+ output_length = snappy::MaxCompressedLength(RSTRING_LEN(src));
528
+
529
+ dst = rb_str_new(NULL, output_length);
530
+
531
+ snappy::RawCompress(RSTRING_PTR(src), RSTRING_LEN(src), RSTRING_PTR(dst), &output_length);
532
+ rb_str_resize(dst, output_length);
533
+
534
+ return dst;
535
+ }
536
+
537
+ VALUE msgpack_buffer_all_as_string(msgpack_buffer_t* b)
538
+ {
539
+ if(b->head == &b->tail) {
540
+ return _msgpack_buffer_snappify_string(_msgpack_buffer_head_chunk_as_string(b));
541
+ }
542
+
543
+ size_t length = msgpack_buffer_all_readable_size(b);
544
+ VALUE string = rb_str_new(NULL, length);
545
+ char* buffer = RSTRING_PTR(string);
546
+
547
+ size_t avail = msgpack_buffer_top_readable_size(b);
548
+ memcpy(buffer, b->read_buffer, avail);
549
+ buffer += avail;
550
+ length -= avail;
551
+
552
+ msgpack_buffer_chunk_t* c = b->head->next;
553
+
554
+ while(true) {
555
+ avail = c->last - c->first;
556
+ memcpy(buffer, c->first, avail);
557
+
558
+ if(length <= avail) {
559
+ return _msgpack_buffer_snappify_string(string);
560
+ }
561
+ buffer += avail;
562
+ length -= avail;
563
+
564
+ c = c->next;
565
+ }
566
+ }
567
+
568
+ VALUE msgpack_buffer_all_as_string_array(msgpack_buffer_t* b)
569
+ {
570
+ if(b->head == &b->tail) {
571
+ VALUE s = msgpack_buffer_all_as_string(b);
572
+ VALUE ary = rb_ary_new(); /* TODO capacity = 1 */
573
+ rb_ary_push(ary, s);
574
+ return ary;
575
+ }
576
+
577
+ /* TODO optimize ary construction */
578
+ VALUE ary = rb_ary_new();
579
+
580
+ VALUE s = _msgpack_buffer_head_chunk_as_string(b);
581
+ rb_ary_push(ary, s);
582
+
583
+ msgpack_buffer_chunk_t* c = b->head->next;
584
+
585
+ while(true) {
586
+ s = _msgpack_buffer_chunk_as_string(c);
587
+ rb_ary_push(ary, s);
588
+ if(c == &b->tail) {
589
+ return ary;
590
+ }
591
+ c = c->next;
592
+ }
593
+
594
+ return ary;
595
+ }
596
+
597
+ size_t msgpack_buffer_flush_to_io(msgpack_buffer_t* b, VALUE io, ID write_method, bool consume)
598
+ {
599
+ if(msgpack_buffer_top_readable_size(b) == 0) {
600
+ return 0;
601
+ }
602
+
603
+ VALUE s = _msgpack_buffer_head_chunk_as_string(b);
604
+ rb_funcall(io, write_method, 1, s);
605
+ size_t sz = RSTRING_LEN(s);
606
+
607
+ if(consume) {
608
+ while(_msgpack_buffer_shift_chunk(b)) {
609
+ s = _msgpack_buffer_chunk_as_string(b->head);
610
+ rb_funcall(io, write_method, 1, s);
611
+ sz += RSTRING_LEN(s);
612
+ }
613
+ return sz;
614
+
615
+ } else {
616
+ if(b->head == &b->tail) {
617
+ return sz;
618
+ }
619
+ msgpack_buffer_chunk_t* c = b->head->next;
620
+ while(true) {
621
+ s = _msgpack_buffer_chunk_as_string(c);
622
+ rb_funcall(io, write_method, 1, s);
623
+ sz += RSTRING_LEN(s);
624
+ if(c == &b->tail) {
625
+ return sz;
626
+ }
627
+ c = c->next;
628
+ }
629
+ }
630
+ }
631
+
632
+ size_t _msgpack_buffer_feed_from_io(msgpack_buffer_t* b)
633
+ {
634
+ if(b->io_buffer == Qnil) {
635
+ b->io_buffer = rb_funcall(b->io, b->io_partial_read_method, 1, LONG2FIX(b->io_buffer_size));
636
+ if(b->io_buffer == Qnil) {
637
+ rb_raise(rb_eEOFError, "IO reached end of file");
638
+ }
639
+ StringValue(b->io_buffer);
640
+ } else {
641
+ rb_funcall(b->io, b->io_partial_read_method, 2, LONG2FIX(b->io_buffer_size), b->io_buffer);
642
+ }
643
+
644
+ size_t len = RSTRING_LEN(b->io_buffer);
645
+ if(len == 0) {
646
+ rb_raise(rb_eEOFError, "IO reached end of file");
647
+ }
648
+
649
+ /* TODO zero-copy optimize? */
650
+ msgpack_buffer_append_nonblock(b, RSTRING_PTR(b->io_buffer), len);
651
+
652
+ return len;
653
+ }
654
+
655
+ size_t _msgpack_buffer_read_from_io_to_string(msgpack_buffer_t* b, VALUE string, size_t length)
656
+ {
657
+ if(RSTRING_LEN(string) == 0) {
658
+ /* direct read */
659
+ rb_funcall(b->io, b->io_partial_read_method, 2, LONG2FIX(length), string);
660
+ return RSTRING_LEN(string);
661
+ }
662
+
663
+ /* copy via io_buffer */
664
+ if(b->io_buffer == Qnil) {
665
+ b->io_buffer = rb_str_buf_new(0);
666
+ }
667
+
668
+ rb_funcall(b->io, b->io_partial_read_method, 2, LONG2FIX(length), b->io_buffer);
669
+ size_t rl = RSTRING_LEN(b->io_buffer);
670
+
671
+ rb_str_buf_cat(string, (const char*)RSTRING_PTR(b->io_buffer), rl);
672
+ return rl;
673
+ }
674
+
675
+ size_t _msgpack_buffer_skip_from_io(msgpack_buffer_t* b, size_t length)
676
+ {
677
+ if(b->io_buffer == Qnil) {
678
+ b->io_buffer = rb_str_buf_new(0);
679
+ }
680
+
681
+ rb_funcall(b->io, b->io_partial_read_method, 2, LONG2FIX(length), b->io_buffer);
682
+ return RSTRING_LEN(b->io_buffer);
683
+ }
684
+