google-protobuf 4.26.1 → 4.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of google-protobuf might be problematic. Click here for more details.

Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/ext/google/protobuf_c/convert.c +39 -14
  3. data/ext/google/protobuf_c/defs.c +327 -1
  4. data/ext/google/protobuf_c/glue.c +16 -0
  5. data/ext/google/protobuf_c/map.c +72 -20
  6. data/ext/google/protobuf_c/map.h +6 -2
  7. data/ext/google/protobuf_c/message.c +78 -50
  8. data/ext/google/protobuf_c/message.h +1 -5
  9. data/ext/google/protobuf_c/protobuf.c +11 -9
  10. data/ext/google/protobuf_c/protobuf.h +3 -7
  11. data/ext/google/protobuf_c/repeated_field.c +61 -17
  12. data/ext/google/protobuf_c/repeated_field.h +5 -1
  13. data/ext/google/protobuf_c/ruby-upb.c +1693 -475
  14. data/ext/google/protobuf_c/ruby-upb.h +2526 -1386
  15. data/ext/google/protobuf_c/shared_convert.c +5 -2
  16. data/ext/google/protobuf_c/shared_message.c +0 -30
  17. data/ext/google/protobuf_c/shared_message.h +0 -4
  18. data/lib/google/protobuf/descriptor_pb.rb +2 -1
  19. data/lib/google/protobuf/ffi/descriptor.rb +4 -3
  20. data/lib/google/protobuf/ffi/descriptor_pool.rb +3 -1
  21. data/lib/google/protobuf/ffi/enum_descriptor.rb +3 -1
  22. data/lib/google/protobuf/ffi/ffi.rb +0 -5
  23. data/lib/google/protobuf/ffi/field_descriptor.rb +4 -2
  24. data/lib/google/protobuf/ffi/file_descriptor.rb +3 -1
  25. data/lib/google/protobuf/ffi/internal/arena.rb +0 -6
  26. data/lib/google/protobuf/ffi/internal/convert.rb +14 -7
  27. data/lib/google/protobuf/ffi/map.rb +45 -21
  28. data/lib/google/protobuf/ffi/message.rb +182 -60
  29. data/lib/google/protobuf/ffi/method_descriptor.rb +114 -0
  30. data/lib/google/protobuf/ffi/oneof_descriptor.rb +3 -1
  31. data/lib/google/protobuf/ffi/repeated_field.rb +42 -16
  32. data/lib/google/protobuf/ffi/service_descriptor.rb +107 -0
  33. data/lib/google/protobuf/repeated_field.rb +3 -3
  34. data/lib/google/protobuf_ffi.rb +2 -0
  35. metadata +20 -4
@@ -80,11 +80,13 @@ const upb_Message* Message_Get(VALUE msg_rb, const upb_MessageDef** m) {
80
80
  }
81
81
 
82
82
  upb_Message* Message_GetMutable(VALUE msg_rb, const upb_MessageDef** m) {
83
- rb_check_frozen(msg_rb);
84
- return (upb_Message*)Message_Get(msg_rb, m);
83
+ const upb_Message* upb_msg = Message_Get(msg_rb, m);
84
+ Protobuf_CheckNotFrozen(msg_rb, upb_Message_IsFrozen(upb_msg));
85
+ return (upb_Message*)upb_msg;
85
86
  }
86
87
 
