google-protobuf 3.19.1 → 4.30.2

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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/ext/google/protobuf_c/Rakefile +3 -0
  3. data/ext/google/protobuf_c/convert.c +153 -166
  4. data/ext/google/protobuf_c/convert.h +15 -37
  5. data/ext/google/protobuf_c/defs.c +867 -251
  6. data/ext/google/protobuf_c/defs.h +22 -47
  7. data/ext/google/protobuf_c/extconf.rb +25 -5
  8. data/ext/google/protobuf_c/glue.c +135 -0
  9. data/ext/google/protobuf_c/map.c +182 -145
  10. data/ext/google/protobuf_c/map.h +16 -35
  11. data/ext/google/protobuf_c/message.c +534 -437
  12. data/ext/google/protobuf_c/message.h +30 -49
  13. data/ext/google/protobuf_c/protobuf.c +125 -238
  14. data/ext/google/protobuf_c/protobuf.h +40 -49
  15. data/ext/google/protobuf_c/repeated_field.c +152 -120
  16. data/ext/google/protobuf_c/repeated_field.h +16 -34
  17. data/ext/google/protobuf_c/ruby-upb.c +15827 -7228
  18. data/ext/google/protobuf_c/ruby-upb.h +15075 -3866
  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 +22 -0
  24. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.c +207 -0
  25. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.h +22 -0
  26. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range_neon.inc +117 -0
  27. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range_sse.inc +272 -0
  28. data/ext/google/protobuf_c/wrap_memcpy.c +7 -29
  29. data/lib/google/protobuf/any_pb.rb +6 -8
  30. data/lib/google/protobuf/api_pb.rb +6 -26
  31. data/lib/google/protobuf/descriptor_pb.rb +24 -225
  32. data/lib/google/protobuf/duration_pb.rb +6 -8
  33. data/lib/google/protobuf/empty_pb.rb +6 -6
  34. data/lib/google/protobuf/ffi/descriptor.rb +175 -0
  35. data/lib/google/protobuf/ffi/descriptor_pool.rb +77 -0
  36. data/lib/google/protobuf/ffi/enum_descriptor.rb +183 -0
  37. data/lib/google/protobuf/ffi/ffi.rb +214 -0
  38. data/lib/google/protobuf/ffi/field_descriptor.rb +340 -0
  39. data/lib/google/protobuf/ffi/file_descriptor.rb +59 -0
  40. data/lib/google/protobuf/ffi/internal/arena.rb +60 -0
  41. data/lib/google/protobuf/ffi/internal/convert.rb +292 -0
  42. data/lib/google/protobuf/ffi/internal/pointer_helper.rb +35 -0
  43. data/lib/google/protobuf/ffi/internal/type_safety.rb +25 -0
  44. data/lib/google/protobuf/ffi/map.rb +433 -0
  45. data/lib/google/protobuf/ffi/message.rb +783 -0
  46. data/lib/google/protobuf/ffi/method_descriptor.rb +124 -0
  47. data/lib/google/protobuf/ffi/object_cache.rb +30 -0
  48. data/lib/google/protobuf/ffi/oneof_descriptor.rb +107 -0
  49. data/lib/google/protobuf/ffi/repeated_field.rb +411 -0
  50. data/lib/google/protobuf/ffi/service_descriptor.rb +117 -0
  51. data/lib/google/protobuf/field_mask_pb.rb +6 -7
  52. data/lib/google/protobuf/internal/object_cache.rb +99 -0
  53. data/lib/google/protobuf/message_exts.rb +10 -28
  54. data/lib/google/protobuf/plugin_pb.rb +25 -0
  55. data/lib/google/protobuf/repeated_field.rb +22 -33
  56. data/lib/google/protobuf/source_context_pb.rb +6 -7
  57. data/lib/google/protobuf/struct_pb.rb +6 -23
  58. data/lib/google/protobuf/timestamp_pb.rb +6 -8
  59. data/lib/google/protobuf/type_pb.rb +6 -71
  60. data/lib/google/protobuf/well_known_types.rb +16 -40
  61. data/lib/google/protobuf/wrappers_pb.rb +6 -31
  62. data/lib/google/protobuf.rb +32 -50
  63. data/lib/google/protobuf_ffi.rb +52 -0
  64. data/lib/google/protobuf_native.rb +19 -0
  65. data/lib/google/tasks/ffi.rake +100 -0
  66. metadata +97 -20
  67. data/lib/google/protobuf/descriptor_dsl.rb +0 -458
  68. data/tests/basic.rb +0 -640
  69. data/tests/generated_code_test.rb +0 -23
  70. data/tests/stress.rb +0 -38
