google-protobuf 3.21.0 → 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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/ext/google/protobuf_c/Rakefile +3 -0
  3. data/ext/google/protobuf_c/convert.c +67 -86
  4. data/ext/google/protobuf_c/convert.h +3 -28
  5. data/ext/google/protobuf_c/defs.c +537 -75
  6. data/ext/google/protobuf_c/defs.h +3 -28
  7. data/ext/google/protobuf_c/extconf.rb +4 -4
  8. data/ext/google/protobuf_c/glue.c +72 -0
  9. data/ext/google/protobuf_c/map.c +114 -85
  10. data/ext/google/protobuf_c/map.h +12 -30
  11. data/ext/google/protobuf_c/message.c +250 -234
  12. data/ext/google/protobuf_c/message.h +11 -33
  13. data/ext/google/protobuf_c/protobuf.c +63 -185
  14. data/ext/google/protobuf_c/protobuf.h +27 -39
  15. data/ext/google/protobuf_c/repeated_field.c +72 -38
  16. data/ext/google/protobuf_c/repeated_field.h +11 -29
  17. data/ext/google/protobuf_c/ruby-upb.c +14513 -9003
  18. data/ext/google/protobuf_c/ruby-upb.h +13990 -4496
  19. data/ext/google/protobuf_c/shared_convert.c +69 -0
  20. data/ext/google/protobuf_c/shared_convert.h +26 -0
  21. data/ext/google/protobuf_c/shared_message.c +37 -0
  22. data/ext/google/protobuf_c/shared_message.h +21 -0
  23. data/ext/google/protobuf_c/third_party/utf8_range/LICENSE +1 -0
  24. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.c +467 -0
  25. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.h +20 -7
  26. data/ext/google/protobuf_c/wrap_memcpy.c +3 -26
  27. data/lib/google/protobuf/any_pb.rb +6 -8
  28. data/lib/google/protobuf/api_pb.rb +6 -26
  29. data/lib/google/protobuf/descriptor_pb.rb +23 -226
  30. data/lib/google/protobuf/duration_pb.rb +6 -8
  31. data/lib/google/protobuf/empty_pb.rb +6 -6
  32. data/lib/google/protobuf/ffi/descriptor.rb +165 -0
  33. data/lib/google/protobuf/ffi/descriptor_pool.rb +77 -0
  34. data/lib/google/protobuf/ffi/enum_descriptor.rb +173 -0
  35. data/lib/google/protobuf/ffi/ffi.rb +210 -0
  36. data/lib/google/protobuf/ffi/field_descriptor.rb +330 -0
  37. data/lib/google/protobuf/ffi/file_descriptor.rb +49 -0
  38. data/lib/google/protobuf/ffi/internal/arena.rb +60 -0
  39. data/lib/google/protobuf/ffi/internal/convert.rb +296 -0
  40. data/lib/google/protobuf/ffi/internal/pointer_helper.rb +35 -0
  41. data/lib/google/protobuf/ffi/internal/type_safety.rb +25 -0
  42. data/lib/google/protobuf/ffi/map.rb +433 -0
  43. data/lib/google/protobuf/ffi/message.rb +781 -0
  44. data/lib/google/protobuf/ffi/method_descriptor.rb +114 -0
  45. data/lib/google/protobuf/ffi/object_cache.rb +30 -0
  46. data/lib/google/protobuf/ffi/oneof_descriptor.rb +97 -0
  47. data/lib/google/protobuf/ffi/repeated_field.rb +411 -0
  48. data/lib/google/protobuf/ffi/service_descriptor.rb +107 -0
  49. data/lib/google/protobuf/field_mask_pb.rb +6 -7
  50. data/lib/google/protobuf/internal/object_cache.rb +99 -0
  51. data/lib/google/protobuf/message_exts.rb +8 -26
  52. data/lib/google/protobuf/plugin_pb.rb +25 -0
  53. data/lib/google/protobuf/repeated_field.rb +7 -31
  54. data/lib/google/protobuf/source_context_pb.rb +6 -7
  55. data/lib/google/protobuf/struct_pb.rb +6 -23
  56. data/lib/google/protobuf/timestamp_pb.rb +6 -8
  57. data/lib/google/protobuf/type_pb.rb +6 -71
  58. data/lib/google/protobuf/well_known_types.rb +5 -34
  59. data/lib/google/protobuf/wrappers_pb.rb +6 -31
  60. data/lib/google/protobuf.rb +27 -45
  61. data/lib/google/protobuf_ffi.rb +51 -0
  62. data/lib/google/protobuf_native.rb +19 -0
  63. data/lib/google/tasks/ffi.rake +100 -0
  64. metadata +92 -16
  65. data/ext/google/protobuf_c/third_party/utf8_range/naive.c +0 -92
  66. data/ext/google/protobuf_c/third_party/utf8_range/range2-neon.c +0 -157
  67. data/ext/google/protobuf_c/third_party/utf8_range/range2-sse.c +0 -170
  68. data/lib/google/protobuf/descriptor_dsl.rb +0 -465
  69. data/tests/basic.rb +0 -739
  70. data/tests/generated_code_test.rb +0 -23
  71. data/tests/stress.rb +0 -38