87
- void Message_InitPtr(VALUE self_, upb_Message* msg, VALUE arena) {
88
+ void Message_InitPtr(VALUE self_, const upb_Message* msg, VALUE arena) {
89
+ PBRUBY_ASSERT(arena != Qnil);
88
90
  Message* self = ruby_to_Message(self_);
89
91
  self->msg = msg;
90
92
  RB_OBJ_WRITE(self_, &self->arena, arena);
@@ -105,7 +107,7 @@ void Message_CheckClass(VALUE klass) {
105
107
  }
106
108
  }
107
109
 
108
- VALUE Message_GetRubyWrapper(upb_Message* msg, const upb_MessageDef* m,
110
+ VALUE Message_GetRubyWrapper(const upb_Message* msg, const upb_MessageDef* m,
109
111
  VALUE arena) {
110
112
  if (msg == NULL) return Qnil;
111
113
 
@@ -116,7 +118,6 @@ VALUE Message_GetRubyWrapper(upb_Message* msg, const upb_MessageDef* m,
116
118
  val = Message_alloc(klass);
117
119
  Message_InitPtr(val, msg, arena);
118
120
  }
119
-
120
121
  return val;
121
122
  }
122
123
 
@@ -248,7 +249,7 @@ static int extract_method_call(VALUE method_name, Message* self,
248
249
  static VALUE Message_oneof_accessor(VALUE _self, const upb_OneofDef* o,
249
250
  int accessor_type) {
250
251
  Message* self = ruby_to_Message(_self);
251
- const upb_FieldDef* oneof_field = upb_Message_WhichOneof(self->msg, o);
252
+ const upb_FieldDef* oneof_field = upb_Message_WhichOneofByDef(self->msg, o);
252
253
 
253
254
  switch (accessor_type) {
254
255
  case METHOD_PRESENCE:
@@ -288,13 +289,42 @@ static void Message_setfield(upb_Message* msg, const upb_FieldDef* f, VALUE val,
288
289
  upb_Message_SetFieldByDef(msg, f, msgval, arena);
289
290
  }
290
291
 
292
+ VALUE Message_getfield_frozen(const upb_Message* msg, const upb_FieldDef* f,
293
+ VALUE arena) {
294
+ upb_MessageValue msgval = upb_Message_GetFieldByDef(msg, f);
295
+ if (upb_FieldDef_IsMap(f)) {
296
+ if (msgval.map_val == NULL) {
297
+ return Map_EmptyFrozen(f);
298
+ }
299
+ const upb_FieldDef* key_f = map_field_key(f);
300
+ const upb_FieldDef* val_f = map_field_value(f);
301
+ upb_CType key_type = upb_FieldDef_CType(key_f);
302
+ TypeInfo value_type_info = TypeInfo_get(val_f);
303
+ return Map_GetRubyWrapper(msgval.map_val, key_type, value_type_info, arena);
304
+ }
305
+ if (upb_FieldDef_IsRepeated(f)) {
306
+ if (msgval.array_val == NULL) {
307
+ return RepeatedField_EmptyFrozen(f);
308
+ }
309
+ return RepeatedField_GetRubyWrapper(msgval.array_val, TypeInfo_get(f),
310
+ arena);
311
+ }
312
+ VALUE ret;
313
+ if (upb_FieldDef_IsSubMessage(f)) {
314
+ const upb_MessageDef* m = upb_FieldDef_MessageSubDef(f);
315
+ ret = Message_GetRubyWrapper(msgval.msg_val, m, arena);
316
+ } else {
317
+ ret = Convert_UpbToRuby(msgval, TypeInfo_get(f), Qnil);
318
+ }
319
+ return ret;
320
+ }
321
+
291
322
  VALUE Message_getfield(VALUE _self, const upb_FieldDef* f) {
292
323
  Message* self = ruby_to_Message(_self);
293
- // This is a special-case: upb_Message_Mutable() for map & array are logically
294
- // const (they will not change what is serialized) but physically
295
- // non-const, as they do allocate a repeated field or map. The logical
296
- // constness means it's ok to do even if the message is frozen.
297
- upb_Message* msg = (upb_Message*)self->msg;
324
+ if (upb_Message_IsFrozen(self->msg)) {
325
+ return Message_getfield_frozen(self->msg, f, self->arena);
326
+ }
327
+ upb_Message* msg = Message_GetMutable(_self, NULL);
298
328
  upb_Arena* arena = Arena_get(self->arena);
299
329
  if (upb_FieldDef_IsMap(f)) {
300
330
  upb_Map* map = upb_Message_Mutable(msg, f, arena).map;
@@ -307,12 +337,12 @@ VALUE Message_getfield(VALUE _self, const upb_FieldDef* f) {
307
337
  upb_Array* arr = upb_Message_Mutable(msg, f, arena).array;
308
338
  return RepeatedField_GetRubyWrapper(arr, TypeInfo_get(f), self->arena);
309
339
  } else if (upb_FieldDef_IsSubMessage(f)) {
310
- if (!upb_Message_HasFieldByDef(self->msg, f)) return Qnil;
340
+ if (!upb_Message_HasFieldByDef(msg, f)) return Qnil;
311
341
  upb_Message* submsg = upb_Message_Mutable(msg, f, arena).msg;
312
342
  const upb_MessageDef* m = upb_FieldDef_MessageSubDef(f);
313
343
  return Message_GetRubyWrapper(submsg, m, self->arena);
314
344
  } else {
315
- upb_MessageValue msgval = upb_Message_GetFieldByDef(self->msg, f);
345
+ upb_MessageValue msgval = upb_Message_GetFieldByDef(msg, f);
316
346
  return Convert_UpbToRuby(msgval, TypeInfo_get(f), self->arena);
317
347
  }
318
348
  }
@@ -436,7 +466,6 @@ static VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) {
436
466
  if (argc != 2) {
437
467
  rb_raise(rb_eArgError, "Expected 2 arguments, received %d", argc);
438
468
  }
439
- rb_check_frozen(_self);
440
469
  break;
441
470
  default:
442
471
  if (argc != 1) {
@@ -669,20 +698,6 @@ static VALUE Message_dup(VALUE _self) {
669
698
  return new_msg;
670
699
  }
671
700
 
672
- // Support function for Message_eq, and also used by other #eq functions.
673
- bool Message_Equal(const upb_Message* m1, const upb_Message* m2,
674
- const upb_MessageDef* m) {
675
- upb_Status status;
676
- upb_Status_Clear(&status);
677
- bool return_value = shared_Message_Equal(m1, m2, m, &status);
678
- if (upb_Status_IsOk(&status)) {
679
- return return_value;
680
- } else {
681
- rb_raise(cParseError, "Message_Equal(): %s",
682
- upb_Status_ErrorMessage(&status));
683
- }
684
- }
685
-
686
701
  /*
687
702
  * call-seq:
688
703
  * Message.==(other) => boolean
@@ -699,7 +714,10 @@ static VALUE Message_eq(VALUE _self, VALUE _other) {
699
714
  Message* other = ruby_to_Message(_other);
700
715
  assert(self->msgdef == other->msgdef);
701
716
 
702
- return Message_Equal(self->msg, other->msg, self->msgdef) ? Qtrue : Qfalse;
717
+ const upb_MiniTable* m = upb_MessageDef_MiniTable(self->msgdef);
718
+ const int options = 0;
719
+ return upb_Message_IsEqual(self->msg, other->msg, m, options) ? Qtrue
720
+ : Qfalse;
703
721
  }
704
722
 
705
723
  uint64_t Message_Hash(const upb_Message* msg, const upb_MessageDef* m,
@@ -823,33 +841,42 @@ static VALUE Message_to_h(VALUE _self) {
823
841
 
824
842
  /*
825
843
  * call-seq:
826
- * Message.freeze => self
844
+ * Message.frozen? => bool
827
845
  *
828
- * Freezes the message object. We have to intercept this so we can pin the
829
- * Ruby object into memory so we don't forget it's frozen.
846
+ * Returns true if the message is frozen in either Ruby or the underlying
847
+ * representation. Freezes the Ruby message object if it is not already frozen
848
+ * in Ruby but it is frozen in the underlying representation.
830
849
  */
831
- VALUE Message_freeze(VALUE _self) {
850
+ VALUE Message_frozen(VALUE _self) {
832
851
  Message* self = ruby_to_Message(_self);
852
+ if (!upb_Message_IsFrozen(self->msg)) {
853
+ PBRUBY_ASSERT(!RB_OBJ_FROZEN(_self));
854
+ return Qfalse;
855
+ }
833
856
 
834
- if (RB_OBJ_FROZEN(_self)) return _self;
835
- Arena_Pin(self->arena, _self);
836
- RB_OBJ_FREEZE(_self);
857
+ // Lazily freeze the Ruby wrapper.
858
+ if (!RB_OBJ_FROZEN(_self)) RB_OBJ_FREEZE(_self);
859
+ return Qtrue;
860
+ }
837
861
 
838
- int n = upb_MessageDef_FieldCount(self->msgdef);
839
- for (int i = 0; i < n; i++) {
840
- const upb_FieldDef* f = upb_MessageDef_Field(self->msgdef, i);
841
- VALUE field = Message_getfield(_self, f);
842
-
843
- if (field != Qnil) {
844
- if (upb_FieldDef_IsMap(f)) {
845
- Map_freeze(field);
846
- } else if (upb_FieldDef_IsRepeated(f)) {
847
- RepeatedField_freeze(field);
848
- } else if (upb_FieldDef_IsSubMessage(f)) {
849
- Message_freeze(field);
850
- }
851
- }
862
+ /*
863
+ * call-seq:
864
+ * Message.freeze => self
865
+ *
866
+ * Freezes the message object. We have to intercept this so we can freeze the
867
+ * underlying representation, not just the Ruby wrapper.
868
+ */
869
+ VALUE Message_freeze(VALUE _self) {
870
+ Message* self = ruby_to_Message(_self);
871
+ if (RB_OBJ_FROZEN(_self)) {
872
+ PBRUBY_ASSERT(upb_Message_IsFrozen(self->msg));
873
+ return _self;
852
874
  }
875
+ if (!upb_Message_IsFrozen(self->msg)) {
876
+ upb_Message_Freeze(Message_GetMutable(_self, NULL),
877
+ upb_MessageDef_MiniTable(self->msgdef));
878
+ }
879
+ RB_OBJ_FREEZE(_self);
853
880
  return _self;
854
881
  }
855
882
 
@@ -1363,6 +1390,7 @@ static void Message_define_class(VALUE klass) {
1363
1390
  rb_define_method(klass, "==", Message_eq, 1);
1364
1391
  rb_define_method(klass, "eql?", Message_eq, 1);
1365
1392
  rb_define_method(klass, "freeze", Message_freeze, 0);
1393
+ rb_define_method(klass, "frozen?", Message_frozen, 0);
1366
1394
  rb_define_method(klass, "hash", Message_hash, 0);
1367
1395
  rb_define_method(klass, "to_h", Message_to_h, 0);
1368
1396
  rb_define_method(klass, "inspect", Message_inspect, 0);
@@ -36,7 +36,7 @@ const upb_Message* Message_GetUpbMessage(VALUE value, const upb_MessageDef* m,
36
36
 
37
37
  // Gets or constructs a Ruby wrapper object for the given message. The wrapper
38
38
  // object will reference |arena| and ensure that it outlives this object.
39
- VALUE Message_GetRubyWrapper(upb_Message* msg, const upb_MessageDef* m,
39
+ VALUE Message_GetRubyWrapper(const upb_Message* msg, const upb_MessageDef* m,
40
40
  VALUE arena);
41
41
 
42
42
  // Gets the given field from this message.
@@ -54,10 +54,6 @@ uint64_t Message_Hash(const upb_Message* msg, const upb_MessageDef* m,
54
54
  upb_Message* Message_deep_copy(const upb_Message* msg, const upb_MessageDef* m,
55
55
  upb_Arena* arena);
56
56
 
57
- // Returns true if these two messages are equal.
58
- bool Message_Equal(const upb_Message* m1, const upb_Message* m2,
59
- const upb_MessageDef* m);
60
-
61
57
  // Checks that this Ruby object is a message, and raises an exception if not.
62
58
  void Message_CheckClass(VALUE klass);
63
59
 
@@ -221,15 +221,6 @@ void Arena_fuse(VALUE _arena, upb_Arena *other) {
221
221
 
222
222
  VALUE Arena_new() { return Arena_alloc(cArena); }
223
223
 
224
- void Arena_Pin(VALUE _arena, VALUE obj) {
225
- Arena *arena;
226
- TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
227
- if (arena->pinned_objs == Qnil) {
228
- RB_OBJ_WRITE(_arena, &arena->pinned_objs, rb_ary_new());
229
- }
230
- rb_ary_push(arena->pinned_objs, obj);
231
- }
232
-
233
224
  void Arena_register(VALUE module) {
234
225
  VALUE internal = rb_define_module_under(module, "Internal");
235
226
  VALUE klass = rb_define_class_under(internal, "Arena", rb_cObject);
@@ -354,3 +345,14 @@ __attribute__((visibility("default"))) void Init_protobuf_c() {
354
345
  rb_define_singleton_method(protobuf, "deep_copy", Google_Protobuf_deep_copy,
355
346
  1);
356
347
  }
348
+
349
+ // -----------------------------------------------------------------------------
350
+ // Utilities
351
+ // -----------------------------------------------------------------------------
352
+
353
+ // Raises a Ruby error if val is frozen in Ruby or UPB.
354
+ void Protobuf_CheckNotFrozen(VALUE val, bool upb_frozen) {
355
+ if (RB_UNLIKELY(rb_obj_frozen_p(val)||upb_frozen)) {
356
+ rb_error_frozen_object(val);
357
+ }
358
+ }
@@ -50,13 +50,6 @@ upb_Arena* Arena_get(VALUE arena);
50
50
  // possible.
51
51
  void Arena_fuse(VALUE arena, upb_Arena* other);
52
52
 
53
- // Pins this Ruby object to the lifetime of this arena, so that as long as the
54
- // arena is alive this object will not be collected.
55
- //
56
- // We use this to guarantee that the "frozen" bit on the object will be
57
- // remembered, even if the user drops their reference to this precise object.
58
- void Arena_Pin(VALUE arena, VALUE obj);
59
-
60
53
  // -----------------------------------------------------------------------------
61
54
  // ObjectCache
62
55
  // -----------------------------------------------------------------------------
@@ -105,6 +98,9 @@ extern VALUE cTypeError;
105
98
  rb_bug("Assertion failed at %s:%d, expr: %s", __FILE__, __LINE__, #expr)
106
99
  #endif
107
100
 
101
+ // Raises a Ruby error if val is frozen in Ruby or upb_frozen is true.
102
+ void Protobuf_CheckNotFrozen(VALUE val, bool upb_frozen);
103
+
108
104
  #define PBRUBY_MAX(x, y) (((x) > (y)) ? (x) : (y))
109
105
 
110
106
  #define UPB_UNUSED(var) (void)var
@@ -44,8 +44,9 @@ static RepeatedField* ruby_to_RepeatedField(VALUE _self) {
44
44
  }
45
45
 
46
46
  static upb_Array* RepeatedField_GetMutable(VALUE _self) {
47
- rb_check_frozen(_self);
48
- return (upb_Array*)ruby_to_RepeatedField(_self)->array;
47
+ const upb_Array* array = ruby_to_RepeatedField(_self)->array;
48
+ Protobuf_CheckNotFrozen(_self, upb_Array_IsFrozen(array));
49
+ return (upb_Array*)array;
49
50
  }
50
51
 
51
52
  VALUE RepeatedField_alloc(VALUE klass) {
@@ -56,9 +57,32 @@ VALUE RepeatedField_alloc(VALUE klass) {
56
57
  return TypedData_Wrap_Struct(klass, &RepeatedField_type, self);
57
58
  }
58
59
 
59
- VALUE RepeatedField_GetRubyWrapper(upb_Array* array, TypeInfo type_info,
60
+ VALUE RepeatedField_EmptyFrozen(const upb_FieldDef* f) {
61
+ PBRUBY_ASSERT(upb_FieldDef_IsRepeated(f));
62
+ VALUE val = ObjectCache_Get(f);
63
+
64
+ if (val == Qnil) {
65
+ val = RepeatedField_alloc(cRepeatedField);
66
+ RepeatedField* self;
67
+ TypedData_Get_Struct(val, RepeatedField, &RepeatedField_type, self);
68
+ self->arena = Arena_new();
69
+ TypeInfo type_info = TypeInfo_get(f);
70
+ self->array = upb_Array_New(Arena_get(self->arena), type_info.type);
71
+ self->type_info = type_info;
72
+ if (self->type_info.type == kUpb_CType_Message) {
73
+ self->type_class = Descriptor_DefToClass(type_info.def.msgdef);
74
+ }
75
+ val = ObjectCache_TryAdd(f, RepeatedField_freeze(val));
76
+ }
77
+ PBRUBY_ASSERT(RB_OBJ_FROZEN(val));
78
+ PBRUBY_ASSERT(upb_Array_IsFrozen(ruby_to_RepeatedField(val)->array));
79
+ return val;
80
+ }
81
+
82
+ VALUE RepeatedField_GetRubyWrapper(const upb_Array* array, TypeInfo type_info,
60
83
  VALUE arena) {
61
84
  PBRUBY_ASSERT(array);
85
+ PBRUBY_ASSERT(arena != Qnil);
62
86
  VALUE val = ObjectCache_Get(array);
63
87
 
64
88
  if (val == Qnil) {
@@ -78,7 +102,6 @@ VALUE RepeatedField_GetRubyWrapper(upb_Array* array, TypeInfo type_info,
78
102
  PBRUBY_ASSERT(ruby_to_RepeatedField(val)->type_info.def.msgdef ==
79
103
  type_info.def.msgdef);
80
104
  PBRUBY_ASSERT(ruby_to_RepeatedField(val)->array == array);
81
-
82
105
  return val;
83
106
  }
84
107
 
@@ -471,29 +494,49 @@ VALUE RepeatedField_eq(VALUE _self, VALUE _other) {
471
494
  return Qtrue;
472
495
  }
473
496
 
497
+ /*
498
+ * call-seq:
499
+ * RepeatedField.frozen? => bool
500
+ *
501
+ * Returns true if the repeated field is frozen in either Ruby or the underlying
502
+ * representation. Freezes the Ruby repeated field object if it is not already
503
+ * frozen in Ruby but it is frozen in the underlying representation.
504
+ */
505
+ VALUE RepeatedField_frozen(VALUE _self) {
506
+ RepeatedField* self = ruby_to_RepeatedField(_self);
507
+ if (!upb_Array_IsFrozen(self->array)) {
508
+ PBRUBY_ASSERT(!RB_OBJ_FROZEN(_self));
509
+ return Qfalse;
510
+ }
511
+
512
+ // Lazily freeze the Ruby wrapper.
513
+ if (!RB_OBJ_FROZEN(_self)) RB_OBJ_FREEZE(_self);
514
+ return Qtrue;
515
+ }
516
+
474
517
  /*
475
518
  * call-seq:
476
519
  * RepeatedField.freeze => self
477
520
  *
478
- * Freezes the repeated field. We have to intercept this so we can pin the Ruby
479
- * object into memory so we don't forget it's frozen.
521
+ * Freezes the repeated field object. We have to intercept this so we can freeze
522
+ * the underlying representation, not just the Ruby wrapper.
480
523
  */
481
524
  VALUE RepeatedField_freeze(VALUE _self) {
482
525
  RepeatedField* self = ruby_to_RepeatedField(_self);
526
+ if (RB_OBJ_FROZEN(_self)) {
527
+ PBRUBY_ASSERT(upb_Array_IsFrozen(self->array));
528
+ return _self;
529
+ }
483
530
 
484
- if (RB_OBJ_FROZEN(_self)) return _self;
485
- Arena_Pin(self->arena, _self);
486
- RB_OBJ_FREEZE(_self);
487
-
488
- if (self->type_info.type == kUpb_CType_Message) {
489
- int size = upb_Array_Size(self->array);
490
- int i;
491
- for (i = 0; i < size; i++) {
492
- upb_MessageValue msgval = upb_Array_Get(self->array, i);
493
- VALUE val = Convert_UpbToRuby(msgval, self->type_info, self->arena);
494
- Message_freeze(val);
531
+ if (!upb_Array_IsFrozen(self->array)) {
532
+ if (self->type_info.type == kUpb_CType_Message) {
533
+ upb_Array_Freeze(RepeatedField_GetMutable(_self),
534
+ upb_MessageDef_MiniTable(self->type_info.def.msgdef));
535
+ } else {
536
+ upb_Array_Freeze(RepeatedField_GetMutable(_self), NULL);
495
537
  }
496
538
  }
539
+ RB_OBJ_FREEZE(_self);
497
540
  return _self;
498
541
  }
499
542
 
@@ -640,6 +683,7 @@ void RepeatedField_register(VALUE module) {
640
683
  rb_define_method(klass, "==", RepeatedField_eq, 1);
641
684
  rb_define_method(klass, "to_ary", RepeatedField_to_ary, 0);
642
685
  rb_define_method(klass, "freeze", RepeatedField_freeze, 0);
686
+ rb_define_method(klass, "frozen?", RepeatedField_frozen, 0);
643
687
  rb_define_method(klass, "hash", RepeatedField_hash, 0);
644
688
  rb_define_method(klass, "+", RepeatedField_plus, 1);
645
689
  rb_define_method(klass, "concat", RepeatedField_concat, 1);
@@ -11,9 +11,13 @@
11
11
  #include "protobuf.h"
12
12
  #include "ruby-upb.h"
13
13
 
14
+ // Returns a frozen sentinel Ruby wrapper object for an empty upb_Array of the
15
+ // type specified by the field. Creates one if it doesn't exist.
16
+ VALUE RepeatedField_EmptyFrozen(const upb_FieldDef* f);
17
+
14
18
  // Returns a Ruby wrapper object for the given upb_Array, which will be created
15
19
  // if one does not exist already.
16
- VALUE RepeatedField_GetRubyWrapper(upb_Array* msg, TypeInfo type_info,
20
+ VALUE RepeatedField_GetRubyWrapper(const upb_Array* msg, TypeInfo type_info,
17
21
  VALUE arena);
18
22
 
19
23
  // Gets the underlying upb_Array for this Ruby RepeatedField object, which must