@@ -1,90 +1,64 @@
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
 
39
- // Gets the underlying upb_msg* and upb_msgdef for the given Ruby message
40
- // wrapper. Requires that |value| is indeed a message object.
41
- const upb_msg *Message_Get(VALUE value, const upb_msgdef **m);
14
+ // Gets the underlying upb_Message* and upb_MessageDef for the given Ruby
15
+ // message wrapper. Requires that |value| is indeed a message object.
16
+ const upb_Message* Message_Get(VALUE value, const upb_MessageDef** m);
42
17
 
43
18
  // Like Message_Get(), but checks that the object is not frozen and returns a
44
19
  // mutable pointer.
45
- upb_msg *Message_GetMutable(VALUE value, const upb_msgdef **m);
20
+ upb_Message* Message_GetMutable(VALUE value, const upb_MessageDef** m);
46
21
 
47
22
  // Returns the Arena object for this message.
48
23
  VALUE Message_GetArena(VALUE value);
49
24
 
50
- // Converts |value| into a upb_msg value of the expected upb_msgdef type,
51
- // raising an error if this is not possible. Used when assigning |value| to a
52
- // field of another message, which means the message must be of a particular
53
- // type.
25
+ // Converts |value| into a upb_Message value of the expected upb_MessageDef
26
+ // type, raising an error if this is not possible. Used when assigning |value|
27
+ // to a field of another message, which means the message must be of a
28
+ // particular type.
54
29
  //
55
30
  // This will perform automatic conversions in some cases (for example, Time ->
56
31
  // Google::Protobuf::Timestamp). If any new message is created, it will be
57
32
  // created on |arena|, and any existing message will have its arena fused with
58
33
  // |arena|.
59
- const upb_msg* Message_GetUpbMessage(VALUE value, const upb_msgdef* m,
60
- const char* name, upb_arena* arena);
34
+ const upb_Message* Message_GetUpbMessage(VALUE value, const upb_MessageDef* m,
35
+ const char* name, upb_Arena* arena);
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_msg* msg, const upb_msgdef* m, VALUE arena);
39
+ VALUE Message_GetRubyWrapper(const upb_Message* msg, const upb_MessageDef* m,
40
+ VALUE arena);
65
41
 
66
42
  // Gets the given field from this message.
67
- VALUE Message_getfield(VALUE _self, const upb_fielddef* f);
43
+ VALUE Message_getfield(VALUE _self, const upb_FieldDef* f);
68
44
 
69
45
  // Implements #inspect for this message, printing the text to |b|.
70
- void Message_PrintMessage(StringBuilder* b, const upb_msg* msg,
71
- const upb_msgdef* m);
46
+ void Message_PrintMessage(StringBuilder* b, const upb_Message* msg,
47
+ const upb_MessageDef* m);
72
48
 
73
49
  // Returns a hash value for the given message.
74
- uint64_t Message_Hash(const upb_msg *msg, const upb_msgdef *m, uint64_t seed);
50
+ uint64_t Message_Hash(const upb_Message* msg, const upb_MessageDef* m,
51
+ uint64_t seed);
75
52
 
76
53
  // Returns a deep copy of the given message.
77
- upb_msg* Message_deep_copy(const upb_msg* msg, const upb_msgdef* m,
78
- upb_arena *arena);
79
-
80
- // Returns true if these two messages are equal.
81
- bool Message_Equal(const upb_msg *m1, const upb_msg *m2, const upb_msgdef *m);
54
+ upb_Message* Message_deep_copy(const upb_Message* msg, const upb_MessageDef* m,
55
+ upb_Arena* arena);
82
56
 
83
57
  // Checks that this Ruby object is a message, and raises an exception if not.
84
58
  void Message_CheckClass(VALUE klass);
85
59
 
86
60
  // Returns a new Hash object containing the contents of this message.
