| 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 |