google-protobuf 3.21.12 → 3.24.2

Sign up to get free protection for your applications and to get access to all the features.
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 +132 -105
  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 +11395 -9701
  16. data/ext/google/protobuf_c/ruby-upb.h +11620 -5210
  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 +13 -1
  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 +0 -0
  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