87
- VALUE Scalar_CreateHash(upb_msgval val, TypeInfo type_info);
61
+ VALUE Scalar_CreateHash(upb_MessageValue val, TypeInfo type_info);
88
62
 
89
63
  // Creates a message class or enum module for this descriptor, respectively.
90
64
  VALUE build_class_from_descriptor(VALUE descriptor);
@@ -95,6 +69,13 @@ VALUE build_module_from_enumdesc(VALUE _enumdesc);
95
69
  // module.
96
70
  VALUE MessageOrEnum_GetDescriptor(VALUE klass);
97
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
+
98
79
  // Call at startup to register all types in this module.
99
80
  void Message_register(VALUE protobuf);
100
81
 
@@ -1,37 +1,12 @@
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
 
33
- #include <ruby/version.h>
34
-
35
10
  #include "defs.h"
36
11
  #include "map.h"
37
12
  #include "message.h"
@@ -40,14 +15,14 @@
40
15
  VALUE cParseError;
41
16
  VALUE cTypeError;
42
17
 
43
- const upb_fielddef* map_field_key(const upb_fielddef* field) {
44
- const upb_msgdef *entry = upb_fielddef_msgsubdef(field);
45
- return upb_msgdef_itof(entry, 1);
18
+ const upb_FieldDef *map_field_key(const upb_FieldDef *field) {
19
+ const upb_MessageDef *entry = upb_FieldDef_MessageSubDef(field);
20
+ return upb_MessageDef_FindFieldByNumber(entry, 1);
46
21
  }
47
22
 
48
- const upb_fielddef* map_field_value(const upb_fielddef* field) {
49
- const upb_msgdef *entry = upb_fielddef_msgsubdef(field);
50
- return upb_msgdef_itof(entry, 2);
23
+ const upb_FieldDef *map_field_value(const upb_FieldDef *field) {
24
+ const upb_MessageDef *entry = upb_FieldDef_MessageSubDef(field);
25
+ return upb_MessageDef_FindFieldByNumber(entry, 2);
51
26
  }
52
27
 
53
28
  // -----------------------------------------------------------------------------
@@ -66,21 +41,21 @@ static size_t StringBuilder_SizeOf(size_t cap) {
66
41
  return sizeof(StringBuilder) + cap;
67
42
  }
68
43
 
69
- StringBuilder* StringBuilder_New() {
44
+ StringBuilder *StringBuilder_New() {
70
45
  const size_t cap = 128;
71
- StringBuilder* builder = malloc(sizeof(*builder));
46
+ StringBuilder *builder = malloc(sizeof(*builder));
72
47
  builder->size = 0;
73
48
  builder->cap = cap;
74
49
  builder->data = malloc(builder->cap);
75
50
  return builder;
76
51
  }
77
52
 
78
- void StringBuilder_Free(StringBuilder* b) {
53
+ void StringBuilder_Free(StringBuilder *b) {
79
54
  free(b->data);
80
55
  free(b);
81
56
  }
82
57
 
83
- void StringBuilder_Printf(StringBuilder* b, const char *fmt, ...) {
58
+ void StringBuilder_Printf(StringBuilder *b, const char *fmt, ...) {
84
59
  size_t have = b->cap - b->size;
85
60
  size_t n;
86
61
  va_list args;
@@ -104,60 +79,62 @@ void StringBuilder_Printf(StringBuilder* b, const char *fmt, ...) {
104
79
  b->size += n;
105
80
  }
106
81
 
107
- VALUE StringBuilder_ToRubyString(StringBuilder* b) {
82
+ VALUE StringBuilder_ToRubyString(StringBuilder *b) {
108
83
  VALUE ret = rb_str_new(b->data, b->size);
109
84
  rb_enc_associate(ret, rb_utf8_encoding());
110
85
  return ret;
111
86
  }
112
87
 
113
- static void StringBuilder_PrintEnum(StringBuilder* b, int32_t val,
114
- const upb_enumdef* e) {
115
- const char *name = upb_enumdef_iton(e, val);
116
- if (name) {
117
- StringBuilder_Printf(b, ":%s", name);
88
+ static void StringBuilder_PrintEnum(StringBuilder *b, int32_t val,
89
+ const upb_EnumDef *e) {
90
+ const upb_EnumValueDef *ev = upb_EnumDef_FindValueByNumber(e, val);
91
+ if (ev) {
92
+ StringBuilder_Printf(b, ":%s", upb_EnumValueDef_Name(ev));
118
93
  } else {
119
94
  StringBuilder_Printf(b, "%" PRId32, val);
120
95
  }
121
96
  }
122
97
 
123
- void StringBuilder_PrintMsgval(StringBuilder* b, upb_msgval val,
98
+ void StringBuilder_PrintMsgval(StringBuilder *b, upb_MessageValue val,
124
99
  TypeInfo info) {
125
100
  switch (info.type) {
126
- case UPB_TYPE_BOOL:
101
+ case kUpb_CType_Bool:
127
102
  StringBuilder_Printf(b, "%s", val.bool_val ? "true" : "false");
128
103
  break;
129
- case UPB_TYPE_FLOAT: {
104
+ case kUpb_CType_Float: {
130
105
  VALUE str = rb_inspect(DBL2NUM(val.float_val));
131
106
  StringBuilder_Printf(b, "%s", RSTRING_PTR(str));
132
107
  break;
133
108
  }
134
- case UPB_TYPE_DOUBLE: {
109
+ case kUpb_CType_Double: {
135
110
  VALUE str = rb_inspect(DBL2NUM(val.double_val));
136
111
  StringBuilder_Printf(b, "%s", RSTRING_PTR(str));
137
112
  break;
138
113
  }
139
- case UPB_TYPE_INT32:
114
+ case kUpb_CType_Int32:
140
115
  StringBuilder_Printf(b, "%" PRId32, val.int32_val);
141
116
  break;
142
- case UPB_TYPE_UINT32:
117
+ case kUpb_CType_UInt32:
143
118
  StringBuilder_Printf(b, "%" PRIu32, val.uint32_val);
144
119
  break;
145
- case UPB_TYPE_INT64:
120
+ case kUpb_CType_Int64:
146
121
  StringBuilder_Printf(b, "%" PRId64, val.int64_val);
147
122
  break;
148
- case UPB_TYPE_UINT64:
123
+ case kUpb_CType_UInt64:
149
124
  StringBuilder_Printf(b, "%" PRIu64, val.uint64_val);
150
125
  break;
151
- case UPB_TYPE_STRING:
152
- StringBuilder_Printf(b, "\"%.*s\"", (int)val.str_val.size, val.str_val.data);
126
+ case kUpb_CType_String:
127
+ StringBuilder_Printf(b, "\"%.*s\"", (int)val.str_val.size,
128
+ val.str_val.data);
153
129
  break;
154
- case UPB_TYPE_BYTES:
155
- StringBuilder_Printf(b, "\"%.*s\"", (int)val.str_val.size, val.str_val.data);
130
+ case kUpb_CType_Bytes:
131
+ StringBuilder_Printf(b, "\"%.*s\"", (int)val.str_val.size,
132
+ val.str_val.data);
156
133
  break;
157
- case UPB_TYPE_ENUM:
134
+ case kUpb_CType_Enum:
158
135
  StringBuilder_PrintEnum(b, val.int32_val, info.def.enumdef);
159
136
  break;
160
- case UPB_TYPE_MESSAGE:
137
+ case kUpb_CType_Message:
161
138
  Message_PrintMessage(b, val.msg_val, info.def.msgdef);
162
139
  break;
163
140
  }
@@ -168,7 +145,9 @@ void StringBuilder_PrintMsgval(StringBuilder* b, upb_msgval val,
168
145
  // -----------------------------------------------------------------------------
169
146
 
170
147
  typedef struct {
171
- upb_arena *arena;
148
+ upb_Arena *arena;
149
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
150
+ // macro to update VALUE references, as to trigger write barriers.
172
151
  VALUE pinned_objs;
173
152
  } Arena;
174
153
 
@@ -179,53 +158,66 @@ static void Arena_mark(void *data) {
179
158
 
180
159
  static void Arena_free(void *data) {
181
160
  Arena *arena = data;
182
- upb_arena_free(arena->arena);
161
+ upb_Arena_Free(arena->arena);
183
162
  xfree(arena);
184
163
  }
185
164
 
165
+ static size_t Arena_memsize(const void *data) {
166
+ const Arena *arena = data;
167
+ size_t fused_count;
168
+ size_t memsize = upb_Arena_SpaceAllocated(arena->arena, &fused_count);
169
+ if (fused_count > 1) {
170
+ // If other arena were fused we attribute an equal
171
+ // share of memory usage to each one.
172
+ memsize /= fused_count;
173
+ }
174
+ return memsize + sizeof(Arena);
175
+ }
176
+
186
177
  static VALUE cArena;
187
178
 
188
179
  const rb_data_type_t Arena_type = {
189
- "Google::Protobuf::Internal::Arena",
190
- { Arena_mark, Arena_free, NULL },
191
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
180
+ "Google::Protobuf::Internal::Arena",
181
+ {Arena_mark, Arena_free, Arena_memsize},
182
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
192
183
  };
193
184
 
185
+ static void *ruby_upb_allocfunc(upb_alloc *alloc, void *ptr, size_t oldsize,
186
+ size_t size) {
187
+ if (size == 0) {
188
+ xfree(ptr);
189
+ return NULL;
190
+ } else {
191
+ return xrealloc(ptr, size);
192
+ }
193
+ }
194
+
195
+ upb_alloc ruby_upb_alloc = {&ruby_upb_allocfunc};
196
+
194
197
  static VALUE Arena_alloc(VALUE klass) {
195
198
  Arena *arena = ALLOC(Arena);
196
- arena->arena = upb_arena_new();
199
+ arena->arena = upb_Arena_Init(NULL, 0, &ruby_upb_alloc);
197
200
  arena->pinned_objs = Qnil;
198
201
  return TypedData_Wrap_Struct(klass, &Arena_type, arena);
199
202
  }
200
203
 
201
- upb_arena *Arena_get(VALUE _arena) {
204
+ upb_Arena *Arena_get(VALUE _arena) {
202
205
  Arena *arena;
203
206
  TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
204
207
  return arena->arena;
205
208
  }
206
209
 
207
- void Arena_fuse(VALUE _arena, upb_arena *other) {
210
+ void Arena_fuse(VALUE _arena, upb_Arena *other) {
208
211
  Arena *arena;
209
212
  TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
210
- if (!upb_arena_fuse(arena->arena, other)) {
213
+ if (!upb_Arena_Fuse(arena->arena, other)) {
211
214
  rb_raise(rb_eRuntimeError,
212
215
  "Unable to fuse arenas. This should never happen since Ruby does "
213
216
  "not use initial blocks");
214
217
  }
215
218
  }
216
219
 
217
- VALUE Arena_new() {
218
- return Arena_alloc(cArena);
219
- }
220
-
221
- void Arena_Pin(VALUE _arena, VALUE obj) {
222
- Arena *arena;
223
- TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
224
- if (arena->pinned_objs == Qnil) {
225
- arena->pinned_objs = rb_ary_new();
226
- }
227
- rb_ary_push(arena->pinned_objs, obj);
228
- }
220
+ VALUE Arena_new() { return Arena_alloc(cArena); }
229
221
 
230
222
  void Arena_register(VALUE module) {
231
223
  VALUE internal = rb_define_module_under(module, "Internal");
@@ -239,164 +231,49 @@ void Arena_register(VALUE module) {
239
231
  // Object Cache
240
232
  // -----------------------------------------------------------------------------
241
233
 
242
- // A pointer -> Ruby Object cache that keeps references to Ruby wrapper
243
- // objects. This allows us to look up any Ruby wrapper object by the address
244
- // of the object it is wrapping. That way we can avoid ever creating two
245
- // different wrapper objects for the same C object, which saves memory and
246
- // preserves object identity.
247
- //
248
- // We use WeakMap for the cache. For Ruby <2.7 we also need a secondary Hash
249
- // to store WeakMap keys because Ruby <2.7 WeakMap doesn't allow non-finalizable
250
- // keys.
251
- //
252
- // We also need the secondary Hash if sizeof(long) < sizeof(VALUE), because this
253
- // means it may not be possible to fit a pointer into a Fixnum. Keys are
254
- // pointers, and if they fit into a Fixnum, Ruby doesn't collect them, but if
255
- // they overflow and require allocating a Bignum, they could get collected
256
- // prematurely, thus removing the cache entry. This happens on 64-bit Windows,
257
- // on which pointers are 64 bits but longs are 32 bits. In this case, we enable
258
- // the secondary Hash to hold the keys and prevent them from being collected.
259
-
260
- #if RUBY_API_VERSION_CODE >= 20700 && SIZEOF_LONG >= SIZEOF_VALUE
261
- #define USE_SECONDARY_MAP 0
262
- #else
263
- #define USE_SECONDARY_MAP 1
264
- #endif
265
-
266
- #if USE_SECONDARY_MAP
267
-
268
- // Maps Numeric -> Object. The object is then used as a key into the WeakMap.
269
- // This is needed for Ruby <2.7 where a number cannot be a key to WeakMap.
270
- // The object is used only for its identity; it does not contain any data.
271
- VALUE secondary_map = Qnil;
272
-
273
- // Mutations to the map are under a mutex, because SeconaryMap_MaybeGC()
274
- // iterates over the map which cannot happen in parallel with insertions, or
275
- // Ruby will throw:
276
- // can't add a new key into hash during iteration (RuntimeError)
277
- VALUE secondary_map_mutex = Qnil;
278
-
279
- // Lambda that will GC entries from the secondary map that are no longer present
280
- // in the primary map.
281
- VALUE gc_secondary_map_lambda = Qnil;
282
- ID length;
283
-
284
- extern VALUE weak_obj_cache;
285
-
286
- static void SecondaryMap_Init() {
287
- rb_gc_register_address(&secondary_map);
288
- rb_gc_register_address(&gc_secondary_map_lambda);
289
- rb_gc_register_address(&secondary_map_mutex);
290
- secondary_map = rb_hash_new();
291
- gc_secondary_map_lambda = rb_eval_string(
292
- "->(secondary, weak) {\n"
293
- " secondary.delete_if { |k, v| !weak.key?(v) }\n"
294
- "}\n");
295
- secondary_map_mutex = rb_mutex_new();
296
- length = rb_intern("length");
297
- }
298
-
299
- // The secondary map is a regular Hash, and will never shrink on its own.
300
- // The main object cache is a WeakMap that will automatically remove entries
301
- // when the target object is no longer reachable, but unless we manually
302
- // remove the corresponding entries from the secondary map, it will grow
303
- // without bound.
304
- //
305
- // To avoid this unbounded growth we periodically remove entries from the
306
- // secondary map that are no longer present in the WeakMap. The logic of
307
- // how often to perform this GC is an artbirary tuning parameter that
308
- // represents a straightforward CPU/memory tradeoff.
309
- //
310
- // Requires: secondary_map_mutex is held.
311
- static void SecondaryMap_MaybeGC() {
312
- PBRUBY_ASSERT(rb_mutex_locked_p(secondary_map_mutex) == Qtrue);
313
- size_t weak_len = NUM2ULL(rb_funcall(weak_obj_cache, length, 0));
314
- size_t secondary_len = RHASH_SIZE(secondary_map);
315
- if (secondary_len < weak_len) {
316
- // Logically this case should not be possible: a valid entry cannot exist in
317
- // the weak table unless there is a corresponding entry in the secondary
318
- // table. It should *always* be the case that secondary_len >= weak_len.
319
- //
320
- // However ObjectSpace::WeakMap#length (and therefore weak_len) is
321
- // unreliable: it overreports its true length by including non-live objects.
322
- // However these non-live objects are not yielded in iteration, so we may
323
- // have previously deleted them from the secondary map in a previous
324
- // invocation of SecondaryMap_MaybeGC().
325
- //
326
- // In this case, we can't measure any waste, so we just return.
327
- return;
328
- }
329
- size_t waste = secondary_len - weak_len;
330
- // GC if we could remove at least 2000 entries or 20% of the table size
331
- // (whichever is greater). Since the cost of the GC pass is O(N), we
332
- // want to make sure that we condition this on overall table size, to
333
- // avoid O(N^2) CPU costs.
334
- size_t threshold = PBRUBY_MAX(secondary_len * 0.2, 2000);
335
- if (waste > threshold) {
336
- rb_funcall(gc_secondary_map_lambda, rb_intern("call"), 2,
337
- secondary_map, weak_obj_cache);
338
- }
339
- }
340
-
341
- // Requires: secondary_map_mutex is held by this thread iff create == true.
342
- static VALUE SecondaryMap_Get(VALUE key, bool create) {
343
- PBRUBY_ASSERT(!create || rb_mutex_locked_p(secondary_map_mutex) == Qtrue);
344
- VALUE ret = rb_hash_lookup(secondary_map, key);
345
- if (ret == Qnil && create) {
346
- SecondaryMap_MaybeGC();
347
- ret = rb_class_new_instance(0, NULL, rb_cObject);
348
- rb_hash_aset(secondary_map, key, ret);
349
- }
350
- return ret;
351
- }
352
-
353
- #endif
354
-
355
- // Requires: secondary_map_mutex is held by this thread iff create == true.
356
- static VALUE ObjectCache_GetKey(const void* key, bool create) {
357
- VALUE key_val = (VALUE)key;
358
- PBRUBY_ASSERT((key_val & 3) == 0);
359
- VALUE ret = LL2NUM(key_val >> 2);
360
- #if USE_SECONDARY_MAP
361
- ret = SecondaryMap_Get(ret, create);
362
- #endif
363
- return ret;
364
- }
365
-
366
234
  // Public ObjectCache API.
367
235
 
368
236
  VALUE weak_obj_cache = Qnil;
369
237
  ID item_get;
370
- ID item_set;
238
+ ID item_try_add;
239
+
240
+ static void ObjectCache_Init(VALUE protobuf) {
241
+ item_get = rb_intern("get");
242
+ item_try_add = rb_intern("try_add");
371
243
 
372
- static void ObjectCache_Init() {
373
244
  rb_gc_register_address(&weak_obj_cache);
374
- VALUE klass = rb_eval_string("ObjectSpace::WeakMap");
375
- weak_obj_cache = rb_class_new_instance(0, NULL, klass);
376
- item_get = rb_intern("[]");
377
- item_set = rb_intern("[]=");
378
- #if USE_SECONDARY_MAP
379
- SecondaryMap_Init();
245
+ VALUE internal = rb_const_get(protobuf, rb_intern("Internal"));
246
+ #if SIZEOF_LONG >= SIZEOF_VALUE
247
+ VALUE cache_class = rb_const_get(internal, rb_intern("ObjectCache"));
248
+ #else
249
+ VALUE cache_class = rb_const_get(internal, rb_intern("LegacyObjectCache"));
380
250
  #endif
251
+
252
+ weak_obj_cache = rb_class_new_instance(0, NULL, cache_class);
253
+ rb_const_set(internal, rb_intern("OBJECT_CACHE"), weak_obj_cache);
254
+ rb_const_set(internal, rb_intern("SIZEOF_LONG"), INT2NUM(SIZEOF_LONG));
255
+ rb_const_set(internal, rb_intern("SIZEOF_VALUE"), INT2NUM(SIZEOF_VALUE));
381
256
  }
382
257
 
383
- void ObjectCache_Add(const void* key, VALUE val) {
384
- PBRUBY_ASSERT(ObjectCache_Get(key) == Qnil);
385
- #if USE_SECONDARY_MAP
386
- rb_mutex_lock(secondary_map_mutex);
387
- #endif
388
- VALUE key_rb = ObjectCache_GetKey(key, true);
389
- rb_funcall(weak_obj_cache, item_set, 2, key_rb, val);
390
- #if USE_SECONDARY_MAP
391
- rb_mutex_unlock(secondary_map_mutex);
392
- #endif
393
- PBRUBY_ASSERT(ObjectCache_Get(key) == val);
258
+ static VALUE ObjectCache_GetKey(const void *key) {
259
+ VALUE key_val = (VALUE)key;
260
+ PBRUBY_ASSERT((key_val & 3) == 0);
261
+ // Ensure the key can be stored as a Fixnum since 1 bit is needed for
262
+ // FIXNUM_FLAG and 1 bit is needed for the sign bit.
263
+ VALUE new_key = LL2NUM(key_val >> 2);
264
+ PBRUBY_ASSERT(FIXNUM_P(new_key));
265
+ return new_key;
266
+ }
267
+
268
+ VALUE ObjectCache_TryAdd(const void *key, VALUE val) {
269
+ VALUE key_val = ObjectCache_GetKey(key);
270
+ return rb_funcall(weak_obj_cache, item_try_add, 2, key_val, val);
394
271
  }
395
272
 
396
273
  // Returns the cached object for this key, if any. Otherwise returns Qnil.
397
- VALUE ObjectCache_Get(const void* key) {
398
- VALUE key_rb = ObjectCache_GetKey(key, false);
399
- return rb_funcall(weak_obj_cache, item_get, 1, key_rb);
274
+ VALUE ObjectCache_Get(const void *key) {
275
+ VALUE key_val = ObjectCache_GetKey(key);
276
+ return rb_funcall(weak_obj_cache, item_get, 1, key_val);
400
277
  }
401
278
 
402
279
  /*
@@ -407,9 +284,10 @@ VALUE ObjectCache_Get(const void* key) {
407
284
  * unknown fields in submessages.
408
285
  */
409
286
  static VALUE Google_Protobuf_discard_unknown(VALUE self, VALUE msg_rb) {
410
- const upb_msgdef *m;
411
- upb_msg *msg = Message_GetMutable(msg_rb, &m);
412
- if (!upb_msg_discardunknown(msg, m, 128)) {
287
+ const upb_MessageDef *m;
288
+ upb_Message *msg = Message_GetMutable(msg_rb, &m);
289
+ const upb_DefPool* ext_pool = upb_FileDef_Pool(upb_MessageDef_File(m));
290
+ if (!upb_Message_DiscardUnknown(msg, m, ext_pool, 128)) {
413
291
  rb_raise(rb_eRuntimeError, "Messages nested too deeply.");
414
292
  }
415
293
 
@@ -431,10 +309,10 @@ VALUE Google_Protobuf_deep_copy(VALUE self, VALUE obj) {
431
309
  return Map_deep_copy(obj);
432
310
  } else {
433
311
  VALUE new_arena_rb = Arena_new();
434
- upb_arena *new_arena = Arena_get(new_arena_rb);
435
- const upb_msgdef *m;
436
- const upb_msg *msg = Message_Get(obj, &m);
437
- upb_msg* new_msg = Message_deep_copy(msg, m, new_arena);
312
+ upb_Arena *new_arena = Arena_get(new_arena_rb);
313
+ const upb_MessageDef *m;
314
+ const upb_Message *msg = Message_Get(obj, &m);
315
+ upb_Message *new_msg = Message_deep_copy(msg, m, new_arena);
438
316
  return Message_GetRubyWrapper(new_msg, m, new_arena_rb);
439
317
  }
440
318
  }
@@ -445,13 +323,11 @@ VALUE Google_Protobuf_deep_copy(VALUE self, VALUE obj) {
445
323
 
446
324
  // This must be named "Init_protobuf_c" because the Ruby module is named
447
325
  // "protobuf_c" -- the VM looks for this symbol in our .so.
448
- __attribute__ ((visibility ("default")))
449
- void Init_protobuf_c() {
450
- ObjectCache_Init();
451
-
326
+ __attribute__((visibility("default"))) void Init_protobuf_c() {
452
327
  VALUE google = rb_define_module("Google");
453
328
  VALUE protobuf = rb_define_module_under(google, "Protobuf");
454
329
 
330
+ ObjectCache_Init(protobuf);
455
331
  Arena_register(protobuf);
456
332
  Defs_register(protobuf);
457
333
  RepeatedField_register(protobuf);
@@ -465,6 +341,17 @@ void Init_protobuf_c() {
465
341
 
466
342
  rb_define_singleton_method(protobuf, "discard_unknown",
467
343
  Google_Protobuf_discard_unknown, 1);
468
- rb_define_singleton_method(protobuf, "deep_copy",
469
- Google_Protobuf_deep_copy, 1);
344
+ rb_define_singleton_method(protobuf, "deep_copy", Google_Protobuf_deep_copy,
345
+ 1);
346
+ }
347
+
348
+ // -----------------------------------------------------------------------------
349
+ // Utilities
350
+ // -----------------------------------------------------------------------------
351
+
352
+ // Raises a Ruby error if val is frozen in Ruby or UPB.
353
+ void Protobuf_CheckNotFrozen(VALUE val, bool upb_frozen) {
354
+ if (RB_UNLIKELY(rb_obj_frozen_p(val)||upb_frozen)) {
355
+ rb_error_frozen_object(val);
356
+ }
470
357
  }