google-protobuf 3.22.5 → 3.24.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed447d83d06d36c581e489caa90787e14eb0ef197e9a1f8019f1d8e39ab3930e
4
- data.tar.gz: 4ccbc995a7bc56cfd96d171d50fd81eac19b20f0fb10969cae0d59c5a424716b
3
+ metadata.gz: 802cae742cd0aa258920ff9bde883ab655092d76cde65b4b047dd60a5a6ebd24
4
+ data.tar.gz: 55ca2a83f2a774be049602510b19b1bd53fcc7a5b10988c8a5020cf38aa354c3
5
5
  SHA512:
6
- metadata.gz: 136713eb6ec3326893e85b49861f5d399c137b222e7eaee77f028dc9195f3e9850f0a591735ffe2c01af0ee6f3213992e83f7a10ddeb0eb086051b748b57bba0
7
- data.tar.gz: 118a5c4657e91b3276ea2f8ca5c42526df71a49861e130dac13901019f9ed2391f3a1c2f7da150897d72e63bf56a0af1d4e75c264e53ba0dd9278955db944249
6
+ metadata.gz: fd69b0c91d8bf5597c5ec98b77b597af7182386d99d78f4a84b4fde0c352a930005ad8bbba83650b5d54069cbb5503be4b302622229edee04d7e2fe8ee790804
7
+ data.tar.gz: 74c4c1057144c20b2e5efc49f665fd066e7507a32b77222154b3a4bd1dc7c6c1439a12398363a95be45244cc59461b651563c456c1c3916a51ee506de01d447f
@@ -31,8 +31,6 @@
31
31
  #ifndef RUBY_PROTOBUF_CONVERT_H_
32
32
  #define RUBY_PROTOBUF_CONVERT_H_
33
33
 
34
- #include <ruby/ruby.h>
35
-
36
34
  #include "protobuf.h"
37
35
  #include "ruby-upb.h"
38
36
 
