google-protobuf 3.20.3 → 3.24.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/ext/google/protobuf_c/convert.c +0 -0
  3. data/ext/google/protobuf_c/convert.h +0 -2
  4. data/ext/google/protobuf_c/defs.c +36 -25
  5. data/ext/google/protobuf_c/defs.h +0 -2
  6. data/ext/google/protobuf_c/extconf.rb +2 -3
  7. data/ext/google/protobuf_c/map.c +31 -44
  8. data/ext/google/protobuf_c/map.h +0 -2
  9. data/ext/google/protobuf_c/message.c +140 -106
  10. data/ext/google/protobuf_c/message.h +0 -2
  11. data/ext/google/protobuf_c/protobuf.c +36 -150
  12. data/ext/google/protobuf_c/protobuf.h +21 -6
  13. data/ext/google/protobuf_c/repeated_field.c +5 -2
  14. data/ext/google/protobuf_c/repeated_field.h +0 -2
  15. data/ext/google/protobuf_c/ruby-upb.c +11686 -8578
  16. data/ext/google/protobuf_c/ruby-upb.h +11727 -4588
  17. data/ext/google/protobuf_c/third_party/utf8_range/LICENSE +1 -0
  18. data/ext/google/protobuf_c/third_party/utf8_range/naive.c +0 -0
  19. data/ext/google/protobuf_c/third_party/utf8_range/range2-neon.c +1 -1
  20. data/ext/google/protobuf_c/third_party/utf8_range/range2-sse.c +0 -0
  21. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.h +12 -0
  22. data/ext/google/protobuf_c/wrap_memcpy.c +0 -0
  23. data/lib/google/protobuf/any_pb.rb +24 -5
  24. data/lib/google/protobuf/api_pb.rb +26 -23
  25. data/lib/google/protobuf/descriptor_dsl.rb +0 -0
  26. data/lib/google/protobuf/descriptor_pb.rb +40 -226
  27. data/lib/google/protobuf/duration_pb.rb +24 -5
  28. data/lib/google/protobuf/empty_pb.rb +24 -3
  29. data/lib/google/protobuf/field_mask_pb.rb +24 -4
  30. data/lib/google/protobuf/message_exts.rb +5 -0
  31. data/lib/google/protobuf/object_cache.rb +120 -0
  32. data/lib/google/protobuf/plugin_pb.rb +47 -0
  33. data/lib/google/protobuf/repeated_field.rb +15 -2
  34. data/lib/google/protobuf/source_context_pb.rb +24 -4
  35. data/lib/google/protobuf/struct_pb.rb +24 -20
  36. data/lib/google/protobuf/timestamp_pb.rb +24 -5
  37. data/lib/google/protobuf/type_pb.rb +26 -68
  38. data/lib/google/protobuf/well_known_types.rb +2 -8
  39. data/lib/google/protobuf/wrappers_pb.rb +24 -28
  40. data/lib/google/protobuf.rb +1 -0
  41. metadata +8 -12
  42. data/tests/basic.rb +0 -739
  43. data/tests/generated_code_test.rb +0 -23
  44. data/tests/stress.rb +0 -38
@@ -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))
@@ -87,7 +87,6 @@ VALUE RepeatedField_GetRubyWrapper(upb_Array* array, TypeInfo type_info,
87
87
  if (val == Qnil) {
88
88
  val = RepeatedField_alloc(cRepeatedField);
89
89
  RepeatedField* self;
90
- ObjectCache_Add(array, val);
91
90
  TypedData_Get_Struct(val, RepeatedField, &RepeatedField_type, self);
92
91
  self->array = array;
93
92
  self->arena = arena;
@@ -95,11 +94,14 @@ VALUE RepeatedField_GetRubyWrapper(upb_Array* array, TypeInfo type_info,
95
94
  if (self->type_info.type == kUpb_CType_Message) {
96
95
  self->type_class = Descriptor_DefToClass(type_info.def.msgdef);
97
96
  }
97
+ val = ObjectCache_TryAdd(array, val);
98
98
  }
99
99
 
100
100
  PBRUBY_ASSERT(ruby_to_RepeatedField(val)->type_info.type == type_info.type);
101
101
  PBRUBY_ASSERT(ruby_to_RepeatedField(val)->type_info.def.msgdef ==
102
102
  type_info.def.msgdef);
103
+ PBRUBY_ASSERT(ruby_to_RepeatedField(val)->array == array);
104
+
103
105
  return val;
104
106
  }
105
107
 
@@ -613,7 +615,8 @@ VALUE RepeatedField_init(int argc, VALUE* argv, VALUE _self) {
613
615
 
614
616
  self->type_info = TypeInfo_FromClass(argc, argv, 0, &self->type_class, &ary);
615
617
  self->array = upb_Array_New(arena, self->type_info.type);
616
- ObjectCache_Add(self->array, _self);
618
+ VALUE stored_val = ObjectCache_TryAdd(self->array, _self);
619
+ PBRUBY_ASSERT(stored_val == _self);
617
620
 
618
621
  if (ary != Qnil) {
619
622
  if (!RB_TYPE_P(ary, T_ARRAY)) {
@@ -31,8 +31,6 @@
31
31
  #ifndef RUBY_PROTOBUF_REPEATED_FIELD_H_
32
32
  #define RUBY_PROTOBUF_REPEATED_FIELD_H_
33
33
 
34
- #include <ruby/ruby.h>
35
-
36
34
  #include "protobuf.h"
37
35
  #include "ruby-upb.h"
38
36