google-protobuf 3.25.4 → 4.27.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.
Potentially problematic release.
This version of google-protobuf might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/ext/google/protobuf_c/convert.c +7 -4
- data/ext/google/protobuf_c/defs.c +367 -28
- data/ext/google/protobuf_c/extconf.rb +1 -1
- data/ext/google/protobuf_c/glue.c +16 -0
- data/ext/google/protobuf_c/map.c +12 -19
- data/ext/google/protobuf_c/map.h +1 -1
- data/ext/google/protobuf_c/message.c +43 -90
- data/ext/google/protobuf_c/message.h +1 -5
- data/ext/google/protobuf_c/protobuf.c +19 -6
- data/ext/google/protobuf_c/repeated_field.c +6 -15
- data/ext/google/protobuf_c/repeated_field.h +1 -1
- data/ext/google/protobuf_c/ruby-upb.c +12684 -10768
- data/ext/google/protobuf_c/ruby-upb.h +6889 -5584
- data/ext/google/protobuf_c/shared_convert.c +10 -5
- data/ext/google/protobuf_c/shared_convert.h +2 -2
- data/ext/google/protobuf_c/shared_message.c +3 -31
- data/ext/google/protobuf_c/shared_message.h +0 -4
- data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.c +467 -0
- data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.h +9 -8
- data/lib/google/protobuf/any_pb.rb +1 -22
- data/lib/google/protobuf/api_pb.rb +1 -24
- data/lib/google/protobuf/descriptor_pb.rb +3 -23
- data/lib/google/protobuf/duration_pb.rb +1 -22
- data/lib/google/protobuf/empty_pb.rb +1 -22
- data/lib/google/protobuf/ffi/descriptor.rb +4 -3
- data/lib/google/protobuf/ffi/descriptor_pool.rb +3 -1
- data/lib/google/protobuf/ffi/enum_descriptor.rb +3 -1
- data/lib/google/protobuf/ffi/ffi.rb +3 -6
- data/lib/google/protobuf/ffi/field_descriptor.rb +13 -2
- data/lib/google/protobuf/ffi/file_descriptor.rb +3 -13
- data/lib/google/protobuf/ffi/internal/convert.rb +8 -24
- data/lib/google/protobuf/ffi/map.rb +13 -11
- data/lib/google/protobuf/ffi/message.rb +10 -13
- data/lib/google/protobuf/ffi/method_descriptor.rb +114 -0
- data/lib/google/protobuf/ffi/object_cache.rb +3 -3
- data/lib/google/protobuf/ffi/oneof_descriptor.rb +3 -1
- data/lib/google/protobuf/ffi/repeated_field.rb +12 -10
- data/lib/google/protobuf/ffi/service_descriptor.rb +107 -0
- data/lib/google/protobuf/field_mask_pb.rb +1 -22
- data/lib/google/protobuf/internal/object_cache.rb +99 -0
- data/lib/google/protobuf/plugin_pb.rb +2 -24
- data/lib/google/protobuf/repeated_field.rb +4 -5
- data/lib/google/protobuf/source_context_pb.rb +1 -22
- data/lib/google/protobuf/struct_pb.rb +1 -22
- data/lib/google/protobuf/timestamp_pb.rb +1 -22
- data/lib/google/protobuf/type_pb.rb +1 -24
- data/lib/google/protobuf/wrappers_pb.rb +1 -22
- data/lib/google/protobuf.rb +1 -1
- data/lib/google/protobuf_ffi.rb +3 -2
- data/lib/google/protobuf_native.rb +0 -1
- data/lib/google/tasks/ffi.rake +1 -3
- metadata +25 -12
- data/ext/google/protobuf_c/third_party/utf8_range/naive.c +0 -92
- data/ext/google/protobuf_c/third_party/utf8_range/range2-neon.c +0 -157
- data/ext/google/protobuf_c/third_party/utf8_range/range2-sse.c +0 -170
- data/lib/google/protobuf/descriptor_dsl.rb +0 -465
- data/lib/google/protobuf/object_cache.rb +0 -97
@@ -1,465 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
#
|
3
|
-
# Code that implements the DSL for defining proto messages.
|
4
|
-
|
5
|
-
# Suppress warning: loading in progress, circular require considered harmful.
|
6
|
-
# This circular require is intentional to avoid missing dependency.
|
7
|
-
begin
|
8
|
-
old_verbose, $VERBOSE = $VERBOSE, nil
|
9
|
-
require 'google/protobuf/descriptor_pb'
|
10
|
-
ensure
|
11
|
-
$VERBOSE = old_verbose
|
12
|
-
end
|
13
|
-
|
14
|
-
module Google
|
15
|
-
module Protobuf
|
16
|
-
module Internal
|
17
|
-
class AtomicCounter
|
18
|
-
def initialize
|
19
|
-
@n = 0
|
20
|
-
@mu = Mutex.new
|
21
|
-
end
|
22
|
-
|
23
|
-
def get_and_increment
|
24
|
-
n = @n
|
25
|
-
@mu.synchronize {
|
26
|
-
@n += 1
|
27
|
-
}
|
28
|
-
return n
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class Builder
|
33
|
-
@@file_number = AtomicCounter.new
|
34
|
-
|
35
|
-
def initialize(pool)
|
36
|
-
@pool = pool
|
37
|
-
@default_file = nil # Constructed lazily
|
38
|
-
end
|
39
|
-
|
40
|
-
def add_file(name, options={}, &block)
|
41
|
-
builder = FileBuilder.new(@pool, name, options)
|
42
|
-
builder.instance_eval(&block)
|
43
|
-
internal_add_file(builder)
|
44
|
-
end
|
45
|
-
|
46
|
-
def add_message(name, &block)
|
47
|
-
internal_default_file.add_message(name, &block)
|
48
|
-
end
|
49
|
-
|
50
|
-
def add_enum(name, &block)
|
51
|
-
internal_default_file.add_enum(name, &block)
|
52
|
-
end
|
53
|
-
|
54
|
-
# ---- Internal methods, not part of the DSL ----
|
55
|
-
|
56
|
-
def build
|
57
|
-
if @default_file
|
58
|
-
internal_add_file(@default_file)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
private def internal_add_file(file_builder)
|
63
|
-
proto = file_builder.build
|
64
|
-
serialized = Google::Protobuf::FileDescriptorProto.encode(proto)
|
65
|
-
@pool.add_serialized_file(serialized)
|
66
|
-
end
|
67
|
-
|
68
|
-
private def internal_default_file
|
69
|
-
number = @@file_number.get_and_increment
|
70
|
-
filename = "ruby_default_file#{number}.proto"
|
71
|
-
@default_file ||= FileBuilder.new(@pool, filename)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
class FileBuilder
|
76
|
-
def initialize(pool, name, options={})
|
77
|
-
@pool = pool
|
78
|
-
@file_proto = Google::Protobuf::FileDescriptorProto.new(
|
79
|
-
name: name,
|
80
|
-
syntax: options.fetch(:syntax, "proto3")
|
81
|
-
)
|
82
|
-
end
|
83
|
-
|
84
|
-
def add_message(name, &block)
|
85
|
-
builder = MessageBuilder.new(name, self, @file_proto)
|
86
|
-
builder.instance_eval(&block)
|
87
|
-
builder.internal_add_synthetic_oneofs
|
88
|
-
end
|
89
|
-
|
90
|
-
def add_enum(name, &block)
|
91
|
-
EnumBuilder.new(name, @file_proto).instance_eval(&block)
|
92
|
-
end
|
93
|
-
|
94
|
-
# ---- Internal methods, not part of the DSL ----
|
95
|
-
|
96
|
-
# These methods fix up the file descriptor to account for differences
|
97
|
-
# between the DSL and FileDescriptorProto.
|
98
|
-
|
99
|
-
# The DSL can omit a package name; here we infer what the package is if
|
100
|
-
# was not specified.
|
101
|
-
def infer_package(names)
|
102
|
-
# Package is longest common prefix ending in '.', if any.
|
103
|
-
if not names.empty?
|
104
|
-
min, max = names.minmax
|
105
|
-
last_common_dot = nil
|
106
|
-
min.size.times { |i|
|
107
|
-
if min[i] != max[i] then break end
|
108
|
-
if min[i] == "." then last_common_dot = i end
|
109
|
-
}
|
110
|
-
if last_common_dot
|
111
|
-
return min.slice(0, last_common_dot)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
nil
|
116
|
-
end
|
117
|
-
|
118
|
-
def rewrite_enum_default(field)
|
119
|
-
if field.type != :TYPE_ENUM or !field.has_default_value? or !field.has_type_name?
|
120
|
-
return
|
121
|
-
end
|
122
|
-
|
123
|
-
value = field.default_value
|
124
|
-
type_name = field.type_name
|
125
|
-
|
126
|
-
if value.empty? or value[0].ord < "0".ord or value[0].ord > "9".ord
|
127
|
-
return
|
128
|
-
end
|
129
|
-
|
130
|
-
if type_name.empty? || type_name[0] != "."
|
131
|
-
return
|
132
|
-
end
|
133
|
-
|
134
|
-
type_name = type_name[1..-1]
|
135
|
-
as_int = Integer(value) rescue return
|
136
|
-
|
137
|
-
enum_desc = @pool.lookup(type_name)
|
138
|
-
if enum_desc.is_a?(Google::Protobuf::EnumDescriptor)
|
139
|
-
# Enum was defined in a previous file.
|
140
|
-
name = enum_desc.lookup_value(as_int)
|
141
|
-
if name
|
142
|
-
# Update the default value in the proto.
|
143
|
-
field.default_value = name
|
144
|
-
end
|
145
|
-
else
|
146
|
-
# See if enum was defined in this file.
|
147
|
-
@file_proto.enum_type.each { |enum_proto|
|
148
|
-
if enum_proto.name == type_name
|
149
|
-
enum_proto.value.each { |enum_value_proto|
|
150
|
-
if enum_value_proto.number == as_int
|
151
|
-
# Update the default value in the proto.
|
152
|
-
field.default_value = enum_value_proto.name
|
153
|
-
return
|
154
|
-
end
|
155
|
-
}
|
156
|
-
# We found the right enum, but no value matched.
|
157
|
-
return
|
158
|
-
end
|
159
|
-
}
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
# Historically we allowed enum defaults to be specified as a number.
|
164
|
-
# In retrospect this was a mistake as descriptors require defaults to
|
165
|
-
# be specified as a label. This can make a difference if multiple
|
166
|
-
# labels have the same number.
|
167
|
-
#
|
168
|
-
# Here we do a pass over all enum defaults and rewrite numeric defaults
|
169
|
-
# by looking up their labels. This is complicated by the fact that the
|
170
|
-
# enum definition can live in either the symtab or the file_proto.
|
171
|
-
#
|
172
|
-
# We take advantage of the fact that this is called *before* enums or
|
173
|
-
# messages are nested in other messages, so we only have to iterate
|
174
|
-
# one level deep.
|
175
|
-
def rewrite_enum_defaults
|
176
|
-
@file_proto.message_type.each { |msg|
|
177
|
-
msg.field.each { |field|
|
178
|
-
rewrite_enum_default(field)
|
179
|
-
}
|
180
|
-
}
|
181
|
-
end
|
182
|
-
|
183
|
-
# We have to do some relatively complicated logic here for backward
|
184
|
-
# compatibility.
|
185
|
-
#
|
186
|
-
# In descriptor.proto, messages are nested inside other messages if that is
|
187
|
-
# what the original .proto file looks like. For example, suppose we have this
|
188
|
-
# foo.proto:
|
189
|
-
#
|
190
|
-
# package foo;
|
191
|
-
# message Bar {
|
192
|
-
# message Baz {}
|
193
|
-
# }
|
194
|
-
#
|
195
|
-
# The descriptor for this must look like this:
|
196
|
-
#
|
197
|
-
# file {
|
198
|
-
# name: "test.proto"
|
199
|
-
# package: "foo"
|
200
|
-
# message_type {
|
201
|
-
# name: "Bar"
|
202
|
-
# nested_type {
|
203
|
-
# name: "Baz"
|
204
|
-
# }
|
205
|
-
# }
|
206
|
-
# }
|
207
|
-
#
|
208
|
-
# However, the Ruby generated code has always generated messages in a flat,
|
209
|
-
# non-nested way:
|
210
|
-
#
|
211
|
-
# Google::Protobuf::DescriptorPool.generated_pool.build do
|
212
|
-
# add_message "foo.Bar" do
|
213
|
-
# end
|
214
|
-
# add_message "foo.Bar.Baz" do
|
215
|
-
# end
|
216
|
-
# end
|
217
|
-
#
|
218
|
-
# Here we need to do a translation where we turn this generated code into the
|
219
|
-
# above descriptor. We need to infer that "foo" is the package name, and not
|
220
|
-
# a message itself. */
|
221
|
-
|
222
|
-
def split_parent_name(msg_or_enum)
|
223
|
-
name = msg_or_enum.name
|
224
|
-
idx = name.rindex(?.)
|
225
|
-
if idx
|
226
|
-
return name[0...idx], name[idx+1..-1]
|
227
|
-
else
|
228
|
-
return nil, name
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
def get_parent_msg(msgs_by_name, name, parent_name)
|
233
|
-
parent_msg = msgs_by_name[parent_name]
|
234
|
-
if parent_msg.nil?
|
235
|
-
raise "To define name #{name}, there must be a message named #{parent_name} to enclose it"
|
236
|
-
end
|
237
|
-
return parent_msg
|
238
|
-
end
|
239
|
-
|
240
|
-
def fix_nesting
|
241
|
-
# Calculate and update package.
|
242
|
-
msgs_by_name = @file_proto.message_type.map { |msg| [msg.name, msg] }.to_h
|
243
|
-
enum_names = @file_proto.enum_type.map { |enum_proto| enum_proto.name }
|
244
|
-
|
245
|
-
package = infer_package(msgs_by_name.keys + enum_names)
|
246
|
-
if package
|
247
|
-
@file_proto.package = package
|
248
|
-
end
|
249
|
-
|
250
|
-
# Update nesting based on package.
|
251
|
-
final_msgs = Google::Protobuf::RepeatedField.new(:message, Google::Protobuf::DescriptorProto)
|
252
|
-
final_enums = Google::Protobuf::RepeatedField.new(:message, Google::Protobuf::EnumDescriptorProto)
|
253
|
-
|
254
|
-
# Note: We don't iterate over msgs_by_name.values because we want to
|
255
|
-
# preserve order as listed in the DSL.
|
256
|
-
@file_proto.message_type.each { |msg|
|
257
|
-
parent_name, msg.name = split_parent_name(msg)
|
258
|
-
if parent_name == package
|
259
|
-
final_msgs << msg
|
260
|
-
else
|
261
|
-
get_parent_msg(msgs_by_name, msg.name, parent_name).nested_type << msg
|
262
|
-
end
|
263
|
-
}
|
264
|
-
|
265
|
-
@file_proto.enum_type.each { |enum|
|
266
|
-
parent_name, enum.name = split_parent_name(enum)
|
267
|
-
if parent_name == package
|
268
|
-
final_enums << enum
|
269
|
-
else
|
270
|
-
get_parent_msg(msgs_by_name, enum.name, parent_name).enum_type << enum
|
271
|
-
end
|
272
|
-
}
|
273
|
-
|
274
|
-
@file_proto.message_type = final_msgs
|
275
|
-
@file_proto.enum_type = final_enums
|
276
|
-
end
|
277
|
-
|
278
|
-
def internal_file_proto
|
279
|
-
@file_proto
|
280
|
-
end
|
281
|
-
|
282
|
-
def build
|
283
|
-
rewrite_enum_defaults
|
284
|
-
fix_nesting
|
285
|
-
return @file_proto
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
|
-
class MessageBuilder
|
290
|
-
def initialize(name, file_builder, file_proto)
|
291
|
-
@file_builder = file_builder
|
292
|
-
@msg_proto = Google::Protobuf::DescriptorProto.new(
|
293
|
-
:name => name
|
294
|
-
)
|
295
|
-
file_proto.message_type << @msg_proto
|
296
|
-
end
|
297
|
-
|
298
|
-
def optional(name, type, number, type_class=nil, options=nil)
|
299
|
-
internal_add_field(:LABEL_OPTIONAL, name, type, number, type_class, options)
|
300
|
-
end
|
301
|
-
|
302
|
-
def proto3_optional(name, type, number, type_class=nil, options=nil)
|
303
|
-
internal_add_field(:LABEL_OPTIONAL, name, type, number, type_class, options,
|
304
|
-
proto3_optional: true)
|
305
|
-
end
|
306
|
-
|
307
|
-
def required(name, type, number, type_class=nil, options=nil)
|
308
|
-
internal_add_field(:LABEL_REQUIRED, name, type, number, type_class, options)
|
309
|
-
end
|
310
|
-
|
311
|
-
def repeated(name, type, number, type_class = nil, options=nil)
|
312
|
-
internal_add_field(:LABEL_REPEATED, name, type, number, type_class, options)
|
313
|
-
end
|
314
|
-
|
315
|
-
def oneof(name, &block)
|
316
|
-
OneofBuilder.new(name, self).instance_eval(&block)
|
317
|
-
end
|
318
|
-
|
319
|
-
# Defines a new map field on this message type with the given key and
|
320
|
-
# value types, tag number, and type class (for message and enum value
|
321
|
-
# types). The key type must be :int32/:uint32/:int64/:uint64, :bool, or
|
322
|
-
# :string. The value type type must be a Ruby symbol (as accepted by
|
323
|
-
# FieldDescriptor#type=) and the type_class must be a string, if
|
324
|
-
# present (as accepted by FieldDescriptor#submsg_name=).
|
325
|
-
def map(name, key_type, value_type, number, value_type_class = nil)
|
326
|
-
if key_type == :float or key_type == :double or key_type == :enum or
|
327
|
-
key_type == :message
|
328
|
-
raise ArgError, "Not an acceptable key type: " + key_type
|
329
|
-
end
|
330
|
-
entry_name = "#{@msg_proto.name}_MapEntry_#{name}"
|
331
|
-
|
332
|
-
@file_builder.add_message entry_name do
|
333
|
-
optional :key, key_type, 1
|
334
|
-
optional :value, value_type, 2, value_type_class
|
335
|
-
end
|
336
|
-
options = @file_builder.internal_file_proto.message_type.last.options ||= MessageOptions.new
|
337
|
-
options.map_entry = true
|
338
|
-
repeated name, :message, number, entry_name
|
339
|
-
end
|
340
|
-
|
341
|
-
# ---- Internal methods, not part of the DSL ----
|
342
|
-
|
343
|
-
def internal_add_synthetic_oneofs
|
344
|
-
# We have to build a set of all names, to ensure that synthetic oneofs
|
345
|
-
# are not creating conflicts
|
346
|
-
names = {}
|
347
|
-
@msg_proto.field.each { |field| names[field.name] = true }
|
348
|
-
@msg_proto.oneof_decl.each { |oneof| names[oneof.name] = true }
|
349
|
-
|
350
|
-
@msg_proto.field.each { |field|
|
351
|
-
if field.proto3_optional
|
352
|
-
# Prepend '_' until we are no longer conflicting.
|
353
|
-
oneof_name = field.name
|
354
|
-
while names[oneof_name]
|
355
|
-
oneof_name = "_" + oneof_name
|
356
|
-
end
|
357
|
-
names[oneof_name] = true
|
358
|
-
field.oneof_index = @msg_proto.oneof_decl.size
|
359
|
-
@msg_proto.oneof_decl << Google::Protobuf::OneofDescriptorProto.new(
|
360
|
-
name: oneof_name
|
361
|
-
)
|
362
|
-
end
|
363
|
-
}
|
364
|
-
end
|
365
|
-
|
366
|
-
def internal_add_field(label, name, type, number, type_class, options,
|
367
|
-
oneof_index: nil, proto3_optional: false)
|
368
|
-
# Allow passing either:
|
369
|
-
# - (name, type, number, options) or
|
370
|
-
# - (name, type, number, type_class, options)
|
371
|
-
if options.nil? and type_class.instance_of?(Hash)
|
372
|
-
options = type_class;
|
373
|
-
type_class = nil;
|
374
|
-
end
|
375
|
-
|
376
|
-
field_proto = Google::Protobuf::FieldDescriptorProto.new(
|
377
|
-
:label => label,
|
378
|
-
:name => name,
|
379
|
-
:type => ("TYPE_" + type.to_s.upcase).to_sym,
|
380
|
-
:number => number
|
381
|
-
)
|
382
|
-
|
383
|
-
if type_class
|
384
|
-
# Make it an absolute type name by prepending a dot.
|
385
|
-
field_proto.type_name = "." + type_class
|
386
|
-
end
|
387
|
-
|
388
|
-
if oneof_index
|
389
|
-
field_proto.oneof_index = oneof_index
|
390
|
-
end
|
391
|
-
|
392
|
-
if proto3_optional
|
393
|
-
field_proto.proto3_optional = true
|
394
|
-
end
|
395
|
-
|
396
|
-
if options
|
397
|
-
if options.key?(:default)
|
398
|
-
default = options[:default]
|
399
|
-
if !default.instance_of?(String)
|
400
|
-
# Call #to_s since all defaults are strings in the descriptor.
|
401
|
-
default = default.to_s
|
402
|
-
end
|
403
|
-
# XXX: we should be C-escaping bytes defaults.
|
404
|
-
field_proto.default_value = default.dup.force_encoding("UTF-8")
|
405
|
-
end
|
406
|
-
if options.key?(:json_name)
|
407
|
-
field_proto.json_name = options[:json_name]
|
408
|
-
end
|
409
|
-
end
|
410
|
-
|
411
|
-
@msg_proto.field << field_proto
|
412
|
-
end
|
413
|
-
|
414
|
-
def internal_msg_proto
|
415
|
-
@msg_proto
|
416
|
-
end
|
417
|
-
end
|
418
|
-
|
419
|
-
class OneofBuilder
|
420
|
-
def initialize(name, msg_builder)
|
421
|
-
@msg_builder = msg_builder
|
422
|
-
oneof_proto = Google::Protobuf::OneofDescriptorProto.new(
|
423
|
-
:name => name
|
424
|
-
)
|
425
|
-
msg_proto = msg_builder.internal_msg_proto
|
426
|
-
@oneof_index = msg_proto.oneof_decl.size
|
427
|
-
msg_proto.oneof_decl << oneof_proto
|
428
|
-
end
|
429
|
-
|
430
|
-
def optional(name, type, number, type_class=nil, options=nil)
|
431
|
-
@msg_builder.internal_add_field(
|
432
|
-
:LABEL_OPTIONAL, name, type, number, type_class, options,
|
433
|
-
oneof_index: @oneof_index)
|
434
|
-
end
|
435
|
-
end
|
436
|
-
|
437
|
-
class EnumBuilder
|
438
|
-
def initialize(name, file_proto)
|
439
|
-
@enum_proto = Google::Protobuf::EnumDescriptorProto.new(
|
440
|
-
:name => name
|
441
|
-
)
|
442
|
-
file_proto.enum_type << @enum_proto
|
443
|
-
end
|
444
|
-
|
445
|
-
def value(name, number)
|
446
|
-
enum_value_proto = Google::Protobuf::EnumValueDescriptorProto.new(
|
447
|
-
name: name,
|
448
|
-
number: number
|
449
|
-
)
|
450
|
-
@enum_proto.value << enum_value_proto
|
451
|
-
end
|
452
|
-
end
|
453
|
-
|
454
|
-
end
|
455
|
-
|
456
|
-
# Re-open the class (the rest of the class is implemented in C)
|
457
|
-
class DescriptorPool
|
458
|
-
def build(&block)
|
459
|
-
builder = Internal::Builder.new(self)
|
460
|
-
builder.instance_eval(&block)
|
461
|
-
builder.build
|
462
|
-
end
|
463
|
-
end
|
464
|
-
end
|
465
|
-
end
|
@@ -1,97 +0,0 @@
|
|
1
|
-
# Protocol Buffers - Google's data interchange format
|
2
|
-
# Copyright 2023 Google Inc. All rights reserved.
|
3
|
-
#
|
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
|
7
|
-
|
8
|
-
module Google
|
9
|
-
module Protobuf
|
10
|
-
# A pointer -> Ruby Object cache that keeps references to Ruby wrapper
|
11
|
-
# objects. This allows us to look up any Ruby wrapper object by the address
|
12
|
-
# of the object it is wrapping. That way we can avoid ever creating two
|
13
|
-
# different wrapper objects for the same C object, which saves memory and
|
14
|
-
# preserves object identity.
|
15
|
-
#
|
16
|
-
# We use WeakMap for the cache. If sizeof(long) > sizeof(VALUE), we also
|
17
|
-
# need a secondary Hash to store WeakMap keys, because our pointer keys may
|
18
|
-
# need to be stored as Bignum instead of Fixnum. Since WeakMap is weak for
|
19
|
-
# both keys and values, a Bignum key will cause the WeakMap entry to be
|
20
|
-
# collected immediately unless there is another reference to the Bignum.
|
21
|
-
# This happens on 64-bit Windows, on which pointers are 64 bits but longs
|
22
|
-
# are 32 bits. In this case, we enable the secondary Hash to hold the keys
|
23
|
-
# and prevent them from being collected.
|
24
|
-
class ObjectCache
|
25
|
-
def initialize
|
26
|
-
@map = ObjectSpace::WeakMap.new
|
27
|
-
@mutex = Mutex.new
|
28
|
-
end
|
29
|
-
|
30
|
-
def get(key)
|
31
|
-
@map[key]
|
32
|
-
end
|
33
|
-
|
34
|
-
def try_add(key, value)
|
35
|
-
@map[key] || @mutex.synchronize do
|
36
|
-
@map[key] ||= value
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
class LegacyObjectCache
|
42
|
-
def initialize
|
43
|
-
@secondary_map = {}
|
44
|
-
@map = ObjectSpace::WeakMap.new
|
45
|
-
@mutex = Mutex.new
|
46
|
-
end
|
47
|
-
|
48
|
-
def get(key)
|
49
|
-
value = if secondary_key = @secondary_map[key]
|
50
|
-
@map[secondary_key]
|
51
|
-
else
|
52
|
-
@mutex.synchronize do
|
53
|
-
@map[(@secondary_map[key] ||= Object.new)]
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
# GC if we could remove at least 2000 entries or 20% of the table size
|
58
|
-
# (whichever is greater). Since the cost of the GC pass is O(N), we
|
59
|
-
# want to make sure that we condition this on overall table size, to
|
60
|
-
# avoid O(N^2) CPU costs.
|
61
|
-
cutoff = (@secondary_map.size * 0.2).ceil
|
62
|
-
cutoff = 2_000 if cutoff < 2_000
|
63
|
-
if (@secondary_map.size - @map.size) > cutoff
|
64
|
-
purge
|
65
|
-
end
|
66
|
-
|
67
|
-
value
|
68
|
-
end
|
69
|
-
|
70
|
-
def try_add(key, value)
|
71
|
-
if secondary_key = @secondary_map[key]
|
72
|
-
if old_value = @map[secondary_key]
|
73
|
-
return old_value
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
@mutex.synchronize do
|
78
|
-
secondary_key ||= (@secondary_map[key] ||= Object.new)
|
79
|
-
@map[secondary_key] ||= value
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
private
|
84
|
-
|
85
|
-
def purge
|
86
|
-
@mutex.synchronize do
|
87
|
-
@secondary_map.each do |key, secondary_key|
|
88
|
-
unless @map.key?(secondary_key)
|
89
|
-
@secondary_map.delete(key)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
nil
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|