OLD | NEW |
| (Empty) |
1 // Protocol Buffers - Google's data interchange format | |
2 // Copyright 2014 Google Inc. All rights reserved. | |
3 // https://developers.google.com/protocol-buffers/ | |
4 // | |
5 // Redistribution and use in source and binary forms, with or without | |
6 // modification, are permitted provided that the following conditions are | |
7 // met: | |
8 // | |
9 // * Redistributions of source code must retain the above copyright | |
10 // notice, this list of conditions and the following disclaimer. | |
11 // * Redistributions in binary form must reproduce the above | |
12 // copyright notice, this list of conditions and the following disclaimer | |
13 // in the documentation and/or other materials provided with the | |
14 // distribution. | |
15 // * Neither the name of Google Inc. nor the names of its | |
16 // contributors may be used to endorse or promote products derived from | |
17 // this software without specific prior written permission. | |
18 // | |
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
30 | |
31 #include "protobuf.h" | |
32 | |
33 // ----------------------------------------------------------------------------- | |
34 // Repeated field container type. | |
35 // ----------------------------------------------------------------------------- | |
36 | |
37 const rb_data_type_t RepeatedField_type = { | |
38 "Google::Protobuf::RepeatedField", | |
39 { RepeatedField_mark, RepeatedField_free, NULL }, | |
40 }; | |
41 | |
42 VALUE cRepeatedField; | |
43 | |
44 RepeatedField* ruby_to_RepeatedField(VALUE _self) { | |
45 RepeatedField* self; | |
46 TypedData_Get_Struct(_self, RepeatedField, &RepeatedField_type, self); | |
47 return self; | |
48 } | |
49 | |
50 static int index_position(VALUE _index, RepeatedField* repeated_field) { | |
51 int index = NUM2INT(_index); | |
52 if (index < 0 && repeated_field->size > 0) { | |
53 index = repeated_field->size + index; | |
54 } | |
55 return index; | |
56 } | |
57 | |
58 VALUE RepeatedField_subarray(VALUE _self, long beg, long len) { | |
59 RepeatedField* self = ruby_to_RepeatedField(_self); | |
60 int element_size = native_slot_size(self->field_type); | |
61 upb_fieldtype_t field_type = self->field_type; | |
62 VALUE field_type_class = self->field_type_class; | |
63 | |
64 size_t off = beg * element_size; | |
65 VALUE ary = rb_ary_new2(len); | |
66 for (int i = beg; i < beg + len; i++, off += element_size) { | |
67 void* mem = ((uint8_t *)self->elements) + off; | |
68 VALUE elem = native_slot_get(field_type, field_type_class, mem); | |
69 rb_ary_push(ary, elem); | |
70 } | |
71 return ary; | |
72 } | |
73 | |
74 /* | |
75 * call-seq: | |
76 * RepeatedField.each(&block) | |
77 * | |
78 * Invokes the block once for each element of the repeated field. RepeatedField | |
79 * also includes Enumerable; combined with this method, the repeated field thus | |
80 * acts like an ordinary Ruby sequence. | |
81 */ | |
82 VALUE RepeatedField_each(VALUE _self) { | |
83 RepeatedField* self = ruby_to_RepeatedField(_self); | |
84 upb_fieldtype_t field_type = self->field_type; | |
85 VALUE field_type_class = self->field_type_class; | |
86 int element_size = native_slot_size(field_type); | |
87 | |
88 size_t off = 0; | |
89 for (int i = 0; i < self->size; i++, off += element_size) { | |
90 void* memory = (void *) (((uint8_t *)self->elements) + off); | |
91 VALUE val = native_slot_get(field_type, field_type_class, memory); | |
92 rb_yield(val); | |
93 } | |
94 return _self; | |
95 } | |
96 | |
97 | |
98 /* | |
99 * call-seq: | |
100 * RepeatedField.[](index) => value | |
101 * | |
102 * Accesses the element at the given index. Returns nil on out-of-bounds | |
103 */ | |
104 VALUE RepeatedField_index(int argc, VALUE* argv, VALUE _self) { | |
105 RepeatedField* self = ruby_to_RepeatedField(_self); | |
106 int element_size = native_slot_size(self->field_type); | |
107 upb_fieldtype_t field_type = self->field_type; | |
108 VALUE field_type_class = self->field_type_class; | |
109 | |
110 VALUE arg = argv[0]; | |
111 long beg, len; | |
112 | |
113 if (argc == 1){ | |
114 if (FIXNUM_P(arg)) { | |
115 /* standard case */ | |
116 int index = index_position(argv[0], self); | |
117 if (index < 0 || index >= self->size) { | |
118 return Qnil; | |
119 } | |
120 void* memory = (void *) (((uint8_t *)self->elements) + | |
121 index * element_size); | |
122 return native_slot_get(field_type, field_type_class, memory); | |
123 }else{ | |
124 /* check if idx is Range */ | |
125 size_t off; | |
126 switch (rb_range_beg_len(arg, &beg, &len, self->size, 0)) { | |
127 case Qfalse: | |
128 break; | |
129 case Qnil: | |
130 return Qnil; | |
131 default: | |
132 return RepeatedField_subarray(_self, beg, len); | |
133 } | |
134 } | |
135 } | |
136 /* assume 2 arguments */ | |
137 beg = NUM2LONG(argv[0]); | |
138 len = NUM2LONG(argv[1]); | |
139 if (beg < 0) { | |
140 beg += self->size; | |
141 } | |
142 if (beg >= self->size) { | |
143 return Qnil; | |
144 } | |
145 return RepeatedField_subarray(_self, beg, len); | |
146 } | |
147 | |
148 /* | |
149 * call-seq: | |
150 * RepeatedField.[]=(index, value) | |
151 * | |
152 * Sets the element at the given index. On out-of-bounds assignments, extends | |
153 * the array and fills the hole (if any) with default values. | |
154 */ | |
155 VALUE RepeatedField_index_set(VALUE _self, VALUE _index, VALUE val) { | |
156 RepeatedField* self = ruby_to_RepeatedField(_self); | |
157 upb_fieldtype_t field_type = self->field_type; | |
158 VALUE field_type_class = self->field_type_class; | |
159 int element_size = native_slot_size(field_type); | |
160 | |
161 int index = index_position(_index, self); | |
162 if (index < 0 || index >= (INT_MAX - 1)) { | |
163 return Qnil; | |
164 } | |
165 if (index >= self->size) { | |
166 RepeatedField_reserve(self, index + 1); | |
167 upb_fieldtype_t field_type = self->field_type; | |
168 int element_size = native_slot_size(field_type); | |
169 for (int i = self->size; i <= index; i++) { | |
170 void* elem = (void *)(((uint8_t *)self->elements) + i * element_size); | |
171 native_slot_init(field_type, elem); | |
172 } | |
173 self->size = index + 1; | |
174 } | |
175 | |
176 void* memory = (void *) (((uint8_t *)self->elements) + index * element_size); | |
177 native_slot_set(field_type, field_type_class, memory, val); | |
178 return Qnil; | |
179 } | |
180 | |
181 static int kInitialSize = 8; | |
182 | |
183 void RepeatedField_reserve(RepeatedField* self, int new_size) { | |
184 if (new_size <= self->capacity) { | |
185 return; | |
186 } | |
187 if (self->capacity == 0) { | |
188 self->capacity = kInitialSize; | |
189 } | |
190 while (self->capacity < new_size) { | |
191 self->capacity *= 2; | |
192 } | |
193 void* old_elems = self->elements; | |
194 int elem_size = native_slot_size(self->field_type); | |
195 self->elements = ALLOC_N(uint8_t, elem_size * self->capacity); | |
196 if (old_elems != NULL) { | |
197 memcpy(self->elements, old_elems, self->size * elem_size); | |
198 xfree(old_elems); | |
199 } | |
200 } | |
201 | |
202 /* | |
203 * call-seq: | |
204 * RepeatedField.push(value) | |
205 * | |
206 * Adds a new element to the repeated field. | |
207 */ | |
208 VALUE RepeatedField_push(VALUE _self, VALUE val) { | |
209 RepeatedField* self = ruby_to_RepeatedField(_self); | |
210 upb_fieldtype_t field_type = self->field_type; | |
211 int element_size = native_slot_size(field_type); | |
212 RepeatedField_reserve(self, self->size + 1); | |
213 int index = self->size; | |
214 void* memory = (void *) (((uint8_t *)self->elements) + index * element_size); | |
215 native_slot_set(field_type, self->field_type_class, memory, val); | |
216 // native_slot_set may raise an error; bump index only after set. | |
217 self->size++; | |
218 return _self; | |
219 } | |
220 | |
221 | |
222 // Used by parsing handlers. | |
223 void RepeatedField_push_native(VALUE _self, void* data) { | |
224 RepeatedField* self = ruby_to_RepeatedField(_self); | |
225 upb_fieldtype_t field_type = self->field_type; | |
226 int element_size = native_slot_size(field_type); | |
227 RepeatedField_reserve(self, self->size + 1); | |
228 int index = self->size; | |
229 void* memory = (void *) (((uint8_t *)self->elements) + index * element_size); | |
230 memcpy(memory, data, element_size); | |
231 self->size++; | |
232 } | |
233 | |
234 void* RepeatedField_index_native(VALUE _self, int index) { | |
235 RepeatedField* self = ruby_to_RepeatedField(_self); | |
236 upb_fieldtype_t field_type = self->field_type; | |
237 int element_size = native_slot_size(field_type); | |
238 return ((uint8_t *)self->elements) + index * element_size; | |
239 } | |
240 | |
241 /* | |
242 * Private ruby method, used by RepeatedField.pop | |
243 */ | |
244 VALUE RepeatedField_pop_one(VALUE _self) { | |
245 RepeatedField* self = ruby_to_RepeatedField(_self); | |
246 upb_fieldtype_t field_type = self->field_type; | |
247 VALUE field_type_class = self->field_type_class; | |
248 int element_size = native_slot_size(field_type); | |
249 if (self->size == 0) { | |
250 return Qnil; | |
251 } | |
252 int index = self->size - 1; | |
253 void* memory = (void *) (((uint8_t *)self->elements) + index * element_size); | |
254 VALUE ret = native_slot_get(field_type, field_type_class, memory); | |
255 self->size--; | |
256 return ret; | |
257 } | |
258 | |
259 /* | |
260 * call-seq: | |
261 * RepeatedField.replace(list) | |
262 * | |
263 * Replaces the contents of the repeated field with the given list of elements. | |
264 */ | |
265 VALUE RepeatedField_replace(VALUE _self, VALUE list) { | |
266 RepeatedField* self = ruby_to_RepeatedField(_self); | |
267 Check_Type(list, T_ARRAY); | |
268 self->size = 0; | |
269 for (int i = 0; i < RARRAY_LEN(list); i++) { | |
270 RepeatedField_push(_self, rb_ary_entry(list, i)); | |
271 } | |
272 return list; | |
273 } | |
274 | |
275 /* | |
276 * call-seq: | |
277 * RepeatedField.clear | |
278 * | |
279 * Clears (removes all elements from) this repeated field. | |
280 */ | |
281 VALUE RepeatedField_clear(VALUE _self) { | |
282 RepeatedField* self = ruby_to_RepeatedField(_self); | |
283 self->size = 0; | |
284 return _self; | |
285 } | |
286 | |
287 /* | |
288 * call-seq: | |
289 * RepeatedField.length | |
290 * | |
291 * Returns the length of this repeated field. | |
292 */ | |
293 VALUE RepeatedField_length(VALUE _self) { | |
294 RepeatedField* self = ruby_to_RepeatedField(_self); | |
295 return INT2NUM(self->size); | |
296 } | |
297 | |
298 static VALUE RepeatedField_new_this_type(VALUE _self) { | |
299 RepeatedField* self = ruby_to_RepeatedField(_self); | |
300 VALUE new_rptfield = Qnil; | |
301 VALUE element_type = fieldtype_to_ruby(self->field_type); | |
302 if (self->field_type_class != Qnil) { | |
303 new_rptfield = rb_funcall(CLASS_OF(_self), rb_intern("new"), 2, | |
304 element_type, self->field_type_class); | |
305 } else { | |
306 new_rptfield = rb_funcall(CLASS_OF(_self), rb_intern("new"), 1, | |
307 element_type); | |
308 } | |
309 return new_rptfield; | |
310 } | |
311 | |
312 /* | |
313 * call-seq: | |
314 * RepeatedField.dup => repeated_field | |
315 * | |
316 * Duplicates this repeated field with a shallow copy. References to all | |
317 * non-primitive element objects (e.g., submessages) are shared. | |
318 */ | |
319 VALUE RepeatedField_dup(VALUE _self) { | |
320 RepeatedField* self = ruby_to_RepeatedField(_self); | |
321 VALUE new_rptfield = RepeatedField_new_this_type(_self); | |
322 RepeatedField* new_rptfield_self = ruby_to_RepeatedField(new_rptfield); | |
323 RepeatedField_reserve(new_rptfield_self, self->size); | |
324 upb_fieldtype_t field_type = self->field_type; | |
325 size_t elem_size = native_slot_size(field_type); | |
326 size_t off = 0; | |
327 for (int i = 0; i < self->size; i++, off += elem_size) { | |
328 void* to_mem = (uint8_t *)new_rptfield_self->elements + off; | |
329 void* from_mem = (uint8_t *)self->elements + off; | |
330 native_slot_dup(field_type, to_mem, from_mem); | |
331 new_rptfield_self->size++; | |
332 } | |
333 | |
334 return new_rptfield; | |
335 } | |
336 | |
337 // Internal only: used by Google::Protobuf.deep_copy. | |
338 VALUE RepeatedField_deep_copy(VALUE _self) { | |
339 RepeatedField* self = ruby_to_RepeatedField(_self); | |
340 VALUE new_rptfield = RepeatedField_new_this_type(_self); | |
341 RepeatedField* new_rptfield_self = ruby_to_RepeatedField(new_rptfield); | |
342 RepeatedField_reserve(new_rptfield_self, self->size); | |
343 upb_fieldtype_t field_type = self->field_type; | |
344 size_t elem_size = native_slot_size(field_type); | |
345 size_t off = 0; | |
346 for (int i = 0; i < self->size; i++, off += elem_size) { | |
347 void* to_mem = (uint8_t *)new_rptfield_self->elements + off; | |
348 void* from_mem = (uint8_t *)self->elements + off; | |
349 native_slot_deep_copy(field_type, to_mem, from_mem); | |
350 new_rptfield_self->size++; | |
351 } | |
352 | |
353 return new_rptfield; | |
354 } | |
355 | |
356 /* | |
357 * call-seq: | |
358 * RepeatedField.to_ary => array | |
359 * | |
360 * Used when converted implicitly into array, e.g. compared to an Array. | |
361 * Also called as a fallback of Object#to_a | |
362 */ | |
363 VALUE RepeatedField_to_ary(VALUE _self) { | |
364 RepeatedField* self = ruby_to_RepeatedField(_self); | |
365 upb_fieldtype_t field_type = self->field_type; | |
366 | |
367 size_t elem_size = native_slot_size(field_type); | |
368 size_t off = 0; | |
369 VALUE ary = rb_ary_new2(self->size); | |
370 for (int i = 0; i < self->size; i++, off += elem_size) { | |
371 void* mem = ((uint8_t *)self->elements) + off; | |
372 VALUE elem = native_slot_get(field_type, self->field_type_class, mem); | |
373 rb_ary_push(ary, elem); | |
374 } | |
375 return ary; | |
376 } | |
377 | |
378 /* | |
379 * call-seq: | |
380 * RepeatedField.==(other) => boolean | |
381 * | |
382 * Compares this repeated field to another. Repeated fields are equal if their | |
383 * element types are equal, their lengths are equal, and each element is equal. | |
384 * Elements are compared as per normal Ruby semantics, by calling their :== | |
385 * methods (or performing a more efficient comparison for primitive types). | |
386 * | |
387 * Repeated fields with dissimilar element types are never equal, even if value | |
388 * comparison (for example, between integers and floats) would have otherwise | |
389 * indicated that every element has equal value. | |
390 */ | |
391 VALUE RepeatedField_eq(VALUE _self, VALUE _other) { | |
392 if (_self == _other) { | |
393 return Qtrue; | |
394 } | |
395 RepeatedField* self = ruby_to_RepeatedField(_self); | |
396 | |
397 if (TYPE(_other) == T_ARRAY) { | |
398 VALUE self_ary = RepeatedField_to_ary(_self); | |
399 return rb_equal(self_ary, _other); | |
400 } | |
401 | |
402 RepeatedField* other = ruby_to_RepeatedField(_other); | |
403 if (self->field_type != other->field_type || | |
404 self->field_type_class != other->field_type_class || | |
405 self->size != other->size) { | |
406 return Qfalse; | |
407 } | |
408 | |
409 upb_fieldtype_t field_type = self->field_type; | |
410 size_t elem_size = native_slot_size(field_type); | |
411 size_t off = 0; | |
412 for (int i = 0; i < self->size; i++, off += elem_size) { | |
413 void* self_mem = ((uint8_t *)self->elements) + off; | |
414 void* other_mem = ((uint8_t *)other->elements) + off; | |
415 if (!native_slot_eq(field_type, self_mem, other_mem)) { | |
416 return Qfalse; | |
417 } | |
418 } | |
419 return Qtrue; | |
420 } | |
421 | |
422 /* | |
423 * call-seq: | |
424 * RepeatedField.hash => hash_value | |
425 * | |
426 * Returns a hash value computed from this repeated field's elements. | |
427 */ | |
428 VALUE RepeatedField_hash(VALUE _self) { | |
429 RepeatedField* self = ruby_to_RepeatedField(_self); | |
430 | |
431 VALUE hash = LL2NUM(0); | |
432 | |
433 upb_fieldtype_t field_type = self->field_type; | |
434 VALUE field_type_class = self->field_type_class; | |
435 size_t elem_size = native_slot_size(field_type); | |
436 size_t off = 0; | |
437 for (int i = 0; i < self->size; i++, off += elem_size) { | |
438 void* mem = ((uint8_t *)self->elements) + off; | |
439 VALUE elem = native_slot_get(field_type, field_type_class, mem); | |
440 hash = rb_funcall(hash, rb_intern("<<"), 1, INT2NUM(2)); | |
441 hash = rb_funcall(hash, rb_intern("^"), 1, | |
442 rb_funcall(elem, rb_intern("hash"), 0)); | |
443 } | |
444 | |
445 return hash; | |
446 } | |
447 | |
448 /* | |
449 * call-seq: | |
450 * RepeatedField.+(other) => repeated field | |
451 * | |
452 * Returns a new repeated field that contains the concatenated list of this | |
453 * repeated field's elements and other's elements. The other (second) list may | |
454 * be either another repeated field or a Ruby array. | |
455 */ | |
456 VALUE RepeatedField_plus(VALUE _self, VALUE list) { | |
457 VALUE dupped = RepeatedField_dup(_self); | |
458 | |
459 if (TYPE(list) == T_ARRAY) { | |
460 for (int i = 0; i < RARRAY_LEN(list); i++) { | |
461 VALUE elem = rb_ary_entry(list, i); | |
462 RepeatedField_push(dupped, elem); | |
463 } | |
464 } else if (RB_TYPE_P(list, T_DATA) && RTYPEDDATA_P(list) && | |
465 RTYPEDDATA_TYPE(list) == &RepeatedField_type) { | |
466 RepeatedField* self = ruby_to_RepeatedField(_self); | |
467 RepeatedField* list_rptfield = ruby_to_RepeatedField(list); | |
468 if (self->field_type != list_rptfield->field_type || | |
469 self->field_type_class != list_rptfield->field_type_class) { | |
470 rb_raise(rb_eArgError, | |
471 "Attempt to append RepeatedField with different element type."); | |
472 } | |
473 for (int i = 0; i < list_rptfield->size; i++) { | |
474 void* mem = RepeatedField_index_native(list, i); | |
475 RepeatedField_push_native(dupped, mem); | |
476 } | |
477 } else { | |
478 rb_raise(rb_eArgError, "Unknown type appending to RepeatedField"); | |
479 } | |
480 | |
481 return dupped; | |
482 } | |
483 | |
484 /* | |
485 * call-seq: | |
486 * RepeatedField.concat(other) => self | |
487 * | |
488 * concats the passed in array to self. Returns a Ruby array. | |
489 */ | |
490 VALUE RepeatedField_concat(VALUE _self, VALUE list) { | |
491 RepeatedField* self = ruby_to_RepeatedField(_self); | |
492 Check_Type(list, T_ARRAY); | |
493 for (int i = 0; i < RARRAY_LEN(list); i++) { | |
494 RepeatedField_push(_self, rb_ary_entry(list, i)); | |
495 } | |
496 return _self; | |
497 } | |
498 | |
499 | |
500 void validate_type_class(upb_fieldtype_t type, VALUE klass) { | |
501 if (rb_ivar_get(klass, descriptor_instancevar_interned) == Qnil) { | |
502 rb_raise(rb_eArgError, | |
503 "Type class has no descriptor. Please pass a " | |
504 "class or enum as returned by the DescriptorPool."); | |
505 } | |
506 if (type == UPB_TYPE_MESSAGE) { | |
507 VALUE desc = rb_ivar_get(klass, descriptor_instancevar_interned); | |
508 if (!RB_TYPE_P(desc, T_DATA) || !RTYPEDDATA_P(desc) || | |
509 RTYPEDDATA_TYPE(desc) != &_Descriptor_type) { | |
510 rb_raise(rb_eArgError, "Descriptor has an incorrect type."); | |
511 } | |
512 if (rb_get_alloc_func(klass) != &Message_alloc) { | |
513 rb_raise(rb_eArgError, | |
514 "Message class was not returned by the DescriptorPool."); | |
515 } | |
516 } else if (type == UPB_TYPE_ENUM) { | |
517 VALUE enumdesc = rb_ivar_get(klass, descriptor_instancevar_interned); | |
518 if (!RB_TYPE_P(enumdesc, T_DATA) || !RTYPEDDATA_P(enumdesc) || | |
519 RTYPEDDATA_TYPE(enumdesc) != &_EnumDescriptor_type) { | |
520 rb_raise(rb_eArgError, "Descriptor has an incorrect type."); | |
521 } | |
522 } | |
523 } | |
524 | |
525 void RepeatedField_init_args(int argc, VALUE* argv, | |
526 VALUE _self) { | |
527 RepeatedField* self = ruby_to_RepeatedField(_self); | |
528 VALUE ary = Qnil; | |
529 if (argc < 1) { | |
530 rb_raise(rb_eArgError, "Expected at least 1 argument."); | |
531 } | |
532 self->field_type = ruby_to_fieldtype(argv[0]); | |
533 | |
534 if (self->field_type == UPB_TYPE_MESSAGE || | |
535 self->field_type == UPB_TYPE_ENUM) { | |
536 if (argc < 2) { | |
537 rb_raise(rb_eArgError, "Expected at least 2 arguments for message/enum."); | |
538 } | |
539 self->field_type_class = argv[1]; | |
540 if (argc > 2) { | |
541 ary = argv[2]; | |
542 } | |
543 validate_type_class(self->field_type, self->field_type_class); | |
544 } else { | |
545 if (argc > 2) { | |
546 rb_raise(rb_eArgError, "Too many arguments: expected 1 or 2."); | |
547 } | |
548 if (argc > 1) { | |
549 ary = argv[1]; | |
550 } | |
551 } | |
552 | |
553 if (ary != Qnil) { | |
554 if (!RB_TYPE_P(ary, T_ARRAY)) { | |
555 rb_raise(rb_eArgError, "Expected array as initialize argument"); | |
556 } | |
557 for (int i = 0; i < RARRAY_LEN(ary); i++) { | |
558 RepeatedField_push(_self, rb_ary_entry(ary, i)); | |
559 } | |
560 } | |
561 } | |
562 | |
563 // Mark, free, alloc, init and class setup functions. | |
564 | |
565 void RepeatedField_mark(void* _self) { | |
566 RepeatedField* self = (RepeatedField*)_self; | |
567 rb_gc_mark(self->field_type_class); | |
568 upb_fieldtype_t field_type = self->field_type; | |
569 int element_size = native_slot_size(field_type); | |
570 for (int i = 0; i < self->size; i++) { | |
571 void* memory = (((uint8_t *)self->elements) + i * element_size); | |
572 native_slot_mark(self->field_type, memory); | |
573 } | |
574 } | |
575 | |
576 void RepeatedField_free(void* _self) { | |
577 RepeatedField* self = (RepeatedField*)_self; | |
578 xfree(self->elements); | |
579 xfree(self); | |
580 } | |
581 | |
582 /* | |
583 * call-seq: | |
584 * RepeatedField.new(type, type_class = nil, initial_elems = []) | |
585 * | |
586 * Creates a new repeated field. The provided type must be a Ruby symbol, and | |
587 * can take on the same values as those accepted by FieldDescriptor#type=. If | |
588 * the type is :message or :enum, type_class must be non-nil, and must be the | |
589 * Ruby class or module returned by Descriptor#msgclass or | |
590 * EnumDescriptor#enummodule, respectively. An initial list of elements may also | |
591 * be provided. | |
592 */ | |
593 VALUE RepeatedField_alloc(VALUE klass) { | |
594 RepeatedField* self = ALLOC(RepeatedField); | |
595 self->elements = NULL; | |
596 self->size = 0; | |
597 self->capacity = 0; | |
598 self->field_type = -1; | |
599 self->field_type_class = Qnil; | |
600 VALUE ret = TypedData_Wrap_Struct(klass, &RepeatedField_type, self); | |
601 return ret; | |
602 } | |
603 | |
604 VALUE RepeatedField_init(int argc, VALUE* argv, VALUE self) { | |
605 RepeatedField_init_args(argc, argv, self); | |
606 return Qnil; | |
607 } | |
608 | |
609 void RepeatedField_register(VALUE module) { | |
610 VALUE klass = rb_define_class_under( | |
611 module, "RepeatedField", rb_cObject); | |
612 rb_define_alloc_func(klass, RepeatedField_alloc); | |
613 cRepeatedField = klass; | |
614 rb_gc_register_address(&cRepeatedField); | |
615 | |
616 rb_define_method(klass, "initialize", | |
617 RepeatedField_init, -1); | |
618 rb_define_method(klass, "each", RepeatedField_each, 0); | |
619 rb_define_method(klass, "[]", RepeatedField_index, -1); | |
620 rb_define_method(klass, "at", RepeatedField_index, -1); | |
621 rb_define_method(klass, "[]=", RepeatedField_index_set, 2); | |
622 rb_define_method(klass, "push", RepeatedField_push, 1); | |
623 rb_define_method(klass, "<<", RepeatedField_push, 1); | |
624 rb_define_private_method(klass, "pop_one", RepeatedField_pop_one, 0); | |
625 rb_define_method(klass, "replace", RepeatedField_replace, 1); | |
626 rb_define_method(klass, "clear", RepeatedField_clear, 0); | |
627 rb_define_method(klass, "length", RepeatedField_length, 0); | |
628 rb_define_method(klass, "size", RepeatedField_length, 0); | |
629 rb_define_method(klass, "dup", RepeatedField_dup, 0); | |
630 // Also define #clone so that we don't inherit Object#clone. | |
631 rb_define_method(klass, "clone", RepeatedField_dup, 0); | |
632 rb_define_method(klass, "==", RepeatedField_eq, 1); | |
633 rb_define_method(klass, "to_ary", RepeatedField_to_ary, 0); | |
634 rb_define_method(klass, "hash", RepeatedField_hash, 0); | |
635 rb_define_method(klass, "+", RepeatedField_plus, 1); | |
636 rb_define_method(klass, "concat", RepeatedField_concat, 1); | |
637 rb_include_module(klass, rb_mEnumerable); | |
638 } | |
OLD | NEW |