@@ -1,38 +1,13 @@
1
1
  // Protocol Buffers - Google's data interchange format
2
2
  // Copyright 2008 Google Inc. All rights reserved.
3
- // https://developers.google.com/protocol-buffers/
4
3
  //
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.
4
+ // Use of this source code is governed by a BSD-style
5
+ // license that can be found in the LICENSE file or at
6
+ // https://developers.google.com/open-source/licenses/bsd
30
7
 
31
8
  #ifndef RUBY_PROTOBUF_MESSAGE_H_
32
9
  #define RUBY_PROTOBUF_MESSAGE_H_
33
10
 
34
- #include <ruby/ruby.h>
35
-
36
11
  #include "protobuf.h"
37
12
  #include "ruby-upb.h"
38
13
 
@@ -61,7 +36,7 @@ const upb_Message* Message_GetUpbMessage(VALUE value, const upb_MessageDef* m,
61
36
 
62
37
  // Gets or constructs a Ruby wrapper object for the given message. The wrapper
63
38
  // object will reference |arena| and ensure that it outlives this object.
64
- VALUE Message_GetRubyWrapper(upb_Message* msg, const upb_MessageDef* m,
39
+ VALUE Message_GetRubyWrapper(const upb_Message* msg, const upb_MessageDef* m,
65
40
  VALUE arena);
66
41
 
67
42
  // Gets the given field from this message.
@@ -79,10 +54,6 @@ uint64_t Message_Hash(const upb_Message* msg, const upb_MessageDef* m,
79
54
  upb_Message* Message_deep_copy(const upb_Message* msg, const upb_MessageDef* m,
80
55
  upb_Arena* arena);
81
56
 
82
- // Returns true if these two messages are equal.
83
- bool Message_Equal(const upb_Message* m1, const upb_Message* m2,
84
- const upb_MessageDef* m);
85
-
86
57
  // Checks that this Ruby object is a message, and raises an exception if not.
87
58
  void Message_CheckClass(VALUE klass);
88
59
 
@@ -98,6 +69,13 @@ VALUE build_module_from_enumdesc(VALUE _enumdesc);
98
69
  // module.
99
70
  VALUE MessageOrEnum_GetDescriptor(VALUE klass);
100
71
 
72
+ // Decodes a Message from a byte sequence.
73
+ VALUE Message_decode_bytes(int size, const char* bytes, int options,
74
+ VALUE klass, bool freeze);
75
+
76
+ // Recursively freeze message
77
+ VALUE Message_freeze(VALUE _self);
78
+
101
79
  // Call at startup to register all types in this module.
102
80
  void Message_register(VALUE protobuf);
103
81
 
@@ -1,32 +1,9 @@
1
1
  // Protocol Buffers - Google's data interchange format
2
2
  // Copyright 2014 Google Inc. All rights reserved.
3
- // https://developers.google.com/protocol-buffers/
4
3
  //
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.
4
+ // Use of this source code is governed by a BSD-style
5
+ // license that can be found in the LICENSE file or at
6
+ // https://developers.google.com/open-source/licenses/bsd
30
7
 
31
8
  #include "protobuf.h"
32
9
 
@@ -171,6 +148,8 @@ void StringBuilder_PrintMsgval(StringBuilder *b, upb_MessageValue val,
171
148
 
172
149
  typedef struct {
173
150
  upb_Arena *arena;
151
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
152
+ // macro to update VALUE references, as to trigger write barriers.
174
153
  VALUE pinned_objs;
175
154
  } Arena;
176
155
 
@@ -185,15 +164,28 @@ static void Arena_free(void *data) {
185
164
  xfree(arena);
186
165
  }
187
166
 
167
+ static size_t Arena_memsize(const void *data) {
168
+ const Arena *arena = data;
169
+ size_t fused_count;
170
+ size_t memsize = upb_Arena_SpaceAllocated(arena->arena, &fused_count);
171
+ if (fused_count > 1) {
172
+ // If other arena were fused we attribute an equal
173
+ // share of memory usage to each one.
174
+ memsize /= fused_count;
175
+ }
176
+ return memsize + sizeof(Arena);
177
+ }
178
+
188
179
  static VALUE cArena;
189
180
 
190
181
  const rb_data_type_t Arena_type = {
191
182
  "Google::Protobuf::Internal::Arena",
192
- {Arena_mark, Arena_free, NULL},
193
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
183
+ {Arena_mark, Arena_free, Arena_memsize},
184
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
194
185
  };
195
186
 
196
- static void* ruby_upb_allocfunc(upb_alloc* alloc, void* ptr, size_t oldsize, size_t size) {
187
+ static void *ruby_upb_allocfunc(upb_alloc *alloc, void *ptr, size_t oldsize,
188
+ size_t size) {
197
189
  if (size == 0) {
198
190
  xfree(ptr);
199
191
  return NULL;
@@ -229,15 +221,6 @@ void Arena_fuse(VALUE _arena, upb_Arena *other) {
229
221
 
230
222
  VALUE Arena_new() { return Arena_alloc(cArena); }
231
223
 
232
- void Arena_Pin(VALUE _arena, VALUE obj) {
233
- Arena *arena;
234
- TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
235
- if (arena->pinned_objs == Qnil) {
236
- arena->pinned_objs = rb_ary_new();
237
- }
238
- rb_ary_push(arena->pinned_objs, obj);
239
- }
240
-
241
224
  void Arena_register(VALUE module) {
242
225
  VALUE internal = rb_define_module_under(module, "Internal");
243
226
  VALUE klass = rb_define_class_under(internal, "Arena", rb_cObject);
@@ -250,164 +233,49 @@ void Arena_register(VALUE module) {
250
233
  // Object Cache
251
234
  // -----------------------------------------------------------------------------
252
235
 
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
236
  // Public ObjectCache API.
378
237
 
379
238
  VALUE weak_obj_cache = Qnil;
380
239
  ID item_get;
381
- ID item_set;
240
+ ID item_try_add;
241
+
242
+ static void ObjectCache_Init(VALUE protobuf) {
243
+ item_get = rb_intern("get");
244
+ item_try_add = rb_intern("try_add");
382
245
 
383
- static void ObjectCache_Init() {
384
246
  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();
247
+ VALUE internal = rb_const_get(protobuf, rb_intern("Internal"));
248
+ #if SIZEOF_LONG >= SIZEOF_VALUE
249
+ VALUE cache_class = rb_const_get(internal, rb_intern("ObjectCache"));
250
+ #else
251
+ VALUE cache_class = rb_const_get(internal, rb_intern("LegacyObjectCache"));
391
252
  #endif
253
+
254
+ weak_obj_cache = rb_class_new_instance(0, NULL, cache_class);
255
+ rb_const_set(internal, rb_intern("OBJECT_CACHE"), weak_obj_cache);
256
+ rb_const_set(internal, rb_intern("SIZEOF_LONG"), INT2NUM(SIZEOF_LONG));
257
+ rb_const_set(internal, rb_intern("SIZEOF_VALUE"), INT2NUM(SIZEOF_VALUE));
392
258
  }
393
259
 
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);
260
+ static VALUE ObjectCache_GetKey(const void *key) {
261
+ VALUE key_val = (VALUE)key;
262
+ PBRUBY_ASSERT((key_val & 3) == 0);
263
+ // Ensure the key can be stored as a Fixnum since 1 bit is needed for
264
+ // FIXNUM_FLAG and 1 bit is needed for the sign bit.
265
+ VALUE new_key = LL2NUM(key_val >> 2);
266
+ PBRUBY_ASSERT(FIXNUM_P(new_key));
267
+ return new_key;
268
+ }
269
+
270
+ VALUE ObjectCache_TryAdd(const void *key, VALUE val) {
271
+ VALUE key_val = ObjectCache_GetKey(key);
272
+ return rb_funcall(weak_obj_cache, item_try_add, 2, key_val, val);
405
273
  }
406
274
 
407
275
  // Returns the cached object for this key, if any. Otherwise returns Qnil.
408
276
  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);
277
+ VALUE key_val = ObjectCache_GetKey(key);
278
+ return rb_funcall(weak_obj_cache, item_get, 1, key_val);
411
279
  }
412
280
 
413
281
  /*
@@ -457,11 +325,10 @@ VALUE Google_Protobuf_deep_copy(VALUE self, VALUE obj) {
457
325
  // This must be named "Init_protobuf_c" because the Ruby module is named
458
326
  // "protobuf_c" -- the VM looks for this symbol in our .so.
459
327
  __attribute__((visibility("default"))) void Init_protobuf_c() {
460
- ObjectCache_Init();
461
-
462
328
  VALUE google = rb_define_module("Google");
463
329
  VALUE protobuf = rb_define_module_under(google, "Protobuf");
464
330
 
331
+ ObjectCache_Init(protobuf);
465
332
  Arena_register(protobuf);
466
333
  Defs_register(protobuf);
467
334
  RepeatedField_register(protobuf);
@@ -478,3 +345,14 @@ __attribute__((visibility("default"))) void Init_protobuf_c() {
478
345
  rb_define_singleton_method(protobuf, "deep_copy", Google_Protobuf_deep_copy,
479
346
  1);
480
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
+ }
@@ -1,38 +1,29 @@
1
1
  // Protocol Buffers - Google's data interchange format
2
2
  // Copyright 2014 Google Inc. All rights reserved.
3
- // https://developers.google.com/protocol-buffers/
4
3
  //
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.
4
+ // Use of this source code is governed by a BSD-style
5
+ // license that can be found in the LICENSE file or at
6
+ // https://developers.google.com/open-source/licenses/bsd
30
7
 
31
8
  #ifndef __GOOGLE_PROTOBUF_RUBY_PROTOBUF_H__
32
9
  #define __GOOGLE_PROTOBUF_RUBY_PROTOBUF_H__
33
10
 
11
+ // Ruby 3+ defines NDEBUG itself, see: https://bugs.ruby-lang.org/issues/18777
12
+ #ifdef NDEBUG
13
+ #include <ruby.h>
14
+ #else
15
+ #include <ruby.h>
16
+ #undef NDEBUG
17
+ #endif
18
+
19
+ #include <ruby/version.h>
20
+
21
+ #if RUBY_API_VERSION_CODE < 20700
22
+ #error Protobuf requires Ruby >= 2.7
23
+ #endif
24
+
25
+ #include <assert.h> // Must be included after the NDEBUG logic above.
34
26
  #include <ruby/encoding.h>
35
- #include <ruby/ruby.h>
36
27
  #include <ruby/vm.h>
37
28
 
38
29
  #include "defs.h"
@@ -59,13 +50,6 @@ upb_Arena* Arena_get(VALUE arena);
59
50
  // possible.
60
51
  void Arena_fuse(VALUE arena, upb_Arena* other);
61
52
 
62
- // Pins this Ruby object to the lifetime of this arena, so that as long as the
63
- // arena is alive this object will not be collected.
64
- //
65
- // We use this to guarantee that the "frozen" bit on the object will be
66
- // remembered, even if the user drops their reference to this precise object.
67
- void Arena_Pin(VALUE arena, VALUE obj);
68
-
69
53
  // -----------------------------------------------------------------------------
70
54
  // ObjectCache
71
55
  // -----------------------------------------------------------------------------
@@ -76,10 +60,9 @@ void Arena_Pin(VALUE arena, VALUE obj);
76
60
  // being collected (though in Ruby <2.7 is it effectively strong, due to
77
61
  // implementation limitations).
78
62
 
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);
63
+ // Tries to add a new entry to the cache, returning the newly installed value or
64
+ // the pre-existing entry.
65
+ VALUE ObjectCache_TryAdd(const void* key, VALUE val);
83
66
 
84
67
  // Returns the cached object for this key, if any. Otherwise returns Qnil.
85
68
  VALUE ObjectCache_Get(const void* key);
@@ -110,9 +93,14 @@ extern VALUE cTypeError;
110
93
  do { \
111
94
  } while (false && (expr))
112
95
  #else
113
- #define PBRUBY_ASSERT(expr) assert(expr)
96
+ #define PBRUBY_ASSERT(expr) \
97
+ if (!(expr)) \
98
+ rb_bug("Assertion failed at %s:%d, expr: %s", __FILE__, __LINE__, #expr)
114
99
  #endif
115
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
+
116
104
  #define PBRUBY_MAX(x, y) (((x) > (y)) ? (x) : (y))
117
105
 
118
106
  #define UPB_UNUSED(var) (void)var
@@ -1,32 +1,9 @@
1
1
  // Protocol Buffers - Google's data interchange format
2
2
  // Copyright 2014 Google Inc. All rights reserved.
3
- // https://developers.google.com/protocol-buffers/
4
3
  //
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.
4
+ // Use of this source code is governed by a BSD-style
5
+ // license that can be found in the LICENSE file or at
6
+ // https://developers.google.com/open-source/licenses/bsd
30
7
 
31
8
  #include "repeated_field.h"
32
9
 
@@ -67,8 +44,9 @@ static RepeatedField* ruby_to_RepeatedField(VALUE _self) {
67
44
  }
68
45
 
69
46
  static upb_Array* RepeatedField_GetMutable(VALUE _self) {
70
- rb_check_frozen(_self);
71
- 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;
72
50
  }
73
51
 
74
52
  VALUE RepeatedField_alloc(VALUE klass) {
@@ -79,15 +57,37 @@ VALUE RepeatedField_alloc(VALUE klass) {
79
57
  return TypedData_Wrap_Struct(klass, &RepeatedField_type, self);
80
58
  }
81
59
 
82
- 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,
83
83
  VALUE arena) {
84
84
  PBRUBY_ASSERT(array);
85
+ PBRUBY_ASSERT(arena != Qnil);
85
86
  VALUE val = ObjectCache_Get(array);
86
87
 
87
88
  if (val == Qnil) {
88
89
  val = RepeatedField_alloc(cRepeatedField);
89
90
  RepeatedField* self;
90
- ObjectCache_Add(array, val);
91
91
  TypedData_Get_Struct(val, RepeatedField, &RepeatedField_type, self);
92
92
  self->array = array;
93
93
  self->arena = arena;
@@ -95,11 +95,13 @@ VALUE RepeatedField_GetRubyWrapper(upb_Array* array, TypeInfo type_info,
95
95
  if (self->type_info.type == kUpb_CType_Message) {
96
96
  self->type_class = Descriptor_DefToClass(type_info.def.msgdef);
97
97
  }
98
+ val = ObjectCache_TryAdd(array, val);
98
99
  }
99
100
 
100
101
  PBRUBY_ASSERT(ruby_to_RepeatedField(val)->type_info.type == type_info.type);
101
102
  PBRUBY_ASSERT(ruby_to_RepeatedField(val)->type_info.def.msgdef ==
102
103
  type_info.def.msgdef);
104
+ PBRUBY_ASSERT(ruby_to_RepeatedField(val)->array == array);
103
105
  return val;
104
106
  }
105
107
 
@@ -284,7 +286,7 @@ static VALUE RepeatedField_index_set(VALUE _self, VALUE _index, VALUE val) {
284
286
  memset(&fill, 0, sizeof(fill));
285
287
  for (int i = size; i < index; i++) {
286
288
  // Fill default values.
287
- // TODO(haberman): should this happen at the upb level?
289
+ // TODO: should this happen at the upb level?
288
290
  upb_Array_Set(array, i, fill);
289
291
  }
290
292
  }
@@ -492,19 +494,49 @@ VALUE RepeatedField_eq(VALUE _self, VALUE _other) {
492
494
  return Qtrue;
493
495
  }
494
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
+
495
517
  /*
496
518
  * call-seq:
497
519
  * RepeatedField.freeze => self
498
520
  *
499
- * Freezes the repeated field. We have to intercept this so we can pin the Ruby
500
- * 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.
501
523
  */
502
- static VALUE RepeatedField_freeze(VALUE _self) {
524
+ VALUE RepeatedField_freeze(VALUE _self) {
503
525
  RepeatedField* self = ruby_to_RepeatedField(_self);
504
- if (!RB_OBJ_FROZEN(_self)) {
505
- Arena_Pin(self->arena, _self);
506
- RB_OBJ_FREEZE(_self);
526
+ if (RB_OBJ_FROZEN(_self)) {
527
+ PBRUBY_ASSERT(upb_Array_IsFrozen(self->array));
528
+ return _self;
529
+ }
530
+
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);
537
+ }
507
538
  }
539
+ RB_OBJ_FREEZE(_self);
508
540
  return _self;
509
541
  }
510
542
 
@@ -613,7 +645,8 @@ VALUE RepeatedField_init(int argc, VALUE* argv, VALUE _self) {
613
645
 
614
646
  self->type_info = TypeInfo_FromClass(argc, argv, 0, &self->type_class, &ary);
615
647
  self->array = upb_Array_New(arena, self->type_info.type);
616
- ObjectCache_Add(self->array, _self);
648
+ VALUE stored_val = ObjectCache_TryAdd(self->array, _self);
649
+ PBRUBY_ASSERT(stored_val == _self);
617
650
 
618
651
  if (ary != Qnil) {
619
652
  if (!RB_TYPE_P(ary, T_ARRAY)) {
@@ -650,6 +683,7 @@ void RepeatedField_register(VALUE module) {
650
683
  rb_define_method(klass, "==", RepeatedField_eq, 1);
651
684
  rb_define_method(klass, "to_ary", RepeatedField_to_ary, 0);
652
685
  rb_define_method(klass, "freeze", RepeatedField_freeze, 0);
686
+ rb_define_method(klass, "frozen?", RepeatedField_frozen, 0);
653
687
  rb_define_method(klass, "hash", RepeatedField_hash, 0);
654
688
  rb_define_method(klass, "+", RepeatedField_plus, 1);
655
689
  rb_define_method(klass, "concat", RepeatedField_concat, 1);