@@ -73,6 +73,8 @@ static VALUE rb_str_maybe_null(const char* s) {
73
73
  // -----------------------------------------------------------------------------
74
74
 
75
75
  typedef struct {
76
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
77
+ // macro to update VALUE references, as to trigger write barriers.
76
78
  VALUE def_to_descriptor; // Hash table of def* -> Ruby descriptor.
77
79
  upb_DefPool* symtab;
78
80
  } DescriptorPool;
@@ -97,7 +99,7 @@ static void DescriptorPool_free(void* _self) {
97
99
  static const rb_data_type_t DescriptorPool_type = {
98
100
  "Google::Protobuf::DescriptorPool",
99
101
  {DescriptorPool_mark, DescriptorPool_free, NULL},
100
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
102
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
101
103
  };
102
104
 
103
105
  static DescriptorPool* ruby_to_DescriptorPool(VALUE val) {
@@ -125,11 +127,9 @@ static VALUE DescriptorPool_alloc(VALUE klass) {
125
127
  self->def_to_descriptor = Qnil;
126
128
  ret = TypedData_Wrap_Struct(klass, &DescriptorPool_type, self);
127
129
 
128
- self->def_to_descriptor = rb_hash_new();
130
+ RB_OBJ_WRITE(ret, &self->def_to_descriptor, rb_hash_new());
129
131
  self->symtab = upb_DefPool_New();
130
- ObjectCache_Add(self->symtab, ret);
131
-
132
- return ret;
132
+ return ObjectCache_TryAdd(self->symtab, ret);
133
133
  }
134
134
 
135
135
  /*
@@ -223,6 +223,8 @@ static void DescriptorPool_register(VALUE module) {
223
223
 
224
224
  typedef struct {
225
225
  const upb_MessageDef* msgdef;
226
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
227
+ // macro to update VALUE references, as to trigger write barriers.
226
228
  VALUE klass;
227
229
  VALUE descriptor_pool;
228
230
  } Descriptor;
@@ -238,7 +240,7 @@ static void Descriptor_mark(void* _self) {
238
240
  static const rb_data_type_t Descriptor_type = {
239
241
  "Google::Protobuf::Descriptor",
240
242
  {Descriptor_mark, RUBY_DEFAULT_FREE, NULL},
241
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
243
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
242
244
  };
243
245
 
244
246
  static Descriptor* ruby_to_Descriptor(VALUE val) {
@@ -280,7 +282,7 @@ static VALUE Descriptor_initialize(VALUE _self, VALUE cookie,
280
282
  "Descriptor objects may not be created from Ruby.");
281
283
  }
282
284
 
283
- self->descriptor_pool = descriptor_pool;
285
+ RB_OBJ_WRITE(_self, &self->descriptor_pool, descriptor_pool);
284
286
  self->msgdef = (const upb_MessageDef*)NUM2ULL(ptr);
285
287
 
286
288
  return Qnil;
@@ -390,7 +392,7 @@ static VALUE Descriptor_lookup_oneof(VALUE _self, VALUE name) {
390
392
  static VALUE Descriptor_msgclass(VALUE _self) {
391
393
  Descriptor* self = ruby_to_Descriptor(_self);
392
394
  if (self->klass == Qnil) {
393
- self->klass = build_class_from_descriptor(_self);
395
+ RB_OBJ_WRITE(_self, &self->klass, build_class_from_descriptor(_self));
394
396
  }
395
397
  return self->klass;
396
398
  }
@@ -417,6 +419,8 @@ static void Descriptor_register(VALUE module) {
417
419
 
418
420
  typedef struct {
419
421
  const upb_FileDef* filedef;
422
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
423
+ // macro to update VALUE references, as to trigger write barriers.
420
424
  VALUE descriptor_pool; // Owns the upb_FileDef.
421
425
  } FileDescriptor;
422
426
 
@@ -430,7 +434,7 @@ static void FileDescriptor_mark(void* _self) {
430
434
  static const rb_data_type_t FileDescriptor_type = {
431
435
  "Google::Protobuf::FileDescriptor",
432
436
  {FileDescriptor_mark, RUBY_DEFAULT_FREE, NULL},
433
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
437
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
434
438
  };
435
439
 
436
440
  static FileDescriptor* ruby_to_FileDescriptor(VALUE val) {
@@ -463,7 +467,7 @@ static VALUE FileDescriptor_initialize(VALUE _self, VALUE cookie,
463
467
  "Descriptor objects may not be created from Ruby.");
464
468
  }
465
469
 
466
- self->descriptor_pool = descriptor_pool;
470
+ RB_OBJ_WRITE(_self, &self->descriptor_pool, descriptor_pool);
467
471
  self->filedef = (const upb_FileDef*)NUM2ULL(ptr);
468
472
 
469
473
  return Qnil;
@@ -519,6 +523,8 @@ static void FileDescriptor_register(VALUE module) {
519
523
 
520
524
  typedef struct {
521
525
  const upb_FieldDef* fielddef;
526
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
527
+ // macro to update VALUE references, as to trigger write barriers.
522
528
  VALUE descriptor_pool; // Owns the upb_FieldDef.
523
529
  } FieldDescriptor;
524
530
 
@@ -532,7 +538,7 @@ static void FieldDescriptor_mark(void* _self) {
532
538
  static const rb_data_type_t FieldDescriptor_type = {
533
539
  "Google::Protobuf::FieldDescriptor",
534
540
  {FieldDescriptor_mark, RUBY_DEFAULT_FREE, NULL},
535
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
541
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
536
542
  };
537
543
 
538
544
  static FieldDescriptor* ruby_to_FieldDescriptor(VALUE val) {
@@ -570,7 +576,7 @@ static VALUE FieldDescriptor_initialize(VALUE _self, VALUE cookie,
570
576
  "Descriptor objects may not be created from Ruby.");
571
577
  }
572
578
 
573
- self->descriptor_pool = descriptor_pool;
579
+ RB_OBJ_WRITE(_self, &self->descriptor_pool, descriptor_pool);
574
580
  self->fielddef = (const upb_FieldDef*)NUM2ULL(ptr);
575
581
 
576
582
  return Qnil;
@@ -595,7 +601,7 @@ upb_CType ruby_to_fieldtype(VALUE type) {
595
601
 
596
602
  #define CONVERT(upb, ruby) \
597
603
  if (SYM2ID(type) == rb_intern(#ruby)) { \
598
- return kUpb_CType_##upb; \
604
+ return kUpb_CType_##upb; \
599
605
  }
600
606
 
601
607
  CONVERT(Float, float);
@@ -618,7 +624,7 @@ upb_CType ruby_to_fieldtype(VALUE type) {
618
624
 
619
625
  static VALUE descriptortype_to_ruby(upb_FieldType type) {
620
626
  switch (type) {
621
- #define CONVERT(upb, ruby) \
627
+ #define CONVERT(upb, ruby) \
622
628
  case kUpb_FieldType_##upb: \
623
629
  return ID2SYM(rb_intern(#ruby));
624
630
  CONVERT(Float, float);
@@ -703,7 +709,7 @@ static VALUE FieldDescriptor_label(VALUE _self) {
703
709
  FieldDescriptor* self = ruby_to_FieldDescriptor(_self);
704
710
  switch (upb_FieldDef_Label(self->fielddef)) {
705
711
  #define CONVERT(upb, ruby) \
706
- case kUpb_Label_##upb: \
712
+ case kUpb_Label_##upb: \
707
713
  return ID2SYM(rb_intern(#ruby));
708
714
 
709
715
  CONVERT(Optional, optional);
@@ -884,6 +890,8 @@ static void FieldDescriptor_register(VALUE module) {
884
890
 
885
891
  typedef struct {
886
892
  const upb_OneofDef* oneofdef;
893
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
894
+ // macro to update VALUE references, as to trigger write barriers.
887
895
  VALUE descriptor_pool; // Owns the upb_OneofDef.
888
896
  } OneofDescriptor;
889
897
 
@@ -897,7 +905,7 @@ static void OneofDescriptor_mark(void* _self) {
897
905
  static const rb_data_type_t OneofDescriptor_type = {
898
906
  "Google::Protobuf::OneofDescriptor",
899
907
  {OneofDescriptor_mark, RUBY_DEFAULT_FREE, NULL},
900
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
908
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
901
909
  };
902
910
 
903
911
  static OneofDescriptor* ruby_to_OneofDescriptor(VALUE val) {
@@ -936,7 +944,7 @@ static VALUE OneofDescriptor_initialize(VALUE _self, VALUE cookie,
936
944
  "Descriptor objects may not be created from Ruby.");
937
945
  }
938
946
 
939
- self->descriptor_pool = descriptor_pool;
947
+ RB_OBJ_WRITE(_self, &self->descriptor_pool, descriptor_pool);
940
948
  self->oneofdef = (const upb_OneofDef*)NUM2ULL(ptr);
941
949
 
942
950
  return Qnil;
@@ -988,6 +996,8 @@ static void OneofDescriptor_register(VALUE module) {
988
996
 
989
997
  typedef struct {
990
998
  const upb_EnumDef* enumdef;
999
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
1000
+ // macro to update VALUE references, as to trigger write barriers.
991
1001
  VALUE module; // begins as nil
992
1002
  VALUE descriptor_pool; // Owns the upb_EnumDef.
993
1003
  } EnumDescriptor;
@@ -1003,7 +1013,7 @@ static void EnumDescriptor_mark(void* _self) {
1003
1013
  static const rb_data_type_t EnumDescriptor_type = {
1004
1014
  "Google::Protobuf::EnumDescriptor",
1005
1015
  {EnumDescriptor_mark, RUBY_DEFAULT_FREE, NULL},
1006
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
1016
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
1007
1017
  };
1008
1018
 
1009
1019
  static EnumDescriptor* ruby_to_EnumDescriptor(VALUE val) {
@@ -1042,7 +1052,7 @@ static VALUE EnumDescriptor_initialize(VALUE _self, VALUE cookie,
1042
1052
  "Descriptor objects may not be created from Ruby.");
1043
1053
  }
1044
1054
 
1045
- self->descriptor_pool = descriptor_pool;
1055
+ RB_OBJ_WRITE(_self, &self->descriptor_pool, descriptor_pool);
1046
1056
  self->enumdef = (const upb_EnumDef*)NUM2ULL(ptr);
1047
1057
 
1048
1058
  return Qnil;
@@ -1081,7 +1091,7 @@ static VALUE EnumDescriptor_name(VALUE _self) {
1081
1091
  static VALUE EnumDescriptor_lookup_name(VALUE _self, VALUE name) {
1082
1092
  EnumDescriptor* self = ruby_to_EnumDescriptor(_self);
1083
1093
  const char* name_str = rb_id2name(SYM2ID(name));
1084
- const upb_EnumValueDef *ev =
1094
+ const upb_EnumValueDef* ev =
1085
1095
  upb_EnumDef_FindValueByName(self->enumdef, name_str);
1086
1096
  if (ev) {
1087
1097
  return INT2NUM(upb_EnumValueDef_Number(ev));
@@ -1100,7 +1110,8 @@ static VALUE EnumDescriptor_lookup_name(VALUE _self, VALUE name) {
1100
1110
  static VALUE EnumDescriptor_lookup_value(VALUE _self, VALUE number) {
1101
1111
  EnumDescriptor* self = ruby_to_EnumDescriptor(_self);
1102
1112
  int32_t val = NUM2INT(number);
1103
- const upb_EnumValueDef* ev = upb_EnumDef_FindValueByNumber(self->enumdef, val);
1113
+ const upb_EnumValueDef* ev =
1114
+ upb_EnumDef_FindValueByNumber(self->enumdef, val);
1104
1115
  if (ev) {
1105
1116
  return ID2SYM(rb_intern(upb_EnumValueDef_Name(ev)));
1106
1117
  } else {
@@ -1138,7 +1149,7 @@ static VALUE EnumDescriptor_each(VALUE _self) {
1138
1149
  static VALUE EnumDescriptor_enummodule(VALUE _self) {
1139
1150
  EnumDescriptor* self = ruby_to_EnumDescriptor(_self);
1140
1151
  if (self->module == Qnil) {
1141
- self->module = build_module_from_enumdesc(_self);
1152
+ RB_OBJ_WRITE(_self, &self->module, build_module_from_enumdesc(_self));
1142
1153
  }
1143
1154
  return self->module;
1144
1155
  }
@@ -31,8 +31,6 @@
31
31
  #ifndef RUBY_PROTOBUF_DEFS_H_
32
32
  #define RUBY_PROTOBUF_DEFS_H_
33
33
 
34
- #include <ruby/ruby.h>
35
-
36
34
  #include "protobuf.h"
37
35
  #include "ruby-upb.h"
38
36
 
@@ -93,7 +93,6 @@ VALUE Map_GetRubyWrapper(upb_Map* map, upb_CType key_type, TypeInfo value_type,
93
93
  if (val == Qnil) {
94
94
  val = Map_alloc(cMap);
95
95
  Map* self;
96
- ObjectCache_Add(map, val);
97
96
  TypedData_Get_Struct(val, Map, &Map_type, self);
98
97
  self->map = map;
99
98
  self->arena = arena;
@@ -103,6 +102,7 @@ VALUE Map_GetRubyWrapper(upb_Map* map, upb_CType key_type, TypeInfo value_type,
103
102
  const upb_MessageDef* val_m = self->value_type_info.def.msgdef;
104
103
  self->value_type_class = Descriptor_DefToClass(val_m);
105
104
  }
105
+ return ObjectCache_TryAdd(map, val);
106
106
  }
107
107
 
108
108
  return val;
@@ -319,7 +319,9 @@ static VALUE Map_init(int argc, VALUE* argv, VALUE _self) {
319
319
 
320
320
  self->map = upb_Map_New(Arena_get(self->arena), self->key_type,
321
321
  self->value_type_info.type);
322
- ObjectCache_Add(self->map, _self);
322
+ VALUE stored = ObjectCache_TryAdd(self->map, _self);
323
+ (void)stored;
324
+ PBRUBY_ASSERT(stored == _self);
323
325
 
324
326
  if (init_arg != Qnil) {
325
327
  Map_merge_into_self(_self, init_arg);
@@ -31,8 +31,6 @@
31
31
  #ifndef RUBY_PROTOBUF_MAP_H_
32
32
  #define RUBY_PROTOBUF_MAP_H_
33
33
 
34
- #include <ruby/ruby.h>
35
-
36
34
  #include "protobuf.h"
37
35
  #include "ruby-upb.h"
38
36
 
@@ -53,6 +53,8 @@ VALUE MessageOrEnum_GetDescriptor(VALUE klass) {
53
53
  // -----------------------------------------------------------------------------
54
54
 
55
55
  typedef struct {
56
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
57
+ // macro to update VALUE references, as to trigger write barriers.
56
58
  VALUE arena;
57
59
  const upb_Message* msg; // Can get as mutable when non-frozen.
58
60
  const upb_MessageDef*
@@ -65,9 +67,9 @@ static void Message_mark(void* _self) {
65
67
  }
66
68
 
67
69
  static rb_data_type_t Message_type = {
68
- "Message",
70
+ "Google::Protobuf::Message",
69
71
  {Message_mark, RUBY_DEFAULT_FREE, NULL},
70
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
72
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
71
73
  };
72
74
 
73
75
  static Message* ruby_to_Message(VALUE msg_rb) {
@@ -105,8 +107,10 @@ upb_Message* Message_GetMutable(VALUE msg_rb, const upb_MessageDef** m) {
105
107
  void Message_InitPtr(VALUE self_, upb_Message* msg, VALUE arena) {
106
108
  Message* self = ruby_to_Message(self_);
107
109
  self->msg = msg;
108
- self->arena = arena;
109
- ObjectCache_Add(msg, self_);
110
+ RB_OBJ_WRITE(self_, &self->arena, arena);
111
+ VALUE stored = ObjectCache_TryAdd(msg, self_);
112
+ (void)stored;
113
+ PBRUBY_ASSERT(stored == self_);
110
114
  }
111
115
 
112
116
  VALUE Message_GetArena(VALUE msg_rb) {
@@ -978,7 +982,7 @@ static VALUE Message_decode(int argc, VALUE* argv, VALUE klass) {
978
982
  rb_hash_lookup(hash_args, ID2SYM(rb_intern("recursion_limit")));
979
983
 
980
984
  if (depth != Qnil && TYPE(depth) == T_FIXNUM) {
981
- options |= UPB_DECODE_MAXDEPTH(FIX2INT(depth));
985
+ options |= upb_DecodeOptions_MaxDepth(FIX2INT(depth));
982
986
  }
983
987
  }
984
988
 
@@ -1096,7 +1100,7 @@ static VALUE Message_encode(int argc, VALUE* argv, VALUE klass) {
1096
1100
  rb_hash_lookup(hash_args, ID2SYM(rb_intern("recursion_limit")));
1097
1101
 
1098
1102
  if (depth != Qnil && TYPE(depth) == T_FIXNUM) {
1099
- options |= UPB_DECODE_MAXDEPTH(FIX2INT(depth));
1103
+ options |= upb_DecodeOptions_MaxDepth(FIX2INT(depth));
1100
1104
  }
1101
1105
  }
1102
1106
 
@@ -1162,6 +1166,12 @@ static VALUE Message_encode_json(int argc, VALUE* argv, VALUE klass) {
1162
1166
  Qfalse))) {
1163
1167
  options |= upb_JsonEncode_EmitDefaults;
1164
1168
  }
1169
+
1170
+ if (RTEST(rb_hash_lookup2(hash_args,
1171
+ ID2SYM(rb_intern("format_enums_as_integers")),
1172
+ Qfalse))) {
1173
+ options |= upb_JsonEncode_FormatEnumsAsIntegers;
1174
+ }
1165
1175
  }
1166
1176
 
1167
1177
  upb_Status_Clear(&status);
@@ -31,8 +31,6 @@
31
31
  #ifndef RUBY_PROTOBUF_MESSAGE_H_
32
32
  #define RUBY_PROTOBUF_MESSAGE_H_
33
33
 
34
- #include <ruby/ruby.h>
35
-
36
34
  #include "protobuf.h"
37
35
  #include "ruby-upb.h"
38
36
 
@@ -171,6 +171,8 @@ void StringBuilder_PrintMsgval(StringBuilder *b, upb_MessageValue val,
171
171
 
172
172
  typedef struct {
173
173
  upb_Arena *arena;
174
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
175
+ // macro to update VALUE references, as to trigger write barriers.
174
176
  VALUE pinned_objs;
175
177
  } Arena;
176
178
 
@@ -190,10 +192,11 @@ static VALUE cArena;
190
192
  const rb_data_type_t Arena_type = {
191
193
  "Google::Protobuf::Internal::Arena",
192
194
  {Arena_mark, Arena_free, NULL},
193
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
195
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
194
196
  };
195
197
 
196
- static void* ruby_upb_allocfunc(upb_alloc* alloc, void* ptr, size_t oldsize, size_t size) {
198
+ static void *ruby_upb_allocfunc(upb_alloc *alloc, void *ptr, size_t oldsize,
199
+ size_t size) {
197
200
  if (size == 0) {
198
201
  xfree(ptr);
199
202
  return NULL;
@@ -233,7 +236,7 @@ void Arena_Pin(VALUE _arena, VALUE obj) {
233
236
  Arena *arena;
234
237
  TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
235
238
  if (arena->pinned_objs == Qnil) {
236
- arena->pinned_objs = rb_ary_new();
239
+ RB_OBJ_WRITE(_arena, &arena->pinned_objs, rb_ary_new());
237
240
  }
238
241
  rb_ary_push(arena->pinned_objs, obj);
239
242
  }
@@ -250,164 +253,48 @@ void Arena_register(VALUE module) {
250
253
  // Object Cache
251
254
  // -----------------------------------------------------------------------------
252
255
 
253
- // A pointer -> Ruby Object cache that keeps references to Ruby wrapper
254
- // objects. This allows us to look up any Ruby wrapper object by the address
255
- // of the object it is wrapping. That way we can avoid ever creating two
256
- // different wrapper objects for the same C object, which saves memory and
257
- // preserves object identity.
258
- //
259
- // We use WeakMap for the cache. For Ruby <2.7 we also need a secondary Hash
260
- // to store WeakMap keys because Ruby <2.7 WeakMap doesn't allow non-finalizable
261
- // keys.
262
- //
263
- // We also need the secondary Hash if sizeof(long) < sizeof(VALUE), because this
264
- // means it may not be possible to fit a pointer into a Fixnum. Keys are
265
- // pointers, and if they fit into a Fixnum, Ruby doesn't collect them, but if
266
- // they overflow and require allocating a Bignum, they could get collected
267
- // prematurely, thus removing the cache entry. This happens on 64-bit Windows,
268
- // on which pointers are 64 bits but longs are 32 bits. In this case, we enable
269
- // the secondary Hash to hold the keys and prevent them from being collected.
270
-
271
- #if RUBY_API_VERSION_CODE >= 20700 && SIZEOF_LONG >= SIZEOF_VALUE
272
- #define USE_SECONDARY_MAP 0
273
- #else
274
- #define USE_SECONDARY_MAP 1
275
- #endif
276
-
277
- #if USE_SECONDARY_MAP
278
-
279
- // Maps Numeric -> Object. The object is then used as a key into the WeakMap.
280
- // This is needed for Ruby <2.7 where a number cannot be a key to WeakMap.
281
- // The object is used only for its identity; it does not contain any data.
282
- VALUE secondary_map = Qnil;
283
-
284
- // Mutations to the map are under a mutex, because SeconaryMap_MaybeGC()
285
- // iterates over the map which cannot happen in parallel with insertions, or
286
- // Ruby will throw:
287
- // can't add a new key into hash during iteration (RuntimeError)
288
- VALUE secondary_map_mutex = Qnil;
289
-
290
- // Lambda that will GC entries from the secondary map that are no longer present
291
- // in the primary map.
292
- VALUE gc_secondary_map_lambda = Qnil;
293
- ID length;
294
-
295
- extern VALUE weak_obj_cache;
296
-
297
- static void SecondaryMap_Init() {
298
- rb_gc_register_address(&secondary_map);
299
- rb_gc_register_address(&gc_secondary_map_lambda);
300
- rb_gc_register_address(&secondary_map_mutex);
301
- secondary_map = rb_hash_new();
302
- gc_secondary_map_lambda = rb_eval_string(
303
- "->(secondary, weak) {\n"
304
- " secondary.delete_if { |k, v| !weak.key?(v) }\n"
305
- "}\n");
306
- secondary_map_mutex = rb_mutex_new();
307
- length = rb_intern("length");
308
- }
309
-
310
- // The secondary map is a regular Hash, and will never shrink on its own.
311
- // The main object cache is a WeakMap that will automatically remove entries
312
- // when the target object is no longer reachable, but unless we manually
313
- // remove the corresponding entries from the secondary map, it will grow
314
- // without bound.
315
- //
316
- // To avoid this unbounded growth we periodically remove entries from the
317
- // secondary map that are no longer present in the WeakMap. The logic of
318
- // how often to perform this GC is an artbirary tuning parameter that
319
- // represents a straightforward CPU/memory tradeoff.
320
- //
321
- // Requires: secondary_map_mutex is held.
322
- static void SecondaryMap_MaybeGC() {
323
- PBRUBY_ASSERT(rb_mutex_locked_p(secondary_map_mutex) == Qtrue);
324
- size_t weak_len = NUM2ULL(rb_funcall(weak_obj_cache, length, 0));
325
- size_t secondary_len = RHASH_SIZE(secondary_map);
326
- if (secondary_len < weak_len) {
327
- // Logically this case should not be possible: a valid entry cannot exist in
328
- // the weak table unless there is a corresponding entry in the secondary
329
- // table. It should *always* be the case that secondary_len >= weak_len.
330
- //
331
- // However ObjectSpace::WeakMap#length (and therefore weak_len) is
332
- // unreliable: it overreports its true length by including non-live objects.
333
- // However these non-live objects are not yielded in iteration, so we may
334
- // have previously deleted them from the secondary map in a previous
335
- // invocation of SecondaryMap_MaybeGC().
336
- //
337
- // In this case, we can't measure any waste, so we just return.
338
- return;
339
- }
340
- size_t waste = secondary_len - weak_len;
341
- // GC if we could remove at least 2000 entries or 20% of the table size
342
- // (whichever is greater). Since the cost of the GC pass is O(N), we
343
- // want to make sure that we condition this on overall table size, to
344
- // avoid O(N^2) CPU costs.
345
- size_t threshold = PBRUBY_MAX(secondary_len * 0.2, 2000);
346
- if (waste > threshold) {
347
- rb_funcall(gc_secondary_map_lambda, rb_intern("call"), 2, secondary_map,
348
- weak_obj_cache);
349
- }
350
- }
351
-
352
- // Requires: secondary_map_mutex is held by this thread iff create == true.
353
- static VALUE SecondaryMap_Get(VALUE key, bool create) {
354
- PBRUBY_ASSERT(!create || rb_mutex_locked_p(secondary_map_mutex) == Qtrue);
355
- VALUE ret = rb_hash_lookup(secondary_map, key);
356
- if (ret == Qnil && create) {
357
- SecondaryMap_MaybeGC();
358
- ret = rb_class_new_instance(0, NULL, rb_cObject);
359
- rb_hash_aset(secondary_map, key, ret);
360
- }
361
- return ret;
362
- }
363
-
364
- #endif
365
-
366
- // Requires: secondary_map_mutex is held by this thread iff create == true.
367
- static VALUE ObjectCache_GetKey(const void *key, bool create) {
368
- VALUE key_val = (VALUE)key;
369
- PBRUBY_ASSERT((key_val & 3) == 0);
370
- VALUE ret = LL2NUM(key_val >> 2);
371
- #if USE_SECONDARY_MAP
372
- ret = SecondaryMap_Get(ret, create);
373
- #endif
374
- return ret;
375
- }
376
-
377
256
  // Public ObjectCache API.
378
257
 
379
258
  VALUE weak_obj_cache = Qnil;
380
259
  ID item_get;
381
- ID item_set;
260
+ ID item_try_add;
261
+
262
+ static void ObjectCache_Init(VALUE protobuf) {
263
+ item_get = rb_intern("get");
264
+ item_try_add = rb_intern("try_add");
382
265
 
383
- static void ObjectCache_Init() {
384
266
  rb_gc_register_address(&weak_obj_cache);
385
- VALUE klass = rb_eval_string("ObjectSpace::WeakMap");
386
- weak_obj_cache = rb_class_new_instance(0, NULL, klass);
387
- item_get = rb_intern("[]");
388
- item_set = rb_intern("[]=");
389
- #if USE_SECONDARY_MAP
390
- SecondaryMap_Init();
267
+ #if SIZEOF_LONG >= SIZEOF_VALUE
268
+ VALUE cache_class = rb_const_get(protobuf, rb_intern("ObjectCache"));
269
+ #else
270
+ VALUE cache_class = rb_const_get(protobuf, rb_intern("LegacyObjectCache"));
391
271
  #endif
272
+
273
+ weak_obj_cache = rb_class_new_instance(0, NULL, cache_class);
274
+ rb_const_set(protobuf, rb_intern("OBJECT_CACHE"), weak_obj_cache);
275
+ rb_const_set(protobuf, rb_intern("SIZEOF_LONG"), INT2NUM(SIZEOF_LONG));
276
+ rb_const_set(protobuf, rb_intern("SIZEOF_VALUE"), INT2NUM(SIZEOF_VALUE));
392
277
  }
393
278
 
394
- void ObjectCache_Add(const void *key, VALUE val) {
395
- PBRUBY_ASSERT(ObjectCache_Get(key) == Qnil);
396
- #if USE_SECONDARY_MAP
397
- rb_mutex_lock(secondary_map_mutex);
398
- #endif
399
- VALUE key_rb = ObjectCache_GetKey(key, true);
400
- rb_funcall(weak_obj_cache, item_set, 2, key_rb, val);
401
- #if USE_SECONDARY_MAP
402
- rb_mutex_unlock(secondary_map_mutex);
403
- #endif
404
- PBRUBY_ASSERT(ObjectCache_Get(key) == val);
279
+ static VALUE ObjectCache_GetKey(const void *key) {
280
+ VALUE key_val = (VALUE)key;
281
+ PBRUBY_ASSERT((key_val & 3) == 0);
282
+ // Ensure the key can be stored as a Fixnum since 1 bit is needed for
283
+ // FIXNUM_FLAG and 1 bit is needed for the sign bit.
284
+ VALUE new_key = LL2NUM(key_val >> 2);
285
+ PBRUBY_ASSERT(FIXNUM_P(new_key));
286
+ return new_key;
287
+ }
288
+
289
+ VALUE ObjectCache_TryAdd(const void *key, VALUE val) {
290
+ VALUE key_val = ObjectCache_GetKey(key);
291
+ return rb_funcall(weak_obj_cache, item_try_add, 2, key_val, val);
405
292
  }
406
293
 
407
294
  // Returns the cached object for this key, if any. Otherwise returns Qnil.
408
295
  VALUE ObjectCache_Get(const void *key) {
409
- VALUE key_rb = ObjectCache_GetKey(key, false);
410
- return rb_funcall(weak_obj_cache, item_get, 1, key_rb);
296
+ VALUE key_val = ObjectCache_GetKey(key);
297
+ return rb_funcall(weak_obj_cache, item_get, 1, key_val);
411
298
  }
412
299
 
413
300
  /*
@@ -457,11 +344,10 @@ VALUE Google_Protobuf_deep_copy(VALUE self, VALUE obj) {
457
344
  // This must be named "Init_protobuf_c" because the Ruby module is named
458
345
  // "protobuf_c" -- the VM looks for this symbol in our .so.
459
346
  __attribute__((visibility("default"))) void Init_protobuf_c() {
460
- ObjectCache_Init();
461
-
462
347
  VALUE google = rb_define_module("Google");
463
348
  VALUE protobuf = rb_define_module_under(google, "Protobuf");
464
349
 
350
+ ObjectCache_Init(protobuf);
465
351
  Arena_register(protobuf);
466
352
  Defs_register(protobuf);
467
353
  RepeatedField_register(protobuf);
@@ -31,8 +31,22 @@
31
31
  #ifndef __GOOGLE_PROTOBUF_RUBY_PROTOBUF_H__
32
32
  #define __GOOGLE_PROTOBUF_RUBY_PROTOBUF_H__
33
33
 
34
+ // Ruby 3+ defines NDEBUG itself, see: https://bugs.ruby-lang.org/issues/18777
35
+ #ifdef NDEBUG
36
+ #include <ruby.h>
37
+ #else
38
+ #include <ruby.h>
39
+ #undef NDEBUG
40
+ #endif
41
+
42
+ #include <ruby/version.h>
43
+
44
+ #if RUBY_API_VERSION_CODE < 20700
45
+ #error Protobuf requires Ruby >= 2.7
46
+ #endif
47
+
48
+ #include <assert.h> // Must be included after the NDEBUG logic above.
34
49
  #include <ruby/encoding.h>
35
- #include <ruby/ruby.h>
36
50
  #include <ruby/vm.h>
37
51
 
38
52
  #include "defs.h"
@@ -76,10 +90,9 @@ void Arena_Pin(VALUE arena, VALUE obj);
76
90
  // being collected (though in Ruby <2.7 is it effectively strong, due to
77
91
  // implementation limitations).
78
92
 
79
- // Adds an entry to the cache. The "arena" parameter must give the arena that
80
- // "key" was allocated from. In Ruby <2.7.0, it will be used to remove the key
81
- // from the cache when the arena is destroyed.
82
- void ObjectCache_Add(const void* key, VALUE val);
93
+ // Tries to add a new entry to the cache, returning the newly installed value or
94
+ // the pre-existing entry.
95
+ VALUE ObjectCache_TryAdd(const void* key, VALUE val);
83
96
 
84
97
  // Returns the cached object for this key, if any. Otherwise returns Qnil.
85
98
  VALUE ObjectCache_Get(const void* key);
@@ -110,7 +123,9 @@ extern VALUE cTypeError;
110
123
  do { \
111
124
  } while (false && (expr))
112
125
  #else
113
- #define PBRUBY_ASSERT(expr) assert(expr)
126
+ #define PBRUBY_ASSERT(expr) \
127
+ if (!(expr)) \
128
+ rb_bug("Assertion failed at %s:%d, expr: %s", __FILE__, __LINE__, #expr)
114
129
  #endif
115
130
 
116
131
  #define PBRUBY_MAX(x, y) (((x) > (y)) ? (x) : (y))