This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,61 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2008 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
# require mixins before we hook them into the java & c code
|
||||
require 'google/protobuf/message_exts'
|
||||
require 'google/protobuf/internal/object_cache'
|
||||
|
||||
# We define these before requiring the platform-specific modules.
|
||||
# That way the module init can grab references to these.
|
||||
module Google
|
||||
module Protobuf
|
||||
class Error < StandardError; end
|
||||
class ParseError < Error; end
|
||||
class TypeError < ::TypeError; end
|
||||
|
||||
PREFER_FFI = case ENV['PROTOCOL_BUFFERS_RUBY_IMPLEMENTATION']
|
||||
when nil, "", /^native$/i
|
||||
false
|
||||
when /^ffi$/i
|
||||
true
|
||||
else
|
||||
warn "Unexpected value `#{ENV['PROTOCOL_BUFFERS_RUBY_IMPLEMENTATION']}` for environment variable `PROTOCOL_BUFFERS_RUBY_IMPLEMENTATION`. Should be either \"FFI\", \"NATIVE\"."
|
||||
false
|
||||
end
|
||||
|
||||
def self.encode(msg, options = {})
|
||||
msg.to_proto(options)
|
||||
end
|
||||
|
||||
def self.encode_json(msg, options = {})
|
||||
msg.to_json(options)
|
||||
end
|
||||
|
||||
def self.decode(klass, proto, options = {})
|
||||
klass.decode(proto, options)
|
||||
end
|
||||
|
||||
def self.decode_json(klass, json, options = {})
|
||||
klass.decode_json(json, options)
|
||||
end
|
||||
|
||||
IMPLEMENTATION = if PREFER_FFI
|
||||
begin
|
||||
require 'google/protobuf_ffi'
|
||||
:FFI
|
||||
rescue LoadError
|
||||
warn "Caught exception `#{$!.message}` while loading FFI implementation of google/protobuf."
|
||||
warn "Falling back to native implementation."
|
||||
require 'google/protobuf_native'
|
||||
:NATIVE
|
||||
end
|
||||
else
|
||||
require 'google/protobuf_native'
|
||||
:NATIVE
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: google/protobuf/any.proto
|
||||
|
||||
require 'google/protobuf'
|
||||
|
||||
|
||||
descriptor_data = "\n\x19google/protobuf/any.proto\x12\x0fgoogle.protobuf\"&\n\x03\x41ny\x12\x10\n\x08type_url\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\x42v\n\x13\x63om.google.protobufB\x08\x41nyProtoP\x01Z,google.golang.org/protobuf/types/known/anypb\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3"
|
||||
|
||||
pool = Google::Protobuf::DescriptorPool.generated_pool
|
||||
pool.add_serialized_file(descriptor_data)
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
Any = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Any").msgclass
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: google/protobuf/api.proto
|
||||
|
||||
require 'google/protobuf'
|
||||
|
||||
require 'google/protobuf/source_context_pb'
|
||||
require 'google/protobuf/type_pb'
|
||||
|
||||
|
||||
descriptor_data = "\n\x19google/protobuf/api.proto\x12\x0fgoogle.protobuf\x1a$google/protobuf/source_context.proto\x1a\x1agoogle/protobuf/type.proto\"\x81\x02\n\x03\x41pi\x12\x0c\n\x04name\x18\x01 \x01(\t\x12(\n\x07methods\x18\x02 \x03(\x0b\x32\x17.google.protobuf.Method\x12(\n\x07options\x18\x03 \x03(\x0b\x32\x17.google.protobuf.Option\x12\x0f\n\x07version\x18\x04 \x01(\t\x12\x36\n\x0esource_context\x18\x05 \x01(\x0b\x32\x1e.google.protobuf.SourceContext\x12&\n\x06mixins\x18\x06 \x03(\x0b\x32\x16.google.protobuf.Mixin\x12\'\n\x06syntax\x18\x07 \x01(\x0e\x32\x17.google.protobuf.Syntax\"\xd5\x01\n\x06Method\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x18\n\x10request_type_url\x18\x02 \x01(\t\x12\x19\n\x11request_streaming\x18\x03 \x01(\x08\x12\x19\n\x11response_type_url\x18\x04 \x01(\t\x12\x1a\n\x12response_streaming\x18\x05 \x01(\x08\x12(\n\x07options\x18\x06 \x03(\x0b\x32\x17.google.protobuf.Option\x12\'\n\x06syntax\x18\x07 \x01(\x0e\x32\x17.google.protobuf.Syntax\"#\n\x05Mixin\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04root\x18\x02 \x01(\tBv\n\x13\x63om.google.protobufB\x08\x41piProtoP\x01Z,google.golang.org/protobuf/types/known/apipb\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3"
|
||||
|
||||
pool = Google::Protobuf::DescriptorPool.generated_pool
|
||||
pool.add_serialized_file(descriptor_data)
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
Api = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Api").msgclass
|
||||
Method = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Method").msgclass
|
||||
Mixin = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Mixin").msgclass
|
||||
end
|
||||
end
|
||||
+66
File diff suppressed because one or more lines are too long
+17
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: google/protobuf/duration.proto
|
||||
|
||||
require 'google/protobuf'
|
||||
|
||||
|
||||
descriptor_data = "\n\x1egoogle/protobuf/duration.proto\x12\x0fgoogle.protobuf\"*\n\x08\x44uration\x12\x0f\n\x07seconds\x18\x01 \x01(\x03\x12\r\n\x05nanos\x18\x02 \x01(\x05\x42\x83\x01\n\x13\x63om.google.protobufB\rDurationProtoP\x01Z1google.golang.org/protobuf/types/known/durationpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3"
|
||||
|
||||
pool = Google::Protobuf::DescriptorPool.generated_pool
|
||||
pool.add_serialized_file(descriptor_data)
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
Duration = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Duration").msgclass
|
||||
end
|
||||
end
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: google/protobuf/empty.proto
|
||||
|
||||
require 'google/protobuf'
|
||||
|
||||
|
||||
descriptor_data = "\n\x1bgoogle/protobuf/empty.proto\x12\x0fgoogle.protobuf\"\x07\n\x05\x45mptyB}\n\x13\x63om.google.protobufB\nEmptyProtoP\x01Z.google.golang.org/protobuf/types/known/emptypb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3"
|
||||
|
||||
pool = Google::Protobuf::DescriptorPool.generated_pool
|
||||
pool.add_serialized_file(descriptor_data)
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
Empty = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Empty").msgclass
|
||||
end
|
||||
end
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2022 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
##
|
||||
# Message Descriptor - Descriptor for short.
|
||||
class Descriptor
|
||||
attr :descriptor_pool, :msg_class
|
||||
include Enumerable
|
||||
|
||||
# FFI Interface methods and setup
|
||||
extend ::FFI::DataConverter
|
||||
native_type ::FFI::Type::POINTER
|
||||
|
||||
class << self
|
||||
prepend Google::Protobuf::Internal::TypeSafety
|
||||
include Google::Protobuf::Internal::PointerHelper
|
||||
|
||||
# @param value [Descriptor] Descriptor to convert to an FFI native type
|
||||
# @param _ [Object] Unused
|
||||
def to_native(value, _ = nil)
|
||||
msg_def_ptr = value.nil? ? nil : value.instance_variable_get(:@msg_def)
|
||||
return ::FFI::Pointer::NULL if msg_def_ptr.nil?
|
||||
raise "Underlying msg_def was null!" if msg_def_ptr.null?
|
||||
msg_def_ptr
|
||||
end
|
||||
|
||||
##
|
||||
# @param msg_def [::FFI::Pointer] MsgDef pointer to be wrapped
|
||||
# @param _ [Object] Unused
|
||||
def from_native(msg_def, _ = nil)
|
||||
return nil if msg_def.nil? or msg_def.null?
|
||||
file_def = Google::Protobuf::FFI.get_message_file_def msg_def
|
||||
descriptor_from_file_def(file_def, msg_def)
|
||||
end
|
||||
end
|
||||
|
||||
def to_native
|
||||
self.class.to_native(self)
|
||||
end
|
||||
|
||||
##
|
||||
# Great write up of this strategy:
|
||||
# See https://blog.appsignal.com/2018/08/07/ruby-magic-changing-the-way-ruby-creates-objects.html
|
||||
def self.new(*arguments, &block)
|
||||
raise "Descriptor objects may not be created from Ruby."
|
||||
end
|
||||
|
||||
def to_s
|
||||
inspect
|
||||
end
|
||||
|
||||
def inspect
|
||||
"Descriptor - (not the message class) #{name}"
|
||||
end
|
||||
|
||||
def file_descriptor
|
||||
@descriptor_pool.send(:get_file_descriptor, Google::Protobuf::FFI.get_message_file_def(@msg_def))
|
||||
end
|
||||
|
||||
def name
|
||||
@name ||= Google::Protobuf::FFI.get_message_fullname(self)
|
||||
end
|
||||
|
||||
def each_oneof &block
|
||||
n = Google::Protobuf::FFI.oneof_count(self)
|
||||
0.upto(n-1) do |i|
|
||||
yield(Google::Protobuf::FFI.get_oneof_by_index(self, i))
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def each &block
|
||||
n = Google::Protobuf::FFI.field_count(self)
|
||||
0.upto(n-1) do |i|
|
||||
yield(Google::Protobuf::FFI.get_field_by_index(self, i))
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def lookup(name)
|
||||
Google::Protobuf::FFI.get_field_by_name(self, name, name.size)
|
||||
end
|
||||
|
||||
def lookup_oneof(name)
|
||||
Google::Protobuf::FFI.get_oneof_by_name(self, name, name.size)
|
||||
end
|
||||
|
||||
def msgclass
|
||||
@msg_class ||= build_message_class
|
||||
end
|
||||
|
||||
def options
|
||||
@options ||= begin
|
||||
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
temporary_arena = Google::Protobuf::FFI.create_arena
|
||||
buffer = Google::Protobuf::FFI.message_options(self, size_ptr, temporary_arena)
|
||||
opts = Google::Protobuf::MessageOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze)
|
||||
opts.clear_features()
|
||||
opts.freeze
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
extend Google::Protobuf::Internal::Convert
|
||||
|
||||
def initialize(msg_def, descriptor_pool)
|
||||
@msg_def = msg_def
|
||||
@msg_class = nil
|
||||
@descriptor_pool = descriptor_pool
|
||||
end
|
||||
|
||||
def self.private_constructor(msg_def, descriptor_pool)
|
||||
instance = allocate
|
||||
instance.send(:initialize, msg_def, descriptor_pool)
|
||||
instance
|
||||
end
|
||||
|
||||
def wrapper?
|
||||
if defined? @wrapper
|
||||
@wrapper
|
||||
else
|
||||
@wrapper = case Google::Protobuf::FFI.get_well_known_type self
|
||||
when :DoubleValue, :FloatValue, :Int64Value, :UInt64Value, :Int32Value, :UInt32Value, :StringValue, :BytesValue, :BoolValue
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_message(msg, descriptor, arena)
|
||||
return nil if msg.nil? or msg.null?
|
||||
message = OBJECT_CACHE.get(msg.address)
|
||||
if message.nil?
|
||||
message = descriptor.msgclass.send(:private_constructor, arena, msg: msg)
|
||||
end
|
||||
message
|
||||
end
|
||||
|
||||
def pool
|
||||
@descriptor_pool
|
||||
end
|
||||
end
|
||||
|
||||
class FFI
|
||||
# MessageDef
|
||||
attach_function :new_message_from_def, :upb_Message_New, [MiniTable.by_ref, Internal::Arena], :Message
|
||||
attach_function :field_count, :upb_MessageDef_FieldCount, [Descriptor], :int
|
||||
attach_function :get_message_file_def, :upb_MessageDef_File, [:pointer], :FileDef
|
||||
attach_function :get_message_fullname, :upb_MessageDef_FullName, [Descriptor], :string
|
||||
attach_function :get_mini_table, :upb_MessageDef_MiniTable, [Descriptor], MiniTable.ptr
|
||||
attach_function :oneof_count, :upb_MessageDef_OneofCount, [Descriptor], :int
|
||||
attach_function :message_options, :Descriptor_serialized_options, [Descriptor, :pointer, Internal::Arena], :pointer
|
||||
attach_function :get_well_known_type, :upb_MessageDef_WellKnownType, [Descriptor], WellKnown
|
||||
attach_function :find_msg_def_by_name, :upb_MessageDef_FindByNameWithSize, [Descriptor, :string, :size_t, :FieldDefPointer, :OneofDefPointer], :bool
|
||||
end
|
||||
end
|
||||
end
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2022 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
class FFI
|
||||
# DefPool
|
||||
attach_function :add_serialized_file, :upb_DefPool_AddFile, [:DefPool, :FileDescriptorProto, Status.by_ref], :FileDef
|
||||
attach_function :free_descriptor_pool, :upb_DefPool_Free, [:DefPool], :void
|
||||
attach_function :create_descriptor_pool,:upb_DefPool_New, [], :DefPool
|
||||
attach_function :get_extension_registry,:upb_DefPool_ExtensionRegistry, [:DefPool], :ExtensionRegistry
|
||||
attach_function :lookup_enum, :upb_DefPool_FindEnumByName, [:DefPool, :string], EnumDescriptor
|
||||
attach_function :lookup_extension, :upb_DefPool_FindExtensionByName,[:DefPool, :string], FieldDescriptor
|
||||
attach_function :lookup_msg, :upb_DefPool_FindMessageByName, [:DefPool, :string], Descriptor
|
||||
attach_function :lookup_service, :upb_DefPool_FindServiceByName, [:DefPool, :string], ServiceDescriptor
|
||||
|
||||
# FileDescriptorProto
|
||||
attach_function :parse, :FileDescriptorProto_parse, [:binary_string, :size_t, Internal::Arena], :FileDescriptorProto
|
||||
end
|
||||
class DescriptorPool
|
||||
attr :descriptor_pool
|
||||
attr_accessor :descriptor_class_by_def
|
||||
|
||||
def initialize
|
||||
@descriptor_pool = ::FFI::AutoPointer.new(Google::Protobuf::FFI.create_descriptor_pool, Google::Protobuf::FFI.method(:free_descriptor_pool))
|
||||
@descriptor_class_by_def = {}
|
||||
|
||||
# Should always be the last expression of the initializer to avoid
|
||||
# leaking references to this object before construction is complete.
|
||||
Google::Protobuf::OBJECT_CACHE.try_add @descriptor_pool.address, self
|
||||
end
|
||||
|
||||
def add_serialized_file(file_contents)
|
||||
# Allocate memory sized to file_contents
|
||||
memBuf = ::FFI::MemoryPointer.new(:char, file_contents.bytesize)
|
||||
# Insert the data
|
||||
memBuf.put_bytes(0, file_contents)
|
||||
temporary_arena = Google::Protobuf::FFI.create_arena
|
||||
file_descriptor_proto = Google::Protobuf::FFI.parse memBuf, file_contents.bytesize, temporary_arena
|
||||
raise ArgumentError.new("Unable to parse FileDescriptorProto") if file_descriptor_proto.null?
|
||||
|
||||
status = Google::Protobuf::FFI::Status.new
|
||||
file_descriptor = Google::Protobuf::FFI.add_serialized_file @descriptor_pool, file_descriptor_proto, status
|
||||
if file_descriptor.null?
|
||||
raise TypeError.new("Unable to build file to DescriptorPool: #{Google::Protobuf::FFI.error_message(status)}")
|
||||
else
|
||||
@descriptor_class_by_def[file_descriptor.address] = FileDescriptor.new file_descriptor, self
|
||||
end
|
||||
end
|
||||
|
||||
def lookup name
|
||||
Google::Protobuf::FFI.lookup_msg(@descriptor_pool, name) ||
|
||||
Google::Protobuf::FFI.lookup_enum(@descriptor_pool, name) ||
|
||||
Google::Protobuf::FFI.lookup_extension(@descriptor_pool, name) ||
|
||||
Google::Protobuf::FFI.lookup_service(@descriptor_pool, name)
|
||||
end
|
||||
|
||||
def self.generated_pool
|
||||
@@generated_pool ||= DescriptorPool.new
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Implementation details below are subject to breaking changes without
|
||||
# warning and are intended for use only within the gem.
|
||||
|
||||
def get_file_descriptor file_def
|
||||
return nil if file_def.null?
|
||||
@descriptor_class_by_def[file_def.address] ||= FileDescriptor.new(file_def, self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2022 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
class EnumDescriptor
|
||||
attr :descriptor_pool, :enum_def
|
||||
include Enumerable
|
||||
|
||||
# FFI Interface methods and setup
|
||||
extend ::FFI::DataConverter
|
||||
native_type ::FFI::Type::POINTER
|
||||
|
||||
class << self
|
||||
prepend Google::Protobuf::Internal::TypeSafety
|
||||
include Google::Protobuf::Internal::PointerHelper
|
||||
|
||||
# @param value [EnumDescriptor] EnumDescriptor to convert to an FFI native type
|
||||
# @param _ [Object] Unused
|
||||
def to_native(value, _)
|
||||
value.instance_variable_get(:@enum_def) || ::FFI::Pointer::NULL
|
||||
end
|
||||
|
||||
##
|
||||
# @param enum_def [::FFI::Pointer] EnumDef pointer to be wrapped
|
||||
# @param _ [Object] Unused
|
||||
def from_native(enum_def, _)
|
||||
return nil if enum_def.nil? or enum_def.null?
|
||||
file_def = Google::Protobuf::FFI.get_message_file_def enum_def
|
||||
descriptor_from_file_def(file_def, enum_def)
|
||||
end
|
||||
end
|
||||
|
||||
def self.new(*arguments, &block)
|
||||
raise "Descriptor objects may not be created from Ruby."
|
||||
end
|
||||
|
||||
def file_descriptor
|
||||
@descriptor_pool.send(:get_file_descriptor, Google::Protobuf::FFI.get_enum_file_descriptor(self))
|
||||
end
|
||||
|
||||
def name
|
||||
Google::Protobuf::FFI.get_enum_fullname(self)
|
||||
end
|
||||
|
||||
def to_s
|
||||
inspect
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#{self.class.name}: #{name}"
|
||||
end
|
||||
|
||||
def lookup_name(name)
|
||||
self.class.send(:lookup_name, self, name)
|
||||
end
|
||||
|
||||
def lookup_value(number)
|
||||
self.class.send(:lookup_value, self, number)
|
||||
end
|
||||
|
||||
def each &block
|
||||
n = Google::Protobuf::FFI.enum_value_count(self)
|
||||
0.upto(n - 1) do |i|
|
||||
enum_value = Google::Protobuf::FFI.enum_value_by_index(self, i)
|
||||
yield(Google::Protobuf::FFI.enum_name(enum_value).to_sym, Google::Protobuf::FFI.enum_number(enum_value))
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def enummodule
|
||||
if @module.nil?
|
||||
@module = build_enum_module
|
||||
end
|
||||
@module
|
||||
end
|
||||
|
||||
def options
|
||||
@options ||= begin
|
||||
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
temporary_arena = Google::Protobuf::FFI.create_arena
|
||||
buffer = Google::Protobuf::FFI.enum_options(self, size_ptr, temporary_arena)
|
||||
opts = Google::Protobuf::EnumOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze)
|
||||
opts.clear_features()
|
||||
opts.freeze
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(enum_def, descriptor_pool)
|
||||
@descriptor_pool = descriptor_pool
|
||||
@enum_def = enum_def
|
||||
@module = nil
|
||||
end
|
||||
|
||||
def self.private_constructor(enum_def, descriptor_pool)
|
||||
instance = allocate
|
||||
instance.send(:initialize, enum_def, descriptor_pool)
|
||||
instance
|
||||
end
|
||||
|
||||
def self.lookup_value(enum_def, number)
|
||||
enum_value = Google::Protobuf::FFI.enum_value_by_number(enum_def, number)
|
||||
if enum_value.null?
|
||||
nil
|
||||
else
|
||||
Google::Protobuf::FFI.enum_name(enum_value).to_sym
|
||||
end
|
||||
end
|
||||
|
||||
def self.lookup_name(enum_def, name)
|
||||
enum_value = Google::Protobuf::FFI.enum_value_by_name(enum_def, name.to_s, name.size)
|
||||
if enum_value.null?
|
||||
nil
|
||||
else
|
||||
Google::Protobuf::FFI.enum_number(enum_value)
|
||||
end
|
||||
end
|
||||
|
||||
def build_enum_module
|
||||
descriptor = self
|
||||
dynamic_module = Module.new do
|
||||
@descriptor = descriptor
|
||||
|
||||
class << self
|
||||
attr_accessor :descriptor
|
||||
end
|
||||
|
||||
def self.lookup(number)
|
||||
descriptor.lookup_value number
|
||||
end
|
||||
|
||||
def self.resolve(name)
|
||||
descriptor.lookup_name name
|
||||
end
|
||||
end
|
||||
|
||||
self.each do |name, value|
|
||||
if name[0] < 'A' || name[0] > 'Z'
|
||||
if name[0] >= 'a' and name[0] <= 'z'
|
||||
name = name[0].upcase + name[1..-1] # auto capitalize
|
||||
else
|
||||
warn(
|
||||
"Enum value '#{name}' does not start with an uppercase letter " +
|
||||
"as is required for Ruby constants.")
|
||||
next
|
||||
end
|
||||
end
|
||||
dynamic_module.const_set(name.to_sym, value)
|
||||
end
|
||||
dynamic_module
|
||||
end
|
||||
end
|
||||
|
||||
class FFI
|
||||
# EnumDescriptor
|
||||
attach_function :get_enum_file_descriptor, :upb_EnumDef_File, [EnumDescriptor], :FileDef
|
||||
attach_function :enum_value_by_name, :upb_EnumDef_FindValueByNameWithSize,[EnumDescriptor, :string, :size_t], :EnumValueDef
|
||||
attach_function :enum_value_by_number, :upb_EnumDef_FindValueByNumber, [EnumDescriptor, :int], :EnumValueDef
|
||||
attach_function :get_enum_fullname, :upb_EnumDef_FullName, [EnumDescriptor], :string
|
||||
attach_function :enum_options, :EnumDescriptor_serialized_options, [EnumDescriptor, :pointer, Internal::Arena], :pointer
|
||||
attach_function :enum_value_by_index, :upb_EnumDef_Value, [EnumDescriptor, :int], :EnumValueDef
|
||||
attach_function :enum_value_count, :upb_EnumDef_ValueCount, [EnumDescriptor], :int
|
||||
attach_function :enum_name, :upb_EnumValueDef_Name, [:EnumValueDef], :string
|
||||
attach_function :enum_number, :upb_EnumValueDef_Number, [:EnumValueDef], :int
|
||||
end
|
||||
end
|
||||
end
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2022 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
class FFI
|
||||
extend ::FFI::Library
|
||||
# Workaround for Bazel's use of symlinks + JRuby's __FILE__ and `caller`
|
||||
# that resolves them.
|
||||
if ENV['BAZEL'] == 'true'
|
||||
ffi_lib ::FFI::Compiler::Loader.find 'protobuf_c_ffi', ENV['PWD']
|
||||
else
|
||||
ffi_lib ::FFI::Compiler::Loader.find 'protobuf_c_ffi'
|
||||
end
|
||||
|
||||
## Map
|
||||
Upb_Map_Begin = -1
|
||||
|
||||
## Encoding Status
|
||||
Upb_Status_MaxMessage = 511
|
||||
Upb_Encode_Deterministic = 1
|
||||
Upb_Encode_SkipUnknown = 2
|
||||
|
||||
## JSON Encoding options
|
||||
# When set, emits 0/default values. TODO: proto3 only?
|
||||
Upb_JsonEncode_EmitDefaults = 1
|
||||
# When set, use normal (snake_case) field names instead of JSON (camelCase) names.
|
||||
Upb_JsonEncode_UseProtoNames = 2
|
||||
# When set, emits enums as their integer values instead of as their names.
|
||||
Upb_JsonEncode_FormatEnumsAsIntegers = 4
|
||||
|
||||
## JSON Decoding options
|
||||
Upb_JsonDecode_IgnoreUnknown = 1
|
||||
|
||||
## JSON Decoding results
|
||||
Upb_JsonDecodeResult_Ok = 0
|
||||
Upb_JsonDecodeResult_OkWithEmptyStringNumerics = 1
|
||||
Upb_JsonDecodeResult_Error = 2
|
||||
|
||||
typedef :pointer, :Array
|
||||
typedef :pointer, :DefPool
|
||||
typedef :pointer, :EnumValueDef
|
||||
typedef :pointer, :ExtensionRegistry
|
||||
typedef :pointer, :FieldDefPointer
|
||||
typedef :pointer, :FileDef
|
||||
typedef :pointer, :FileDescriptorProto
|
||||
typedef :pointer, :Map
|
||||
typedef :pointer, :Message # Instances of a message
|
||||
typedef :pointer, :OneofDefPointer
|
||||
typedef :pointer, :binary_string
|
||||
if ::FFI::Platform::ARCH == "aarch64"
|
||||
typedef :u_int8_t, :uint8_t
|
||||
typedef :u_int16_t, :uint16_t
|
||||
typedef :u_int32_t, :uint32_t
|
||||
typedef :u_int64_t, :uint64_t
|
||||
end
|
||||
|
||||
FieldType = enum(
|
||||
:double, 1,
|
||||
:float,
|
||||
:int64,
|
||||
:uint64,
|
||||
:int32,
|
||||
:fixed64,
|
||||
:fixed32,
|
||||
:bool,
|
||||
:string,
|
||||
:group,
|
||||
:message,
|
||||
:bytes,
|
||||
:uint32,
|
||||
:enum,
|
||||
:sfixed32,
|
||||
:sfixed64,
|
||||
:sint32,
|
||||
:sint64
|
||||
)
|
||||
|
||||
CType = enum(
|
||||
:bool, 1,
|
||||
:float,
|
||||
:int32,
|
||||
:uint32,
|
||||
:enum,
|
||||
:message,
|
||||
:double,
|
||||
:int64,
|
||||
:uint64,
|
||||
:string,
|
||||
:bytes
|
||||
)
|
||||
|
||||
Label = enum(
|
||||
:optional, 1,
|
||||
:required,
|
||||
:repeated
|
||||
)
|
||||
|
||||
# All the different kind of well known type messages. For simplicity of check,
|
||||
# number wrappers and string wrappers are grouped together. Make sure the
|
||||
# order and merber of these groups are not changed.
|
||||
|
||||
WellKnown = enum(
|
||||
:Unspecified,
|
||||
:Any,
|
||||
:FieldMask,
|
||||
:Duration,
|
||||
:Timestamp,
|
||||
# number wrappers
|
||||
:DoubleValue,
|
||||
:FloatValue,
|
||||
:Int64Value,
|
||||
:UInt64Value,
|
||||
:Int32Value,
|
||||
:UInt32Value,
|
||||
# string wrappers
|
||||
:StringValue,
|
||||
:BytesValue,
|
||||
:BoolValue,
|
||||
:Value,
|
||||
:ListValue,
|
||||
:Struct
|
||||
)
|
||||
|
||||
DecodeStatus = enum(
|
||||
:Ok,
|
||||
:Malformed, # Wire format was corrupt
|
||||
:OutOfMemory, # Arena alloc failed
|
||||
:BadUtf8, # String field had bad UTF-8
|
||||
:MaxDepthExceeded, # Exceeded UPB_DECODE_MAXDEPTH
|
||||
|
||||
# CheckRequired failed, but the parse otherwise succeeded.
|
||||
:MissingRequired,
|
||||
)
|
||||
|
||||
EncodeStatus = enum(
|
||||
:Ok,
|
||||
:OutOfMemory, # Arena alloc failed
|
||||
:MaxDepthExceeded, # Exceeded UPB_DECODE_MAXDEPTH
|
||||
|
||||
# CheckRequired failed, but the parse otherwise succeeded.
|
||||
:MissingRequired,
|
||||
)
|
||||
|
||||
class StringView < ::FFI::Struct
|
||||
layout :data, :pointer,
|
||||
:size, :size_t
|
||||
end
|
||||
|
||||
class MiniTable < ::FFI::Struct
|
||||
layout :subs, :pointer,
|
||||
:fields, :pointer,
|
||||
:size, :uint16_t,
|
||||
:field_count, :uint16_t,
|
||||
:ext, :uint8_t, # upb_ExtMode, declared as uint8_t so sizeof(ext) == 1
|
||||
:dense_below, :uint8_t,
|
||||
:table_mask, :uint8_t,
|
||||
:required_count, :uint8_t # Required fields have the lowest hasbits.
|
||||
# To statically initialize the tables of variable length, we need a flexible
|
||||
# array member, and we need to compile in gnu99 mode (constant initialization
|
||||
# of flexible array members is a GNU extension, not in C99 unfortunately. */
|
||||
# _upb_FastTable_Entry fasttable[];
|
||||
end
|
||||
|
||||
class Status < ::FFI::Struct
|
||||
layout :ok, :bool,
|
||||
:msg, [:char, Upb_Status_MaxMessage]
|
||||
|
||||
def initialize
|
||||
super
|
||||
FFI.clear self
|
||||
end
|
||||
end
|
||||
|
||||
class MessageValue < ::FFI::Union
|
||||
layout :bool_val, :bool,
|
||||
:float_val, :float,
|
||||
:double_val, :double,
|
||||
:int32_val, :int32_t,
|
||||
:int64_val, :int64_t,
|
||||
:uint32_val, :uint32_t,
|
||||
:uint64_val,:uint64_t,
|
||||
:map_val, :pointer,
|
||||
:msg_val, :pointer,
|
||||
:array_val,:pointer,
|
||||
:str_val, StringView
|
||||
end
|
||||
|
||||
Upb_Message_Begin = -1
|
||||
|
||||
class MutableMessageValue < ::FFI::Union
|
||||
layout :map, :Map,
|
||||
:msg, :Message,
|
||||
:array, :Array
|
||||
end
|
||||
|
||||
# Status
|
||||
attach_function :clear, :upb_Status_Clear, [Status.by_ref], :void
|
||||
attach_function :error_message, :upb_Status_ErrorMessage, [Status.by_ref], :string
|
||||
|
||||
# Generic
|
||||
attach_function :memcmp, [:pointer, :pointer, :size_t], :int
|
||||
attach_function :memcpy, [:pointer, :pointer, :size_t], :int
|
||||
|
||||
# Alternatives to pre-processor macros
|
||||
def self.decode_max_depth(i)
|
||||
i << 16
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+330
@@ -0,0 +1,330 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2022 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
class FieldDescriptor
|
||||
attr :field_def, :descriptor_pool
|
||||
|
||||
include Google::Protobuf::Internal::Convert
|
||||
|
||||
# FFI Interface methods and setup
|
||||
extend ::FFI::DataConverter
|
||||
native_type ::FFI::Type::POINTER
|
||||
|
||||
class << self
|
||||
prepend Google::Protobuf::Internal::TypeSafety
|
||||
include Google::Protobuf::Internal::PointerHelper
|
||||
|
||||
# @param value [FieldDescriptor] FieldDescriptor to convert to an FFI native type
|
||||
# @param _ [Object] Unused
|
||||
def to_native(value, _)
|
||||
field_def_ptr = value.instance_variable_get(:@field_def)
|
||||
warn "Underlying field_def was nil!" if field_def_ptr.nil?
|
||||
raise "Underlying field_def was null!" if !field_def_ptr.nil? and field_def_ptr.null?
|
||||
field_def_ptr
|
||||
end
|
||||
|
||||
##
|
||||
# @param field_def [::FFI::Pointer] FieldDef pointer to be wrapped
|
||||
# @param _ [Object] Unused
|
||||
def from_native(field_def, _ = nil)
|
||||
return nil if field_def.nil? or field_def.null?
|
||||
file_def = Google::Protobuf::FFI.file_def_by_raw_field_def(field_def)
|
||||
descriptor_from_file_def(file_def, field_def)
|
||||
end
|
||||
end
|
||||
|
||||
def self.new(*arguments, &block)
|
||||
raise "Descriptor objects may not be created from Ruby."
|
||||
end
|
||||
|
||||
def to_s
|
||||
inspect
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#{self.class.name}: #{name}"
|
||||
end
|
||||
|
||||
def name
|
||||
@name ||= Google::Protobuf::FFI.get_full_name(self)
|
||||
end
|
||||
|
||||
def json_name
|
||||
@json_name ||= Google::Protobuf::FFI.get_json_name(self)
|
||||
end
|
||||
|
||||
def number
|
||||
@number ||= Google::Protobuf::FFI.get_number(self)
|
||||
end
|
||||
|
||||
def type
|
||||
@type ||= Google::Protobuf::FFI.get_type(self)
|
||||
end
|
||||
|
||||
def label
|
||||
@label ||= Google::Protobuf::FFI.get_label(self)
|
||||
end
|
||||
|
||||
def default
|
||||
return nil if Google::Protobuf::FFI.is_sub_message(self)
|
||||
if Google::Protobuf::FFI.is_repeated(self)
|
||||
message_value = Google::Protobuf::FFI::MessageValue.new
|
||||
else
|
||||
message_value = Google::Protobuf::FFI.get_default(self)
|
||||
end
|
||||
enum_def = Google::Protobuf::FFI.get_subtype_as_enum(self)
|
||||
if enum_def.null?
|
||||
convert_upb_to_ruby message_value, c_type
|
||||
else
|
||||
convert_upb_to_ruby message_value, c_type, enum_def
|
||||
end
|
||||
end
|
||||
|
||||
def submsg_name
|
||||
if defined? @submsg_name
|
||||
@submsg_name
|
||||
else
|
||||
@submsg_name = case c_type
|
||||
when :enum
|
||||
Google::Protobuf::FFI.get_enum_fullname Google::Protobuf::FFI.get_subtype_as_enum self
|
||||
when :message
|
||||
Google::Protobuf::FFI.get_message_fullname Google::Protobuf::FFI.get_subtype_as_message self
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Tests if this field has been set on the argument message.
|
||||
#
|
||||
# @param msg [Google::Protobuf::Message]
|
||||
# @return [Object] Value of the field on this message.
|
||||
# @raise [TypeError] If the field is not defined on this message.
|
||||
def get(msg)
|
||||
if msg.class.descriptor == Google::Protobuf::FFI.get_containing_message_def(self)
|
||||
msg.send :get_field, self
|
||||
else
|
||||
raise TypeError.new "get method called on wrong message type"
|
||||
end
|
||||
end
|
||||
|
||||
def subtype
|
||||
if defined? @subtype
|
||||
@subtype
|
||||
else
|
||||
@subtype = case c_type
|
||||
when :enum
|
||||
Google::Protobuf::FFI.get_subtype_as_enum(self)
|
||||
when :message
|
||||
Google::Protobuf::FFI.get_subtype_as_message(self)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Tests if this field has been set on the argument message.
|
||||
#
|
||||
# @param msg [Google::Protobuf::Message]
|
||||
# @return [Boolean] True iff message has this field set
|
||||
# @raise [TypeError] If this field does not exist on the message
|
||||
# @raise [ArgumentError] If this field does not track presence
|
||||
def has?(msg)
|
||||
if msg.class.descriptor != Google::Protobuf::FFI.get_containing_message_def(self)
|
||||
raise TypeError.new "has method called on wrong message type"
|
||||
end
|
||||
unless has_presence?
|
||||
raise ArgumentError.new "does not track presence"
|
||||
end
|
||||
|
||||
Google::Protobuf::FFI.get_message_has msg.instance_variable_get(:@msg), self
|
||||
end
|
||||
|
||||
##
|
||||
# Tests if this field tracks presence.
|
||||
#
|
||||
# @return [Boolean] True iff this field tracks presence
|
||||
def has_presence?
|
||||
@has_presence ||= Google::Protobuf::FFI.get_has_presence(self)
|
||||
end
|
||||
|
||||
##
|
||||
# Tests if this is a repeated field that uses packed encoding.
|
||||
#
|
||||
# @return [Boolean] True iff this field is packed
|
||||
def is_packed?
|
||||
@is_packed ||= Google::Protobuf::FFI.get_is_packed(self)
|
||||
end
|
||||
|
||||
# @param msg [Google::Protobuf::Message]
|
||||
def clear(msg)
|
||||
if msg.class.descriptor != Google::Protobuf::FFI.get_containing_message_def(self)
|
||||
raise TypeError.new "clear method called on wrong message type"
|
||||
end
|
||||
Google::Protobuf::FFI.clear_message_field msg.instance_variable_get(:@msg), self
|
||||
nil
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# FieldDescriptor.set(message, value)
|
||||
#
|
||||
# Sets the value corresponding to this field to the given value on the given
|
||||
# message. Raises an exception if message is of the wrong type. Performs the
|
||||
# ordinary type-checks for field setting.
|
||||
#
|
||||
# @param msg [Google::Protobuf::Message]
|
||||
# @param value [Object]
|
||||
def set(msg, value)
|
||||
if msg.class.descriptor != Google::Protobuf::FFI.get_containing_message_def(self)
|
||||
raise TypeError.new "set method called on wrong message type"
|
||||
end
|
||||
unless set_value_on_message value, msg.instance_variable_get(:@msg), msg.instance_variable_get(:@arena)
|
||||
raise RuntimeError.new "allocation failed"
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def map?
|
||||
@map ||= Google::Protobuf::FFI.is_map self
|
||||
end
|
||||
|
||||
def repeated?
|
||||
@repeated ||= Google::Protobuf::FFI.is_repeated self
|
||||
end
|
||||
|
||||
def sub_message?
|
||||
@sub_message ||= Google::Protobuf::FFI.is_sub_message self
|
||||
end
|
||||
|
||||
def wrapper?
|
||||
if defined? @wrapper
|
||||
@wrapper
|
||||
else
|
||||
message_descriptor = Google::Protobuf::FFI.get_subtype_as_message(self)
|
||||
@wrapper = message_descriptor.nil? ? false : message_descriptor.send(:wrapper?)
|
||||
end
|
||||
end
|
||||
|
||||
def options
|
||||
@options ||= begin
|
||||
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
temporary_arena = Google::Protobuf::FFI.create_arena
|
||||
buffer = Google::Protobuf::FFI.field_options(self, size_ptr, temporary_arena)
|
||||
opts = Google::Protobuf::FieldOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze)
|
||||
opts.clear_features()
|
||||
opts.freeze
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(field_def, descriptor_pool)
|
||||
@field_def = field_def
|
||||
@descriptor_pool = descriptor_pool
|
||||
end
|
||||
|
||||
def self.private_constructor(field_def, descriptor_pool)
|
||||
instance = allocate
|
||||
instance.send(:initialize, field_def, descriptor_pool)
|
||||
instance
|
||||
end
|
||||
|
||||
# TODO Can this be added to the public API?
|
||||
def real_containing_oneof
|
||||
@real_containing_oneof ||= Google::Protobuf::FFI.real_containing_oneof self
|
||||
end
|
||||
|
||||
# Implementation details below are subject to breaking changes without
|
||||
# warning and are intended for use only within the gem.
|
||||
|
||||
##
|
||||
# Sets the field this FieldDescriptor represents to the given value on the given message.
|
||||
# @param value [Object] Value to be set
|
||||
# @param msg [::FFI::Pointer] Pointer the the upb_Message
|
||||
# @param arena [Arena] Arena of the message that owns msg
|
||||
def set_value_on_message(value, msg, arena, wrap: false)
|
||||
message_to_alter = msg
|
||||
field_def_to_set = self
|
||||
if map?
|
||||
raise TypeError.new "Expected map" unless value.is_a? Google::Protobuf::Map
|
||||
message_descriptor = subtype
|
||||
|
||||
key_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 1)
|
||||
key_field_type = Google::Protobuf::FFI.get_type(key_field_def)
|
||||
raise TypeError.new "Map key type does not match field's key type" unless key_field_type == value.send(:key_type)
|
||||
|
||||
value_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 2)
|
||||
value_field_type = Google::Protobuf::FFI.get_type(value_field_def)
|
||||
raise TypeError.new "Map value type does not match field's value type" unless value_field_type == value.send(:value_type)
|
||||
|
||||
raise TypeError.new "Map value type has wrong message/enum class" unless value_field_def.subtype == value.send(:descriptor)
|
||||
|
||||
arena.fuse(value.send(:arena))
|
||||
message_value = Google::Protobuf::FFI::MessageValue.new
|
||||
message_value[:map_val] = value.send(:map_ptr)
|
||||
elsif repeated?
|
||||
raise TypeError.new "Expected repeated field array" unless value.is_a? RepeatedField
|
||||
raise TypeError.new "Repeated field array has wrong message/enum class" unless value.send(:type) == type
|
||||
arena.fuse(value.send(:arena))
|
||||
message_value = Google::Protobuf::FFI::MessageValue.new
|
||||
message_value[:array_val] = value.send(:array)
|
||||
else
|
||||
if value.nil? and (sub_message? or !real_containing_oneof.nil?)
|
||||
Google::Protobuf::FFI.clear_message_field message_to_alter, field_def_to_set
|
||||
return true
|
||||
end
|
||||
if wrap
|
||||
value_field_def = Google::Protobuf::FFI.get_field_by_number subtype, 1
|
||||
type_for_conversion = Google::Protobuf::FFI.get_c_type(value_field_def)
|
||||
raise RuntimeError.new "Not expecting to get a msg or enum when unwrapping" if [:enum, :message].include? type_for_conversion
|
||||
message_value = convert_ruby_to_upb(value, arena, type_for_conversion, nil)
|
||||
message_to_alter = Google::Protobuf::FFI.get_mutable_message(msg, self, arena)[:msg]
|
||||
field_def_to_set = value_field_def
|
||||
else
|
||||
message_value = convert_ruby_to_upb(value, arena, c_type, subtype)
|
||||
end
|
||||
end
|
||||
Google::Protobuf::FFI.set_message_field message_to_alter, field_def_to_set, message_value, arena
|
||||
end
|
||||
|
||||
def c_type
|
||||
@c_type ||= Google::Protobuf::FFI.get_c_type(self)
|
||||
end
|
||||
end
|
||||
|
||||
class FFI
|
||||
# MessageDef
|
||||
attach_function :get_field_by_index, :upb_MessageDef_Field, [Descriptor, :int], FieldDescriptor
|
||||
attach_function :get_field_by_name, :upb_MessageDef_FindFieldByNameWithSize,[Descriptor, :string, :size_t], FieldDescriptor
|
||||
attach_function :get_field_by_number, :upb_MessageDef_FindFieldByNumber, [Descriptor, :uint32_t], FieldDescriptor
|
||||
|
||||
# FieldDescriptor
|
||||
attach_function :field_options, :FieldDescriptor_serialized_options, [FieldDescriptor, :pointer, Internal::Arena], :pointer
|
||||
attach_function :get_containing_message_def, :upb_FieldDef_ContainingType, [FieldDescriptor], Descriptor
|
||||
attach_function :get_c_type, :upb_FieldDef_CType, [FieldDescriptor], CType
|
||||
attach_function :get_default, :upb_FieldDef_Default, [FieldDescriptor], MessageValue.by_value
|
||||
attach_function :get_subtype_as_enum, :upb_FieldDef_EnumSubDef, [FieldDescriptor], EnumDescriptor
|
||||
attach_function :get_has_presence, :upb_FieldDef_HasPresence, [FieldDescriptor], :bool
|
||||
attach_function :get_is_packed, :upb_FieldDef_IsPacked, [FieldDescriptor], :bool
|
||||
attach_function :is_map, :upb_FieldDef_IsMap, [FieldDescriptor], :bool
|
||||
attach_function :is_repeated, :upb_FieldDef_IsRepeated, [FieldDescriptor], :bool
|
||||
attach_function :is_sub_message, :upb_FieldDef_IsSubMessage, [FieldDescriptor], :bool
|
||||
attach_function :get_json_name, :upb_FieldDef_JsonName, [FieldDescriptor], :string
|
||||
attach_function :get_label, :upb_FieldDef_Label, [FieldDescriptor], Label
|
||||
attach_function :get_subtype_as_message, :upb_FieldDef_MessageSubDef, [FieldDescriptor], Descriptor
|
||||
attach_function :get_full_name, :upb_FieldDef_Name, [FieldDescriptor], :string
|
||||
attach_function :get_number, :upb_FieldDef_Number, [FieldDescriptor], :uint32_t
|
||||
attach_function :get_type, :upb_FieldDef_Type, [FieldDescriptor], FieldType
|
||||
attach_function :file_def_by_raw_field_def, :upb_FieldDef_File, [:pointer], :FileDef
|
||||
end
|
||||
end
|
||||
end
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2022 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
class FFI
|
||||
# FileDescriptor
|
||||
attach_function :file_def_name, :upb_FileDef_Name, [:FileDef], :string
|
||||
attach_function :file_def_pool, :upb_FileDef_Pool, [:FileDef], :DefPool
|
||||
attach_function :file_options, :FileDescriptor_serialized_options, [:FileDef, :pointer, Internal::Arena], :pointer
|
||||
end
|
||||
|
||||
class FileDescriptor
|
||||
attr :descriptor_pool, :file_def
|
||||
|
||||
def initialize(file_def, descriptor_pool)
|
||||
@descriptor_pool = descriptor_pool
|
||||
@file_def = file_def
|
||||
end
|
||||
|
||||
def to_s
|
||||
inspect
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#{self.class.name}: #{name}"
|
||||
end
|
||||
|
||||
def name
|
||||
Google::Protobuf::FFI.file_def_name(@file_def)
|
||||
end
|
||||
|
||||
def options
|
||||
@options ||= begin
|
||||
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
temporary_arena = Google::Protobuf::FFI.create_arena
|
||||
buffer = Google::Protobuf::FFI.file_options(@file_def, size_ptr, temporary_arena)
|
||||
opts = Google::Protobuf::FileOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze)
|
||||
opts.clear_features()
|
||||
opts.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2022 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
##
|
||||
# Implementation details below are subject to breaking changes without
|
||||
# warning and are intended for use only within the gem.
|
||||
module Google
|
||||
module Protobuf
|
||||
module Internal
|
||||
class Arena
|
||||
# FFI Interface methods and setup
|
||||
extend ::FFI::DataConverter
|
||||
native_type ::FFI::Type::POINTER
|
||||
|
||||
class << self
|
||||
prepend Google::Protobuf::Internal::TypeSafety
|
||||
|
||||
# @param value [Arena] Arena to convert to an FFI native type
|
||||
# @param _ [Object] Unused
|
||||
def to_native(value, _)
|
||||
value.instance_variable_get(:@arena) || ::FFI::Pointer::NULL
|
||||
end
|
||||
|
||||
##
|
||||
# @param value [::FFI::Pointer] Arena pointer to be wrapped
|
||||
# @param _ [Object] Unused
|
||||
def from_native(value, _)
|
||||
new(value)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(pointer)
|
||||
@arena = ::FFI::AutoPointer.new(pointer, Google::Protobuf::FFI.method(:free_arena))
|
||||
@pinned_messages = []
|
||||
end
|
||||
|
||||
def fuse(other_arena)
|
||||
return if other_arena == self
|
||||
unless Google::Protobuf::FFI.fuse_arena(self, other_arena)
|
||||
raise RuntimeError.new "Unable to fuse arenas. This should never happen since Ruby does not use initial blocks"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class FFI
|
||||
# Arena
|
||||
attach_function :create_arena, :Arena_create, [], Internal::Arena
|
||||
attach_function :fuse_arena, :upb_Arena_Fuse, [Internal::Arena, Internal::Arena], :bool
|
||||
# Argument takes a :pointer rather than a typed Arena here due to
|
||||
# implementation details of FFI::AutoPointer.
|
||||
attach_function :free_arena, :upb_Arena_Free, [:pointer], :void
|
||||
attach_function :arena_malloc, :upb_Arena_Malloc, [Internal::Arena, :size_t], :pointer
|
||||
end
|
||||
end
|
||||
end
|
||||
+296
@@ -0,0 +1,296 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2022 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
##
|
||||
# Implementation details below are subject to breaking changes without
|
||||
# warning and are intended for use only within the gem.
|
||||
module Google
|
||||
module Protobuf
|
||||
module Internal
|
||||
module Convert
|
||||
|
||||
# Arena should be the
|
||||
# @param value [Object] Value to convert
|
||||
# @param arena [Arena] Arena that owns the Message where the MessageValue
|
||||
# will be set
|
||||
# @return [Google::Protobuf::FFI::MessageValue]
|
||||
def convert_ruby_to_upb(value, arena, c_type, msg_or_enum_def)
|
||||
raise ArgumentError.new "Expected Descriptor or EnumDescriptor, instead got #{msg_or_enum_def.class}" unless [NilClass, Descriptor, EnumDescriptor].include? msg_or_enum_def.class
|
||||
return_value = Google::Protobuf::FFI::MessageValue.new
|
||||
case c_type
|
||||
when :float
|
||||
raise TypeError.new "Expected number type for float field '#{name}' (given #{value.class})." unless value.respond_to? :to_f
|
||||
return_value[:float_val] = value.to_f
|
||||
when :double
|
||||
raise TypeError.new "Expected number type for double field '#{name}' (given #{value.class})." unless value.respond_to? :to_f
|
||||
return_value[:double_val] = value.to_f
|
||||
when :bool
|
||||
raise TypeError.new "Invalid argument for boolean field '#{name}' (given #{value.class})." unless [TrueClass, FalseClass].include? value.class
|
||||
return_value[:bool_val] = value
|
||||
when :string
|
||||
raise TypeError.new "Invalid argument for string field '#{name}' (given #{value.class})." unless value.is_a?(String) or value.is_a?(Symbol)
|
||||
value = value.to_s if value.is_a?(Symbol)
|
||||
if value.encoding == Encoding::UTF_8
|
||||
unless value.valid_encoding?
|
||||
# TODO:
|
||||
# For now we only warn for this case. We will remove the
|
||||
# warning and throw an exception below in the 30.x release
|
||||
warn "String is invalid UTF-8. This will be an error in a future version."
|
||||
# raise Encoding::InvalidByteSequenceError.new "String is invalid UTF-8"
|
||||
end
|
||||
string_value = value
|
||||
else
|
||||
string_value = value.to_s.encode("UTF-8")
|
||||
end
|
||||
return_value[:str_val][:size] = string_value.bytesize
|
||||
return_value[:str_val][:data] = Google::Protobuf::FFI.arena_malloc(arena, string_value.bytesize)
|
||||
# TODO - how important is it to still use arena malloc, versus the following?
|
||||
# buffer = ::FFI::MemoryPointer.new(:char, string_value.bytesize)
|
||||
# buffer.put_bytes(0, string_value)
|
||||
# return_value[:str_val][:data] = buffer
|
||||
raise NoMemoryError.new "Cannot allocate #{string_value.bytesize} bytes for string on Arena" if return_value[:str_val][:data].nil? || return_value[:str_val][:data].null?
|
||||
return_value[:str_val][:data].write_string(string_value)
|
||||
when :bytes
|
||||
raise TypeError.new "Invalid argument for bytes field '#{name}' (given #{value.class})." unless value.is_a? String
|
||||
string_value = value.encode("ASCII-8BIT")
|
||||
return_value[:str_val][:size] = string_value.bytesize
|
||||
return_value[:str_val][:data] = Google::Protobuf::FFI.arena_malloc(arena, string_value.bytesize)
|
||||
raise NoMemoryError.new "Cannot allocate #{string_value.bytesize} bytes for bytes on Arena" if return_value[:str_val][:data].nil? || return_value[:str_val][:data].null?
|
||||
return_value[:str_val][:data].write_string_length(string_value, string_value.bytesize)
|
||||
when :message
|
||||
raise TypeError.new "nil message not allowed here." if value.nil?
|
||||
if value.is_a? Hash
|
||||
raise RuntimeError.new "Attempted to initialize message from Hash for field #{name} but have no definition" if msg_or_enum_def.nil?
|
||||
new_message = msg_or_enum_def.msgclass.
|
||||
send(:private_constructor, arena, initial_value: value)
|
||||
return_value[:msg_val] = new_message.instance_variable_get(:@msg)
|
||||
return return_value
|
||||
end
|
||||
|
||||
descriptor = value.class.respond_to?(:descriptor) ? value.class.descriptor : nil
|
||||
if descriptor != msg_or_enum_def
|
||||
wkt = Google::Protobuf::FFI.get_well_known_type(msg_or_enum_def)
|
||||
case wkt
|
||||
when :Timestamp
|
||||
raise TypeError.new "Invalid type #{value.class} to assign to submessage field '#{name}'." unless value.kind_of? Time
|
||||
new_message = Google::Protobuf::FFI.new_message_from_def Google::Protobuf::FFI.get_mini_table(msg_or_enum_def), arena
|
||||
sec = Google::Protobuf::FFI::MessageValue.new
|
||||
sec[:int64_val] = value.tv_sec
|
||||
sec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 1
|
||||
raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, sec_field_def, sec, arena
|
||||
nsec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 2
|
||||
nsec = Google::Protobuf::FFI::MessageValue.new
|
||||
nsec[:int32_val] = value.tv_nsec
|
||||
raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, nsec_field_def, nsec, arena
|
||||
return_value[:msg_val] = new_message
|
||||
when :Duration
|
||||
raise TypeError.new "Invalid type #{value.class} to assign to submessage field '#{name}'." unless value.kind_of? Numeric
|
||||
new_message = Google::Protobuf::FFI.new_message_from_def Google::Protobuf::FFI.get_mini_table(msg_or_enum_def), arena
|
||||
sec = Google::Protobuf::FFI::MessageValue.new
|
||||
sec[:int64_val] = value
|
||||
sec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 1
|
||||
raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, sec_field_def, sec, arena
|
||||
nsec_field_def = Google::Protobuf::FFI.get_field_by_number msg_or_enum_def, 2
|
||||
nsec = Google::Protobuf::FFI::MessageValue.new
|
||||
nsec[:int32_val] = ((value.to_f - value.to_i) * 1000000000).round
|
||||
raise "Should be impossible" unless Google::Protobuf::FFI.set_message_field new_message, nsec_field_def, nsec, arena
|
||||
return_value[:msg_val] = new_message
|
||||
else
|
||||
raise TypeError.new "Invalid type #{value.class} to assign to submessage field '#{name}'."
|
||||
end
|
||||
else
|
||||
arena.fuse(value.instance_variable_get(:@arena))
|
||||
return_value[:msg_val] = value.instance_variable_get :@msg
|
||||
end
|
||||
when :enum
|
||||
return_value[:int32_val] = case value
|
||||
when Numeric
|
||||
value.to_i
|
||||
when String, Symbol
|
||||
enum_number = EnumDescriptor.send(:lookup_name, msg_or_enum_def, value.to_s)
|
||||
#TODO add the bad value to the error message after tests pass
|
||||
raise RangeError.new "Unknown symbol value for enum field '#{name}'." if enum_number.nil?
|
||||
enum_number
|
||||
else
|
||||
raise TypeError.new "Expected number or symbol type for enum field '#{name}'."
|
||||
end
|
||||
#TODO After all tests pass, improve error message across integer type by including actual offending value
|
||||
when :int32
|
||||
raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
|
||||
raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
|
||||
raise RangeError.new "Value assigned to int32 field '#{name}' (given #{value.class}) with more than 32-bits." unless value.to_i.bit_length < 32
|
||||
return_value[:int32_val] = value.to_i
|
||||
when :uint32
|
||||
raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
|
||||
raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
|
||||
raise RangeError.new "Assigning negative value to unsigned integer field '#{name}' (given #{value.class})." if value < 0
|
||||
raise RangeError.new "Value assigned to uint32 field '#{name}' (given #{value.class}) with more than 32-bits." unless value.to_i.bit_length < 33
|
||||
return_value[:uint32_val] = value.to_i
|
||||
when :int64
|
||||
raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
|
||||
raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
|
||||
raise RangeError.new "Value assigned to int64 field '#{name}' (given #{value.class}) with more than 64-bits." unless value.to_i.bit_length < 64
|
||||
return_value[:int64_val] = value.to_i
|
||||
when :uint64
|
||||
raise TypeError.new "Expected number type for integral field '#{name}' (given #{value.class})." unless value.is_a? Numeric
|
||||
raise RangeError.new "Non-integral floating point value assigned to integer field '#{name}' (given #{value.class})." if value.floor != value
|
||||
raise RangeError.new "Assigning negative value to unsigned integer field '#{name}' (given #{value.class})." if value < 0
|
||||
raise RangeError.new "Value assigned to uint64 field '#{name}' (given #{value.class}) with more than 64-bits." unless value.to_i.bit_length < 65
|
||||
return_value[:uint64_val] = value.to_i
|
||||
else
|
||||
raise RuntimeError.new "Unsupported type #{c_type}"
|
||||
end
|
||||
return_value
|
||||
end
|
||||
|
||||
##
|
||||
# Safe to call without an arena if the caller has checked that c_type
|
||||
# is not :message.
|
||||
# @param message_value [Google::Protobuf::FFI::MessageValue] Value to be converted.
|
||||
# @param c_type [Google::Protobuf::FFI::CType] Enum representing the type of message_value
|
||||
# @param msg_or_enum_def [::FFI::Pointer] Pointer to the MsgDef or EnumDef definition
|
||||
# @param arena [Google::Protobuf::Internal::Arena] Arena to create Message instances, if needed
|
||||
def convert_upb_to_ruby(message_value, c_type, msg_or_enum_def = nil, arena = nil)
|
||||
throw TypeError.new "Expected MessageValue but got #{message_value.class}" unless message_value.is_a? Google::Protobuf::FFI::MessageValue
|
||||
|
||||
case c_type
|
||||
when :bool
|
||||
message_value[:bool_val]
|
||||
when :int32
|
||||
message_value[:int32_val]
|
||||
when :uint32
|
||||
message_value[:uint32_val]
|
||||
when :double
|
||||
message_value[:double_val]
|
||||
when :int64
|
||||
message_value[:int64_val]
|
||||
when :uint64
|
||||
message_value[:uint64_val]
|
||||
when :string
|
||||
if message_value[:str_val][:size].zero?
|
||||
""
|
||||
else
|
||||
message_value[:str_val][:data].read_string_length(message_value[:str_val][:size]).force_encoding("UTF-8").freeze
|
||||
end
|
||||
when :bytes
|
||||
if message_value[:str_val][:size].zero?
|
||||
""
|
||||
else
|
||||
message_value[:str_val][:data].read_string_length(message_value[:str_val][:size]).force_encoding("ASCII-8BIT").freeze
|
||||
end
|
||||
when :float
|
||||
message_value[:float_val]
|
||||
when :enum
|
||||
EnumDescriptor.send(:lookup_value, msg_or_enum_def, message_value[:int32_val]) || message_value[:int32_val]
|
||||
when :message
|
||||
raise "Null Arena for message" if arena.nil?
|
||||
Descriptor.send(:get_message, message_value[:msg_val], msg_or_enum_def, arena)
|
||||
else
|
||||
raise RuntimeError.new "Unexpected type #{c_type}"
|
||||
end
|
||||
end
|
||||
|
||||
def to_h_internal(msg, message_descriptor)
|
||||
return nil if msg.nil? or msg.null?
|
||||
hash = {}
|
||||
iter = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
iter.write(:size_t, Google::Protobuf::FFI::Upb_Message_Begin)
|
||||
message_value = Google::Protobuf::FFI::MessageValue.new
|
||||
field_def_ptr = ::FFI::MemoryPointer.new :pointer
|
||||
|
||||
while Google::Protobuf::FFI::message_next(msg, message_descriptor, nil, field_def_ptr, message_value, iter) do
|
||||
field_descriptor = FieldDescriptor.from_native field_def_ptr.get_pointer(0)
|
||||
|
||||
if field_descriptor.map?
|
||||
hash_entry = map_create_hash(message_value[:map_val], field_descriptor)
|
||||
elsif field_descriptor.repeated?
|
||||
hash_entry = repeated_field_create_array(message_value[:array_val], field_descriptor, field_descriptor.type)
|
||||
else
|
||||
hash_entry = scalar_create_hash(message_value, field_descriptor.type, field_descriptor: field_descriptor)
|
||||
end
|
||||
|
||||
hash[field_descriptor.name.to_sym] = hash_entry
|
||||
end
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
def map_create_hash(map_ptr, field_descriptor)
|
||||
return {} if map_ptr.nil? or map_ptr.null?
|
||||
return_value = {}
|
||||
|
||||
message_descriptor = field_descriptor.send(:subtype)
|
||||
key_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 1)
|
||||
key_field_type = Google::Protobuf::FFI.get_type(key_field_def)
|
||||
|
||||
value_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 2)
|
||||
value_field_type = Google::Protobuf::FFI.get_type(value_field_def)
|
||||
|
||||
iter = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
iter.write(:size_t, Google::Protobuf::FFI::Upb_Map_Begin)
|
||||
while Google::Protobuf::FFI.map_next(map_ptr, iter) do
|
||||
iter_size_t = iter.read(:size_t)
|
||||
key_message_value = Google::Protobuf::FFI.map_key(map_ptr, iter_size_t)
|
||||
value_message_value = Google::Protobuf::FFI.map_value(map_ptr, iter_size_t)
|
||||
hash_key = convert_upb_to_ruby(key_message_value, key_field_type)
|
||||
hash_value = scalar_create_hash(value_message_value, value_field_type, msg_or_enum_descriptor: value_field_def.subtype)
|
||||
return_value[hash_key] = hash_value
|
||||
end
|
||||
return_value
|
||||
end
|
||||
|
||||
def repeated_field_create_array(array, field_descriptor, type)
|
||||
return_value = []
|
||||
n = (array.nil? || array.null?) ? 0 : Google::Protobuf::FFI.array_size(array)
|
||||
0.upto(n - 1) do |i|
|
||||
message_value = Google::Protobuf::FFI.get_msgval_at(array, i)
|
||||
return_value << scalar_create_hash(message_value, type, field_descriptor: field_descriptor)
|
||||
end
|
||||
return_value
|
||||
end
|
||||
|
||||
# @param field_descriptor [FieldDescriptor] Descriptor of the field to convert to a hash.
|
||||
def scalar_create_hash(message_value, type, field_descriptor: nil, msg_or_enum_descriptor: nil)
|
||||
if [:message, :enum].include? type
|
||||
if field_descriptor.nil?
|
||||
if msg_or_enum_descriptor.nil?
|
||||
raise "scalar_create_hash requires either a FieldDescriptor, MessageDescriptor, or EnumDescriptor as an argument, but received only nil"
|
||||
end
|
||||
else
|
||||
msg_or_enum_descriptor = field_descriptor.subtype
|
||||
end
|
||||
if type == :message
|
||||
to_h_internal(message_value[:msg_val], msg_or_enum_descriptor)
|
||||
elsif type == :enum
|
||||
convert_upb_to_ruby message_value, type, msg_or_enum_descriptor
|
||||
end
|
||||
else
|
||||
convert_upb_to_ruby message_value, type
|
||||
end
|
||||
end
|
||||
|
||||
def message_value_deep_copy(message_value, type, descriptor, arena)
|
||||
raise unless message_value.is_a? Google::Protobuf::FFI::MessageValue
|
||||
new_message_value = Google::Protobuf::FFI::MessageValue.new
|
||||
case type
|
||||
when :string, :bytes
|
||||
# TODO - how important is it to still use arena malloc, versus using FFI MemoryPointers?
|
||||
new_message_value[:str_val][:size] = message_value[:str_val][:size]
|
||||
new_message_value[:str_val][:data] = Google::Protobuf::FFI.arena_malloc(arena, message_value[:str_val][:size])
|
||||
raise NoMemoryError.new "Allocation failed" if new_message_value[:str_val][:data].nil? or new_message_value[:str_val][:data].null?
|
||||
Google::Protobuf::FFI.memcpy(new_message_value[:str_val][:data], message_value[:str_val][:data], message_value[:str_val][:size])
|
||||
when :message
|
||||
new_message_value[:msg_val] = descriptor.msgclass.send(:deep_copy, message_value[:msg_val], arena).instance_variable_get(:@msg)
|
||||
else
|
||||
Google::Protobuf::FFI.memcpy(new_message_value.to_ptr, message_value.to_ptr, Google::Protobuf::FFI::MessageValue.size)
|
||||
end
|
||||
new_message_value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2023 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
module Internal
|
||||
module PointerHelper
|
||||
# Utility code to defensively walk the object graph from a file_def to
|
||||
# the pool, and either retrieve the wrapper object for the given pointer
|
||||
# or create one. Assumes that the caller is the wrapper class for the
|
||||
# given pointer and that it implements `private_constructor`.
|
||||
def descriptor_from_file_def(file_def, pointer)
|
||||
raise RuntimeError.new "FileDef is nil" if file_def.nil?
|
||||
raise RuntimeError.new "FileDef is null" if file_def.null?
|
||||
pool_def = Google::Protobuf::FFI.file_def_pool file_def
|
||||
raise RuntimeError.new "PoolDef is nil" if pool_def.nil?
|
||||
raise RuntimeError.new "PoolDef is null" if pool_def.null?
|
||||
pool = Google::Protobuf::OBJECT_CACHE.get(pool_def.address)
|
||||
raise "Cannot find pool in ObjectCache!" if pool.nil?
|
||||
descriptor = pool.descriptor_class_by_def[pointer.address]
|
||||
if descriptor.nil?
|
||||
pool.descriptor_class_by_def[pointer.address] = private_constructor(pointer, pool)
|
||||
else
|
||||
descriptor
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2022 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
# A to_native DataConverter method that raises an error if the value is not of the same type.
|
||||
# Adapted from to https://www.varvet.com/blog/advanced-topics-in-ruby-ffi/
|
||||
module Google
|
||||
module Protobuf
|
||||
module Internal
|
||||
module TypeSafety
|
||||
def to_native(value, ctx = nil)
|
||||
if value.kind_of?(self) or value.nil?
|
||||
super
|
||||
else
|
||||
raise TypeError.new "Expected a kind of #{name}, was #{value.class}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+433
@@ -0,0 +1,433 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2022 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
class FFI
|
||||
# Map
|
||||
attach_function :map_clear, :upb_Map_Clear, [:Map], :void
|
||||
attach_function :map_delete, :upb_Map_Delete, [:Map, MessageValue.by_value, MessageValue.by_ref], :bool
|
||||
attach_function :map_get, :upb_Map_Get, [:Map, MessageValue.by_value, MessageValue.by_ref], :bool
|
||||
attach_function :create_map, :upb_Map_New, [Internal::Arena, CType, CType], :Map
|
||||
attach_function :map_size, :upb_Map_Size, [:Map], :size_t
|
||||
attach_function :map_set, :upb_Map_Set, [:Map, MessageValue.by_value, MessageValue.by_value, Internal::Arena], :bool
|
||||
attach_function :map_freeze, :upb_Map_Freeze, [:Map, MiniTable.by_ref], :void
|
||||
attach_function :map_frozen?, :upb_Map_IsFrozen, [:Map], :bool
|
||||
|
||||
# MapIterator
|
||||
attach_function :map_next, :upb_MapIterator_Next, [:Map, :pointer], :bool
|
||||
attach_function :map_done, :upb_MapIterator_Done, [:Map, :size_t], :bool
|
||||
attach_function :map_key, :upb_MapIterator_Key, [:Map, :size_t], MessageValue.by_value
|
||||
attach_function :map_value, :upb_MapIterator_Value, [:Map, :size_t], MessageValue.by_value
|
||||
end
|
||||
class Map
|
||||
include Enumerable
|
||||
##
|
||||
# call-seq:
|
||||
# Map.new(key_type, value_type, value_typeclass = nil, init_hashmap = {})
|
||||
# => new map
|
||||
#
|
||||
# Allocates a new Map container. This constructor may be called with 2, 3, or 4
|
||||
# arguments. The first two arguments are always present and are symbols (taking
|
||||
# on the same values as field-type symbols in message descriptors) that
|
||||
# indicate the type of the map key and value fields.
|
||||
#
|
||||
# The supported key types are: :int32, :int64, :uint32, :uint64, :bool,
|
||||
# :string, :bytes.
|
||||
#
|
||||
# The supported value types are: :int32, :int64, :uint32, :uint64, :bool,
|
||||
# :string, :bytes, :enum, :message.
|
||||
#
|
||||
# The third argument, value_typeclass, must be present if value_type is :enum
|
||||
# or :message. As in RepeatedField#new, this argument must be a message class
|
||||
# (for :message) or enum module (for :enum).
|
||||
#
|
||||
# The last argument, if present, provides initial content for map. Note that
|
||||
# this may be an ordinary Ruby hashmap or another Map instance with identical
|
||||
# key and value types. Also note that this argument may be present whether or
|
||||
# not value_typeclass is present (and it is unambiguously separate from
|
||||
# value_typeclass because value_typeclass's presence is strictly determined by
|
||||
# value_type). The contents of this initial hashmap or Map instance are
|
||||
# shallow-copied into the new Map: the original map is unmodified, but
|
||||
# references to underlying objects will be shared if the value type is a
|
||||
# message type.
|
||||
def self.new(key_type, value_type, value_typeclass = nil, init_hashmap = {})
|
||||
instance = allocate
|
||||
# TODO This argument mangling doesn't agree with the type signature,
|
||||
# but does align with the text of the comments and is required to make unit tests pass.
|
||||
if init_hashmap.empty? and ![:enum, :message].include?(value_type)
|
||||
init_hashmap = value_typeclass
|
||||
value_typeclass = nil
|
||||
end
|
||||
instance.send(:initialize, key_type, value_type, value_type_class: value_typeclass, initial_values: init_hashmap)
|
||||
instance
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# Map.keys => [list_of_keys]
|
||||
#
|
||||
# Returns the list of keys contained in the map, in unspecified order.
|
||||
def keys
|
||||
return_value = []
|
||||
internal_iterator do |iterator|
|
||||
key_message_value = Google::Protobuf::FFI.map_key(@map_ptr, iterator)
|
||||
return_value << convert_upb_to_ruby(key_message_value, key_type)
|
||||
end
|
||||
return_value
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# Map.values => [list_of_values]
|
||||
#
|
||||
# Returns the list of values contained in the map, in unspecified order.
|
||||
def values
|
||||
return_value = []
|
||||
internal_iterator do |iterator|
|
||||
value_message_value = Google::Protobuf::FFI.map_value(@map_ptr, iterator)
|
||||
return_value << convert_upb_to_ruby(value_message_value, value_type, descriptor, arena)
|
||||
end
|
||||
return_value
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# Map.[](key) => value
|
||||
#
|
||||
# Accesses the element at the given key. Throws an exception if the key type is
|
||||
# incorrect. Returns nil when the key is not present in the map.
|
||||
def [](key)
|
||||
value = Google::Protobuf::FFI::MessageValue.new
|
||||
key_message_value = convert_ruby_to_upb(key, arena, key_type, nil)
|
||||
if Google::Protobuf::FFI.map_get(@map_ptr, key_message_value, value)
|
||||
convert_upb_to_ruby(value, value_type, descriptor, arena)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# Map.[]=(key, value) => value
|
||||
#
|
||||
# Inserts or overwrites the value at the given key with the given new value.
|
||||
# Throws an exception if the key type is incorrect. Returns the new value that
|
||||
# was just inserted.
|
||||
def []=(key, value)
|
||||
raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
|
||||
key_message_value = convert_ruby_to_upb(key, arena, key_type, nil)
|
||||
value_message_value = convert_ruby_to_upb(value, arena, value_type, descriptor)
|
||||
Google::Protobuf::FFI.map_set(@map_ptr, key_message_value, value_message_value, arena)
|
||||
value
|
||||
end
|
||||
|
||||
def has_key?(key)
|
||||
key_message_value = convert_ruby_to_upb(key, arena, key_type, nil)
|
||||
Google::Protobuf::FFI.map_get(@map_ptr, key_message_value, nil)
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# Map.delete(key) => old_value
|
||||
#
|
||||
# Deletes the value at the given key, if any, returning either the old value or
|
||||
# nil if none was present. Throws an exception if the key is of the wrong type.
|
||||
def delete(key)
|
||||
raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
|
||||
value = Google::Protobuf::FFI::MessageValue.new
|
||||
key_message_value = convert_ruby_to_upb(key, arena, key_type, nil)
|
||||
if Google::Protobuf::FFI.map_delete(@map_ptr, key_message_value, value)
|
||||
convert_upb_to_ruby(value, value_type, descriptor, arena)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def clear
|
||||
raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
|
||||
Google::Protobuf::FFI.map_clear(@map_ptr)
|
||||
nil
|
||||
end
|
||||
|
||||
def length
|
||||
Google::Protobuf::FFI.map_size(@map_ptr)
|
||||
end
|
||||
alias size length
|
||||
|
||||
##
|
||||
# Is this object frozen?
|
||||
# Returns true if either this Ruby wrapper or the underlying
|
||||
# representation are frozen. Freezes the wrapper if the underlying
|
||||
# representation is already frozen but this wrapper isn't.
|
||||
def frozen?
|
||||
unless Google::Protobuf::FFI.map_frozen? @map_ptr
|
||||
raise RuntimeError.new "Ruby frozen Map with mutable representation" if super
|
||||
return false
|
||||
end
|
||||
method(:freeze).super_method.call unless super
|
||||
true
|
||||
end
|
||||
|
||||
##
|
||||
# Freezes the map object. We have to intercept this so we can freeze the
|
||||
# underlying representation, not just the Ruby wrapper. Returns self.
|
||||
def freeze
|
||||
if method(:frozen?).super_method.call
|
||||
unless Google::Protobuf::FFI.map_frozen? @map_ptr
|
||||
raise RuntimeError.new "Underlying representation of map still mutable despite frozen wrapper"
|
||||
end
|
||||
return self
|
||||
end
|
||||
unless Google::Protobuf::FFI.map_frozen? @map_ptr
|
||||
mini_table = (value_type == :message) ? Google::Protobuf::FFI.get_mini_table(@descriptor) : nil
|
||||
Google::Protobuf::FFI.map_freeze(@map_ptr, mini_table)
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# Map.dup => new_map
|
||||
#
|
||||
# Duplicates this map with a shallow copy. References to all non-primitive
|
||||
# element objects (e.g., submessages) are shared.
|
||||
def dup
|
||||
internal_dup
|
||||
end
|
||||
alias clone dup
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# Map.==(other) => boolean
|
||||
#
|
||||
# Compares this map to another. Maps are equal if they have identical key sets,
|
||||
# and for each key, the values in both maps compare equal. Elements are
|
||||
# compared as per normal Ruby semantics, by calling their :== methods (or
|
||||
# performing a more efficient comparison for primitive types).
|
||||
#
|
||||
# Maps with dissimilar key types or value types/typeclasses are never equal,
|
||||
# even if value comparison (for example, between integers and floats) would
|
||||
# have otherwise indicated that every element has equal value.
|
||||
def ==(other)
|
||||
if other.is_a? Hash
|
||||
other = self.class.send(:private_constructor, key_type, value_type, descriptor, initial_values: other)
|
||||
elsif !other.is_a? Google::Protobuf::Map
|
||||
return false
|
||||
end
|
||||
|
||||
return true if object_id == other.object_id
|
||||
return false if key_type != other.send(:key_type) or value_type != other.send(:value_type) or descriptor != other.send(:descriptor) or length != other.length
|
||||
other_map_ptr = other.send(:map_ptr)
|
||||
each_msg_val do |key_message_value, value_message_value|
|
||||
other_value = Google::Protobuf::FFI::MessageValue.new
|
||||
return false unless Google::Protobuf::FFI.map_get(other_map_ptr, key_message_value, other_value)
|
||||
return false unless Google::Protobuf::FFI.message_value_equal(value_message_value, other_value, value_type, descriptor)
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def hash
|
||||
return_value = 0
|
||||
each_msg_val do |key_message_value, value_message_value|
|
||||
return_value = Google::Protobuf::FFI.message_value_hash(key_message_value, key_type, nil, return_value)
|
||||
return_value = Google::Protobuf::FFI.message_value_hash(value_message_value, value_type, descriptor, return_value)
|
||||
end
|
||||
return_value
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# Map.to_h => {}
|
||||
#
|
||||
# Returns a Ruby Hash object containing all the values within the map
|
||||
def to_h
|
||||
return {} if map_ptr.nil? or map_ptr.null?
|
||||
return_value = {}
|
||||
each_msg_val do |key_message_value, value_message_value|
|
||||
hash_key = convert_upb_to_ruby(key_message_value, key_type)
|
||||
hash_value = scalar_create_hash(value_message_value, value_type, msg_or_enum_descriptor: descriptor)
|
||||
return_value[hash_key] = hash_value
|
||||
end
|
||||
return_value
|
||||
end
|
||||
|
||||
def inspect
|
||||
key_value_pairs = []
|
||||
each_msg_val do |key_message_value, value_message_value|
|
||||
key_string = convert_upb_to_ruby(key_message_value, key_type).inspect
|
||||
if value_type == :message
|
||||
sub_msg_descriptor = Google::Protobuf::FFI.get_subtype_as_message(descriptor)
|
||||
value_string = sub_msg_descriptor.msgclass.send(:inspect_internal, value_message_value[:msg_val])
|
||||
else
|
||||
value_string = convert_upb_to_ruby(value_message_value, value_type, descriptor).inspect
|
||||
end
|
||||
key_value_pairs << "#{key_string}=>#{value_string}"
|
||||
end
|
||||
"{#{key_value_pairs.join(", ")}}"
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# Map.merge(other_map) => map
|
||||
#
|
||||
# Copies key/value pairs from other_map into a copy of this map. If a key is
|
||||
# set in other_map and this map, the value from other_map overwrites the value
|
||||
# in the new copy of this map. Returns the new copy of this map with merged
|
||||
# contents.
|
||||
def merge(other)
|
||||
internal_merge(other)
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# Map.each(&block)
|
||||
#
|
||||
# Invokes &block on each |key, value| pair in the map, in unspecified order.
|
||||
# Note that Map also includes Enumerable; map thus acts like a normal Ruby
|
||||
# sequence.
|
||||
def each &block
|
||||
each_msg_val do |key_message_value, value_message_value|
|
||||
key_value = convert_upb_to_ruby(key_message_value, key_type)
|
||||
value_value = convert_upb_to_ruby(value_message_value, value_type, descriptor, arena)
|
||||
yield key_value, value_value
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
attr :arena, :map_ptr, :key_type, :value_type, :descriptor, :name
|
||||
|
||||
include Google::Protobuf::Internal::Convert
|
||||
|
||||
def internal_iterator
|
||||
iter = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
iter.write(:size_t, Google::Protobuf::FFI::Upb_Map_Begin)
|
||||
while Google::Protobuf::FFI.map_next(@map_ptr, iter) do
|
||||
iter_size_t = iter.read(:size_t)
|
||||
yield iter_size_t
|
||||
end
|
||||
end
|
||||
|
||||
def each_msg_val &block
|
||||
internal_iterator do |iterator|
|
||||
key_message_value = Google::Protobuf::FFI.map_key(@map_ptr, iterator)
|
||||
value_message_value = Google::Protobuf::FFI.map_value(@map_ptr, iterator)
|
||||
yield key_message_value, value_message_value
|
||||
end
|
||||
end
|
||||
|
||||
def internal_dup
|
||||
instance = self.class.send(:private_constructor, key_type, value_type, descriptor, arena: arena)
|
||||
new_map_ptr = instance.send(:map_ptr)
|
||||
each_msg_val do |key_message_value, value_message_value|
|
||||
Google::Protobuf::FFI.map_set(new_map_ptr, key_message_value, value_message_value, arena)
|
||||
end
|
||||
instance
|
||||
end
|
||||
|
||||
def internal_merge_into_self(other)
|
||||
case other
|
||||
when Hash
|
||||
other.each do |key, value|
|
||||
key_message_value = convert_ruby_to_upb(key, arena, key_type, nil)
|
||||
value_message_value = convert_ruby_to_upb(value, arena, value_type, descriptor)
|
||||
Google::Protobuf::FFI.map_set(@map_ptr, key_message_value, value_message_value, arena)
|
||||
end
|
||||
when Google::Protobuf::Map
|
||||
unless key_type == other.send(:key_type) and value_type == other.send(:value_type) and descriptor == other.descriptor
|
||||
raise ArgumentError.new "Attempt to merge Map with mismatching types" #TODO Improve error message by adding type information
|
||||
end
|
||||
arena.fuse(other.send(:arena))
|
||||
iter = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
iter.write(:size_t, Google::Protobuf::FFI::Upb_Map_Begin)
|
||||
other.send(:each_msg_val) do |key_message_value, value_message_value|
|
||||
Google::Protobuf::FFI.map_set(@map_ptr, key_message_value, value_message_value, arena)
|
||||
end
|
||||
else
|
||||
raise ArgumentError.new "Unknown type merging into Map" #TODO improve this error message by including type information
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def internal_merge(other)
|
||||
internal_dup.internal_merge_into_self(other)
|
||||
end
|
||||
|
||||
def initialize(key_type, value_type, value_type_class: nil, initial_values: nil, arena: nil, map: nil, descriptor: nil, name: nil)
|
||||
@name = name || 'Map'
|
||||
|
||||
unless [:int32, :int64, :uint32, :uint64, :bool, :string, :bytes].include? key_type
|
||||
raise ArgumentError.new "Invalid key type for map." #TODO improve error message to include what type was passed
|
||||
end
|
||||
@key_type = key_type
|
||||
|
||||
unless [:int32, :int64, :uint32, :uint64, :bool, :string, :bytes, :enum, :message].include? value_type
|
||||
raise ArgumentError.new "Invalid value type for map." #TODO improve error message to include what type was passed
|
||||
end
|
||||
@value_type = value_type
|
||||
|
||||
if !descriptor.nil?
|
||||
raise ArgumentError "Expected descriptor to be a Descriptor or EnumDescriptor" unless [EnumDescriptor, Descriptor].include? descriptor.class
|
||||
@descriptor = descriptor
|
||||
elsif [:message, :enum].include? value_type
|
||||
raise ArgumentError.new "Expected at least 3 arguments for message/enum." if value_type_class.nil?
|
||||
descriptor = value_type_class.respond_to?(:descriptor) ? value_type_class.descriptor : nil
|
||||
raise ArgumentError.new "Type class #{value_type_class} has no descriptor. Please pass a class or enum as returned by the DescriptorPool." if descriptor.nil?
|
||||
@descriptor = descriptor
|
||||
else
|
||||
@descriptor = nil
|
||||
end
|
||||
|
||||
@arena = arena || Google::Protobuf::FFI.create_arena
|
||||
@map_ptr = map || Google::Protobuf::FFI.create_map(@arena, @key_type, @value_type)
|
||||
|
||||
internal_merge_into_self(initial_values) unless initial_values.nil?
|
||||
|
||||
# Should always be the last expression of the initializer to avoid
|
||||
# leaking references to this object before construction is complete.
|
||||
OBJECT_CACHE.try_add(@map_ptr.address, self)
|
||||
end
|
||||
|
||||
##
|
||||
# Constructor that uses the type information from the given
|
||||
# FieldDescriptor to configure the new Map instance.
|
||||
# @param field [FieldDescriptor] Type information for the new Map
|
||||
# @param arena [Arena] Owning message's arena
|
||||
# @param value [Hash|Map] Initial value
|
||||
# @param map [::FFI::Pointer] Existing upb_Map
|
||||
def self.construct_for_field(field, arena: nil, value: nil, map: nil)
|
||||
raise ArgumentError.new "Expected Hash object as initializer value for map field '#{field.name}' (given #{value.class})." unless value.nil? or value.is_a? Hash
|
||||
instance = allocate
|
||||
raise ArgumentError.new "Expected field with type :message, instead got #{field.class}" unless field.type == :message
|
||||
message_descriptor = field.send(:subtype)
|
||||
key_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 1)
|
||||
key_field_type = Google::Protobuf::FFI.get_type(key_field_def)
|
||||
|
||||
value_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 2)
|
||||
value_field_type = Google::Protobuf::FFI.get_type(value_field_def)
|
||||
instance.send(:initialize, key_field_type, value_field_type, initial_values: value, name: field.name, arena: arena, map: map, descriptor: value_field_def.subtype)
|
||||
instance
|
||||
end
|
||||
|
||||
def self.private_constructor(key_type, value_type, descriptor, initial_values: nil, arena: nil)
|
||||
instance = allocate
|
||||
instance.send(:initialize, key_type, value_type, descriptor: descriptor, initial_values: initial_values, arena: arena)
|
||||
instance
|
||||
end
|
||||
|
||||
extend Google::Protobuf::Internal::Convert
|
||||
|
||||
def self.deep_copy(map)
|
||||
instance = allocate
|
||||
instance.send(:initialize, map.send(:key_type), map.send(:value_type), descriptor: map.send(:descriptor))
|
||||
map.send(:each_msg_val) do |key_message_value, value_message_value|
|
||||
Google::Protobuf::FFI.map_set(instance.send(:map_ptr), key_message_value, message_value_deep_copy(value_message_value, map.send(:value_type), map.send(:descriptor), instance.send(:arena)), instance.send(:arena))
|
||||
end
|
||||
instance
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+785
@@ -0,0 +1,785 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2023 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
|
||||
# Decorates Descriptor with the `build_message_class` method that defines
|
||||
# Message classes.
|
||||
module Google
|
||||
module Protobuf
|
||||
class FFI
|
||||
# Message
|
||||
attach_function :clear_message_field, :upb_Message_ClearFieldByDef, [:Message, FieldDescriptor], :void
|
||||
attach_function :get_message_value, :upb_Message_GetFieldByDef, [:Message, FieldDescriptor], MessageValue.by_value
|
||||
attach_function :get_message_has, :upb_Message_HasFieldByDef, [:Message, FieldDescriptor], :bool
|
||||
attach_function :set_message_field, :upb_Message_SetFieldByDef, [:Message, FieldDescriptor, MessageValue.by_value, Internal::Arena], :bool
|
||||
attach_function :encode_message, :upb_Encode, [:Message, MiniTable.by_ref, :size_t, Internal::Arena, :pointer, :pointer], EncodeStatus
|
||||
attach_function :json_decode_message_detecting_nonconformance, :upb_JsonDecodeDetectingNonconformance, [:binary_string, :size_t, :Message, Descriptor, :DefPool, :int, Internal::Arena, Status.by_ref], :int
|
||||
attach_function :json_encode_message, :upb_JsonEncode, [:Message, Descriptor, :DefPool, :int, :binary_string, :size_t, Status.by_ref], :size_t
|
||||
attach_function :decode_message, :upb_Decode, [:binary_string, :size_t, :Message, MiniTable.by_ref, :ExtensionRegistry, :int, Internal::Arena], DecodeStatus
|
||||
attach_function :get_mutable_message, :upb_Message_Mutable, [:Message, FieldDescriptor, Internal::Arena], MutableMessageValue.by_value
|
||||
attach_function :get_message_which_oneof, :upb_Message_WhichOneofByDef, [:Message, OneofDescriptor], FieldDescriptor
|
||||
attach_function :message_discard_unknown, :upb_Message_DiscardUnknown, [:Message, Descriptor, :int], :bool
|
||||
attach_function :message_next, :upb_Message_Next, [:Message, Descriptor, :DefPool, :FieldDefPointer, MessageValue.by_ref, :pointer], :bool
|
||||
attach_function :message_freeze, :upb_Message_Freeze, [:Message, MiniTable.by_ref], :void
|
||||
attach_function :message_frozen?, :upb_Message_IsFrozen, [:Message], :bool
|
||||
|
||||
# MessageValue
|
||||
attach_function :message_value_equal, :shared_Msgval_IsEqual, [MessageValue.by_value, MessageValue.by_value, CType, Descriptor], :bool
|
||||
attach_function :message_value_hash, :shared_Msgval_GetHash, [MessageValue.by_value, CType, Descriptor, :uint64_t], :uint64_t
|
||||
end
|
||||
|
||||
class Descriptor
|
||||
def build_message_class
|
||||
descriptor = self
|
||||
Class.new(Google::Protobuf::const_get(:AbstractMessage)) do
|
||||
@descriptor = descriptor
|
||||
class << self
|
||||
attr_accessor :descriptor
|
||||
private
|
||||
attr_accessor :oneof_field_names
|
||||
include ::Google::Protobuf::Internal::Convert
|
||||
end
|
||||
|
||||
alias original_method_missing method_missing
|
||||
def method_missing(method_name, *args)
|
||||
method_missing_internal method_name, *args, mode: :method_missing
|
||||
end
|
||||
|
||||
def respond_to_missing?(method_name, include_private = false)
|
||||
method_missing_internal(method_name, mode: :respond_to_missing?) || super
|
||||
end
|
||||
|
||||
##
|
||||
# Public constructor. Automatically allocates from a new Arena.
|
||||
def self.new(initial_value = nil)
|
||||
instance = allocate
|
||||
instance.send(:initialize, initial_value)
|
||||
instance
|
||||
end
|
||||
|
||||
##
|
||||
# Is this object frozen?
|
||||
# Returns true if either this Ruby wrapper or the underlying
|
||||
# representation are frozen. Freezes the wrapper if the underlying
|
||||
# representation is already frozen but this wrapper isn't.
|
||||
def frozen?
|
||||
unless Google::Protobuf::FFI.message_frozen? @msg
|
||||
raise RuntimeError.new "Ruby frozen Message with mutable representation" if super
|
||||
return false
|
||||
end
|
||||
method(:freeze).super_method.call unless super
|
||||
true
|
||||
end
|
||||
|
||||
##
|
||||
# Freezes the map object. We have to intercept this so we can freeze the
|
||||
# underlying representation, not just the Ruby wrapper. Returns self.
|
||||
def freeze
|
||||
if method(:frozen?).super_method.call
|
||||
unless Google::Protobuf::FFI.message_frozen? @msg
|
||||
raise RuntimeError.new "Underlying representation of message still mutable despite frozen wrapper"
|
||||
end
|
||||
return self
|
||||
end
|
||||
unless Google::Protobuf::FFI.message_frozen? @msg
|
||||
Google::Protobuf::FFI.message_freeze(@msg, Google::Protobuf::FFI.get_mini_table(self.class.descriptor))
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def dup
|
||||
duplicate = self.class.private_constructor(@arena)
|
||||
mini_table = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
|
||||
size = mini_table[:size]
|
||||
duplicate.instance_variable_get(:@msg).write_string_length(@msg.read_string_length(size), size)
|
||||
duplicate
|
||||
end
|
||||
alias clone dup
|
||||
|
||||
def eql?(other)
|
||||
return false unless self.class === other
|
||||
encoding_options = Google::Protobuf::FFI::Upb_Encode_Deterministic | Google::Protobuf::FFI::Upb_Encode_SkipUnknown
|
||||
temporary_arena = Google::Protobuf::FFI.create_arena
|
||||
mini_table = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
|
||||
size_one = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
encoding_one = ::FFI::MemoryPointer.new(:pointer, 1)
|
||||
encoding_status = Google::Protobuf::FFI.encode_message(@msg, mini_table, encoding_options, temporary_arena, encoding_one.to_ptr, size_one)
|
||||
raise ParseError.new "Error comparing messages due to #{encoding_status} while encoding LHS of `eql?()`" unless encoding_status == :Ok
|
||||
|
||||
size_two = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
encoding_two = ::FFI::MemoryPointer.new(:pointer, 1)
|
||||
encoding_status = Google::Protobuf::FFI.encode_message(other.instance_variable_get(:@msg), mini_table, encoding_options, temporary_arena, encoding_two.to_ptr, size_two)
|
||||
raise ParseError.new "Error comparing messages due to #{encoding_status} while encoding RHS of `eql?()`" unless encoding_status == :Ok
|
||||
|
||||
if encoding_one.null? or encoding_two.null?
|
||||
raise ParseError.new "Error comparing messages"
|
||||
end
|
||||
size_one.read(:size_t) == size_two.read(:size_t) and Google::Protobuf::FFI.memcmp(encoding_one.read(:pointer), encoding_two.read(:pointer), size_one.read(:size_t)).zero?
|
||||
end
|
||||
alias == eql?
|
||||
|
||||
def hash
|
||||
encoding_options = Google::Protobuf::FFI::Upb_Encode_Deterministic | Google::Protobuf::FFI::Upb_Encode_SkipUnknown
|
||||
temporary_arena = Google::Protobuf::FFI.create_arena
|
||||
mini_table_ptr = Google::Protobuf::FFI.get_mini_table(self.class.descriptor)
|
||||
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
encoding = ::FFI::MemoryPointer.new(:pointer, 1)
|
||||
encoding_status = Google::Protobuf::FFI.encode_message(@msg, mini_table_ptr, encoding_options, temporary_arena, encoding.to_ptr, size_ptr)
|
||||
if encoding_status != :Ok or encoding.null?
|
||||
raise ParseError.new "Error calculating hash"
|
||||
end
|
||||
encoding.read(:pointer).read_string(size_ptr.read(:size_t)).hash
|
||||
end
|
||||
|
||||
def to_h
|
||||
to_h_internal @msg, self.class.descriptor
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# Message.inspect => string
|
||||
#
|
||||
# Returns a human-readable string representing this message. It will be
|
||||
# formatted as "<MessageType: field1: value1, field2: value2, ...>". Each
|
||||
# field's value is represented according to its own #inspect method.
|
||||
def inspect
|
||||
self.class.inspect_internal @msg
|
||||
end
|
||||
|
||||
def to_s
|
||||
self.inspect
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# Message.[](index) => value
|
||||
# Accesses a field's value by field name. The provided field name
|
||||
# should be a string.
|
||||
def [](name)
|
||||
raise TypeError.new "Expected String for name but got #{name.class}" unless name.is_a? String
|
||||
index_internal name
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# Message.[]=(index, value)
|
||||
# Sets a field's value by field name. The provided field name should
|
||||
# be a string.
|
||||
# @param name [String] Name of the field to be set
|
||||
# @param value [Object] Value to set the field to
|
||||
def []=(name, value)
|
||||
raise TypeError.new "Expected String for name but got #{name.class}" unless name.is_a? String
|
||||
index_assign_internal(value, name: name)
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# MessageClass.decode(data, options) => message
|
||||
#
|
||||
# Decodes the given data (as a string containing bytes in protocol buffers wire
|
||||
# format) under the interpretation given by this message class's definition
|
||||
# and returns a message object with the corresponding field values.
|
||||
# @param data [String] Binary string in Protobuf wire format to decode
|
||||
# @param options [Hash] options for the decoder
|
||||
# @option options [Integer] :recursion_limit Set to maximum decoding depth for message (default is 64)
|
||||
def self.decode(data, options = {})
|
||||
raise ArgumentError.new "Expected hash arguments." unless options.is_a? Hash
|
||||
raise ArgumentError.new "Expected string for binary protobuf data." unless data.is_a? String
|
||||
decoding_options = 0
|
||||
depth = options[:recursion_limit]
|
||||
|
||||
if depth.is_a? Numeric
|
||||
decoding_options |= Google::Protobuf::FFI.decode_max_depth(depth.to_i)
|
||||
end
|
||||
|
||||
message = new
|
||||
mini_table_ptr = Google::Protobuf::FFI.get_mini_table(message.class.descriptor)
|
||||
status = Google::Protobuf::FFI.decode_message(
|
||||
data,
|
||||
data.bytesize,
|
||||
message.instance_variable_get(:@msg),
|
||||
mini_table_ptr,
|
||||
Google::Protobuf::FFI.get_extension_registry(message.class.descriptor.send(:pool).descriptor_pool),
|
||||
decoding_options,
|
||||
message.instance_variable_get(:@arena)
|
||||
)
|
||||
raise ParseError.new "Error occurred during parsing" unless status == :Ok
|
||||
message
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# MessageClass.encode(msg, options) => bytes
|
||||
#
|
||||
# Encodes the given message object to its serialized form in protocol buffers
|
||||
# wire format.
|
||||
# @param options [Hash] options for the encoder
|
||||
# @option options [Integer] :recursion_limit Set to maximum encoding depth for message (default is 64)
|
||||
def self.encode(message, options = {})
|
||||
raise ArgumentError.new "Message of wrong type." unless message.is_a? self
|
||||
raise ArgumentError.new "Expected hash arguments." unless options.is_a? Hash
|
||||
|
||||
encoding_options = 0
|
||||
depth = options[:recursion_limit]
|
||||
|
||||
if depth.is_a? Numeric
|
||||
encoding_options |= Google::Protobuf::FFI.decode_max_depth(depth.to_i)
|
||||
end
|
||||
|
||||
encode_internal(message.instance_variable_get(:@msg), encoding_options) do |encoding, size, _|
|
||||
if encoding.nil? or encoding.null?
|
||||
raise RuntimeError.new "Exceeded maximum depth (possibly cycle)"
|
||||
else
|
||||
encoding.read_string_length(size).force_encoding("ASCII-8BIT").freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# all-seq:
|
||||
# MessageClass.decode_json(data, options = {}) => message
|
||||
#
|
||||
# Decodes the given data (as a string containing bytes in protocol buffers wire
|
||||
# format) under the interpretation given by this message class's definition
|
||||
# and returns a message object with the corresponding field values.
|
||||
#
|
||||
# @param options [Hash] options for the decoder
|
||||
# @option options [Boolean] :ignore_unknown_fields Set true to ignore unknown fields (default is to raise an error)
|
||||
# @return [Message]
|
||||
def self.decode_json(data, options = {})
|
||||
decoding_options = 0
|
||||
unless options.is_a? Hash
|
||||
if options.respond_to? :to_h
|
||||
options options.to_h
|
||||
else
|
||||
#TODO can this error message be improve to include what was received?
|
||||
raise ArgumentError.new "Expected hash arguments"
|
||||
end
|
||||
end
|
||||
raise ArgumentError.new "Expected string for JSON data." unless data.is_a? String
|
||||
raise RuntimeError.new "Cannot parse a wrapper directly" if descriptor.send(:wrapper?)
|
||||
|
||||
if options[:ignore_unknown_fields]
|
||||
decoding_options |= Google::Protobuf::FFI::Upb_JsonDecode_IgnoreUnknown
|
||||
end
|
||||
|
||||
message = new
|
||||
pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool
|
||||
status = Google::Protobuf::FFI::Status.new
|
||||
result = Google::Protobuf::FFI.json_decode_message_detecting_nonconformance(data, data.bytesize, message.instance_variable_get(:@msg), message.class.descriptor, pool_def, decoding_options, message.instance_variable_get(:@arena), status)
|
||||
case result
|
||||
when Google::Protobuf::FFI::Upb_JsonDecodeResult_OkWithEmptyStringNumerics
|
||||
warn Google::Protobuf::FFI.error_message(status)
|
||||
when Google::Protobuf::FFI::Upb_JsonDecodeResult_Error
|
||||
raise ParseError.new "Error occurred during parsing: #{Google::Protobuf::FFI.error_message(status)}"
|
||||
end
|
||||
message
|
||||
end
|
||||
|
||||
def self.encode_json(message, options = {})
|
||||
encoding_options = 0
|
||||
unless options.is_a? Hash
|
||||
if options.respond_to? :to_h
|
||||
options = options.to_h
|
||||
else
|
||||
#TODO can this error message be improve to include what was received?
|
||||
raise ArgumentError.new "Expected hash arguments"
|
||||
end
|
||||
end
|
||||
|
||||
if options[:preserve_proto_fieldnames]
|
||||
encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_UseProtoNames
|
||||
end
|
||||
if options[:emit_defaults]
|
||||
encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_EmitDefaults
|
||||
end
|
||||
if options[:format_enums_as_integers]
|
||||
encoding_options |= Google::Protobuf::FFI::Upb_JsonEncode_FormatEnumsAsIntegers
|
||||
end
|
||||
|
||||
buffer_size = 1024
|
||||
buffer = ::FFI::MemoryPointer.new(:char, buffer_size)
|
||||
status = Google::Protobuf::FFI::Status.new
|
||||
msg = message.instance_variable_get(:@msg)
|
||||
pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool
|
||||
size = Google::Protobuf::FFI::json_encode_message(msg, message.class.descriptor, pool_def, encoding_options, buffer, buffer_size, status)
|
||||
unless status[:ok]
|
||||
raise ParseError.new "Error occurred during encoding: #{Google::Protobuf::FFI.error_message(status)}"
|
||||
end
|
||||
|
||||
if size >= buffer_size
|
||||
buffer_size = size + 1
|
||||
buffer = ::FFI::MemoryPointer.new(:char, buffer_size)
|
||||
status.clear
|
||||
size = Google::Protobuf::FFI::json_encode_message(msg, message.class.descriptor, pool_def, encoding_options, buffer, buffer_size, status)
|
||||
unless status[:ok]
|
||||
raise ParseError.new "Error occurred during encoding: #{Google::Protobuf::FFI.error_message(status)}"
|
||||
end
|
||||
if size >= buffer_size
|
||||
raise ParseError.new "Inconsistent JSON encoding sizes - was #{buffer_size - 1}, now #{size}"
|
||||
end
|
||||
end
|
||||
|
||||
buffer.read_string_length(size).force_encoding("UTF-8").freeze
|
||||
end
|
||||
|
||||
private
|
||||
# Implementation details below are subject to breaking changes without
|
||||
# warning and are intended for use only within the gem.
|
||||
|
||||
include Google::Protobuf::Internal::Convert
|
||||
|
||||
##
|
||||
# Checks ObjectCache for a sentinel empty frozen Map of the key and
|
||||
# value types matching the field descriptor's MessageDef and returns
|
||||
# the cache entry. If an entry is not found, one is created and added
|
||||
# to the cache keyed by the MessageDef pointer first.
|
||||
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
||||
def empty_frozen_map(field_descriptor)
|
||||
sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
|
||||
frozen_map = OBJECT_CACHE.get sub_message_def
|
||||
if frozen_map.nil?
|
||||
frozen_map = Google::Protobuf::Map.send(:construct_for_field, field_descriptor)
|
||||
OBJECT_CACHE.try_add(sub_message_def, frozen_map.freeze)
|
||||
end
|
||||
raise "Empty Frozen Map is not frozen" unless frozen_map.frozen?
|
||||
frozen_map
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a frozen Map instance for the given field. If the message
|
||||
# already has a value for that field, it is used. If not, a sentinel
|
||||
# (per FieldDescriptor) empty frozen Map is returned instead.
|
||||
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
||||
def frozen_map_from_field_descriptor(field_descriptor)
|
||||
message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
|
||||
return empty_frozen_map field_descriptor if message_value[:map_val].null?
|
||||
get_map_field(message_value[:map_val], field_descriptor).freeze
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a Map instance for the given field. If the message is frozen
|
||||
# the return value is also frozen. If not, a mutable instance is
|
||||
# returned instead.
|
||||
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
||||
def map_from_field_descriptor(field_descriptor)
|
||||
return frozen_map_from_field_descriptor field_descriptor if frozen?
|
||||
mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
|
||||
get_map_field(mutable_message_value[:map], field_descriptor)
|
||||
end
|
||||
|
||||
##
|
||||
# Checks ObjectCache for a sentinel empty frozen RepeatedField of the
|
||||
# value type matching the field descriptor's MessageDef and returns
|
||||
# the cache entry. If an entry is not found, one is created and added
|
||||
# to the cache keyed by the MessageDef pointer first.
|
||||
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
||||
def empty_frozen_repeated_field(field_descriptor)
|
||||
sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
|
||||
frozen_repeated_field = OBJECT_CACHE.get sub_message_def
|
||||
if frozen_repeated_field.nil?
|
||||
frozen_repeated_field = Google::Protobuf::RepeatedField.send(:construct_for_field, field_descriptor)
|
||||
OBJECT_CACHE.try_add(sub_message_def, frozen_repeated_field.freeze)
|
||||
end
|
||||
raise "Empty frozen RepeatedField is not frozen" unless frozen_repeated_field.frozen?
|
||||
frozen_repeated_field
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a frozen RepeatedField instance for the given field. If the
|
||||
# message already has a value for that field, it is used. If not, a
|
||||
# sentinel (per FieldDescriptor) empty frozen RepeatedField is
|
||||
# returned instead.
|
||||
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
||||
def frozen_repeated_field_from_field_descriptor(field_descriptor)
|
||||
message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
|
||||
return empty_frozen_repeated_field field_descriptor if message_value[:array_val].null?
|
||||
get_repeated_field(message_value[:array_val], field_descriptor).freeze
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a RepeatedField instance for the given field. If the message
|
||||
# is frozen the return value is also frozen. If not, a mutable
|
||||
# instance is returned instead.
|
||||
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
||||
def repeated_field_from_field_descriptor(field_descriptor)
|
||||
return frozen_repeated_field_from_field_descriptor field_descriptor if frozen?
|
||||
mutable_message_value = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
|
||||
get_repeated_field(mutable_message_value[:array], field_descriptor)
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a Message instance for the given field. If the message
|
||||
# is frozen nil is always returned. Otherwise, a mutable instance is
|
||||
# returned instead.
|
||||
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
||||
def message_from_field_descriptor(field_descriptor)
|
||||
return nil if frozen?
|
||||
return nil unless Google::Protobuf::FFI.get_message_has @msg, field_descriptor
|
||||
mutable_message = Google::Protobuf::FFI.get_mutable_message @msg, field_descriptor, @arena
|
||||
sub_message = mutable_message[:msg]
|
||||
sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field_descriptor
|
||||
Descriptor.send(:get_message, sub_message, sub_message_def, @arena)
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a scalar value for the given field. If the message
|
||||
# is frozen the return value is also frozen.
|
||||
# @param field_descriptor [FieldDescriptor] Field to retrieve.
|
||||
def scalar_from_field_descriptor(field_descriptor)
|
||||
c_type = field_descriptor.send(:c_type)
|
||||
message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
|
||||
msg_or_enum_def = c_type == :enum ? Google::Protobuf::FFI.get_subtype_as_enum(field_descriptor) : nil
|
||||
return_value = convert_upb_to_ruby message_value, c_type, msg_or_enum_def
|
||||
frozen? ? return_value.freeze : return_value
|
||||
end
|
||||
|
||||
##
|
||||
# Dynamically define accessors methods for every field of @descriptor.
|
||||
# Methods with names that conflict with existing methods are skipped.
|
||||
def self.setup_accessors!
|
||||
@descriptor.each do |field_descriptor|
|
||||
field_name = field_descriptor.name
|
||||
unless instance_methods(true).include?(field_name.to_sym)
|
||||
# Dispatching to either index_internal or get_field is logically
|
||||
# correct, but slightly slower due to having to perform extra
|
||||
# lookups on each invocation rather than doing it once here.
|
||||
if field_descriptor.map?
|
||||
define_method(field_name) do
|
||||
map_from_field_descriptor field_descriptor
|
||||
end
|
||||
elsif field_descriptor.repeated?
|
||||
define_method(field_name) do
|
||||
repeated_field_from_field_descriptor field_descriptor
|
||||
end
|
||||
elsif field_descriptor.sub_message?
|
||||
define_method(field_name) do
|
||||
message_from_field_descriptor field_descriptor
|
||||
end
|
||||
else
|
||||
define_method(field_name) do
|
||||
scalar_from_field_descriptor field_descriptor
|
||||
end
|
||||
end
|
||||
define_method("#{field_name}=") do |value|
|
||||
index_assign_internal(value, field_descriptor: field_descriptor)
|
||||
end
|
||||
define_method("clear_#{field_name}") do
|
||||
clear_internal(field_descriptor)
|
||||
end
|
||||
if field_descriptor.type == :enum
|
||||
if field_descriptor.repeated?
|
||||
define_method("#{field_name}_const") do
|
||||
return_value = []
|
||||
repeated_field_from_field_descriptor(field_descriptor).send(:each_msg_val) do |msg_val|
|
||||
return_value << msg_val[:int32_val]
|
||||
end
|
||||
return_value
|
||||
end
|
||||
else
|
||||
define_method("#{field_name}_const") do
|
||||
message_value = Google::Protobuf::FFI.get_message_value @msg, field_descriptor
|
||||
message_value[:int32_val]
|
||||
end
|
||||
end
|
||||
end
|
||||
if !field_descriptor.repeated? and field_descriptor.wrapper?
|
||||
define_method("#{field_name}_as_value") do
|
||||
get_field(field_descriptor, unwrap: true)
|
||||
end
|
||||
define_method("#{field_name}_as_value=") do |value|
|
||||
if value.nil?
|
||||
clear_internal(field_descriptor)
|
||||
else
|
||||
index_assign_internal(value, field_descriptor: field_descriptor, wrap: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
if field_descriptor.has_presence?
|
||||
define_method("has_#{field_name}?") do
|
||||
Google::Protobuf::FFI.get_message_has(@msg, field_descriptor)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Dynamically define accessors methods for every OneOf field of
|
||||
# @descriptor.
|
||||
def self.setup_oneof_accessors!
|
||||
@oneof_field_names = []
|
||||
@descriptor.each_oneof do |oneof_descriptor|
|
||||
self.add_oneof_accessors_for! oneof_descriptor
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Dynamically define accessors methods for the given OneOf field.
|
||||
# Methods with names that conflict with existing methods are skipped.
|
||||
# @param oneof_descriptor [OneofDescriptor] Field to create accessors
|
||||
# for.
|
||||
def self.add_oneof_accessors_for!(oneof_descriptor)
|
||||
field_name = oneof_descriptor.name.to_sym
|
||||
@oneof_field_names << field_name
|
||||
unless instance_methods(true).include?(field_name)
|
||||
define_method(field_name) do
|
||||
field_descriptor = Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor)
|
||||
if field_descriptor.nil?
|
||||
return
|
||||
else
|
||||
return field_descriptor.name.to_sym
|
||||
end
|
||||
end
|
||||
define_method("clear_#{field_name}") do
|
||||
field_descriptor = Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor)
|
||||
unless field_descriptor.nil?
|
||||
clear_internal(field_descriptor)
|
||||
end
|
||||
end
|
||||
define_method("has_#{field_name}?") do
|
||||
!Google::Protobuf::FFI.get_message_which_oneof(@msg, oneof_descriptor).nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
setup_accessors!
|
||||
setup_oneof_accessors!
|
||||
|
||||
def self.private_constructor(arena, msg: nil, initial_value: nil)
|
||||
instance = allocate
|
||||
instance.send(:initialize, initial_value, arena, msg)
|
||||
instance
|
||||
end
|
||||
|
||||
def self.inspect_field(field_descriptor, c_type, message_value)
|
||||
if field_descriptor.sub_message?
|
||||
sub_msg_descriptor = Google::Protobuf::FFI.get_subtype_as_message(field_descriptor)
|
||||
sub_msg_descriptor.msgclass.send(:inspect_internal, message_value[:msg_val])
|
||||
else
|
||||
convert_upb_to_ruby(message_value, c_type, field_descriptor.subtype).inspect
|
||||
end
|
||||
end
|
||||
|
||||
# @param msg [::FFI::Pointer] Pointer to the Message
|
||||
def self.inspect_internal(msg)
|
||||
field_output = []
|
||||
descriptor.each do |field_descriptor|
|
||||
next if field_descriptor.has_presence? && !Google::Protobuf::FFI.get_message_has(msg, field_descriptor)
|
||||
if field_descriptor.map?
|
||||
# TODO Adapted - from map#each_msg_val and map#inspect- can this be refactored to reduce echo without introducing a arena allocation?
|
||||
message_descriptor = field_descriptor.subtype
|
||||
key_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 1)
|
||||
key_field_type = Google::Protobuf::FFI.get_type(key_field_def)
|
||||
|
||||
value_field_def = Google::Protobuf::FFI.get_field_by_number(message_descriptor, 2)
|
||||
value_field_type = Google::Protobuf::FFI.get_type(value_field_def)
|
||||
|
||||
message_value = Google::Protobuf::FFI.get_message_value(msg, field_descriptor)
|
||||
iter = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
iter.write(:size_t, Google::Protobuf::FFI::Upb_Map_Begin)
|
||||
key_value_pairs = []
|
||||
while Google::Protobuf::FFI.map_next(message_value[:map_val], iter) do
|
||||
iter_size_t = iter.read(:size_t)
|
||||
key_message_value = Google::Protobuf::FFI.map_key(message_value[:map_val], iter_size_t)
|
||||
value_message_value = Google::Protobuf::FFI.map_value(message_value[:map_val], iter_size_t)
|
||||
key_string = convert_upb_to_ruby(key_message_value, key_field_type).inspect
|
||||
value_string = inspect_field(value_field_def, value_field_type, value_message_value)
|
||||
key_value_pairs << "#{key_string}=>#{value_string}"
|
||||
end
|
||||
field_output << "#{field_descriptor.name}: {#{key_value_pairs.join(", ")}}"
|
||||
elsif field_descriptor.repeated?
|
||||
# TODO Adapted - from repeated_field#each - can this be refactored to reduce echo?
|
||||
repeated_field_output = []
|
||||
message_value = Google::Protobuf::FFI.get_message_value(msg, field_descriptor)
|
||||
array = message_value[:array_val]
|
||||
n = array.null? ? 0 : Google::Protobuf::FFI.array_size(array)
|
||||
0.upto(n - 1) do |i|
|
||||
element = Google::Protobuf::FFI.get_msgval_at(array, i)
|
||||
repeated_field_output << inspect_field(field_descriptor, field_descriptor.send(:c_type), element)
|
||||
end
|
||||
field_output << "#{field_descriptor.name}: [#{repeated_field_output.join(", ")}]"
|
||||
else
|
||||
message_value = Google::Protobuf::FFI.get_message_value msg, field_descriptor
|
||||
rendered_value = inspect_field(field_descriptor, field_descriptor.send(:c_type), message_value)
|
||||
field_output << "#{field_descriptor.name}: #{rendered_value}"
|
||||
end
|
||||
end
|
||||
"<#{name}: #{field_output.join(', ')}>"
|
||||
end
|
||||
|
||||
def self.deep_copy(msg, arena = nil)
|
||||
arena ||= Google::Protobuf::FFI.create_arena
|
||||
encode_internal(msg) do |encoding, size, mini_table_ptr|
|
||||
message = private_constructor(arena)
|
||||
if encoding.nil? or encoding.null? or Google::Protobuf::FFI.decode_message(encoding, size, message.instance_variable_get(:@msg), mini_table_ptr, nil, 0, arena) != :Ok
|
||||
raise ParseError.new "Error occurred copying proto"
|
||||
end
|
||||
message
|
||||
end
|
||||
end
|
||||
|
||||
def self.encode_internal(msg, encoding_options = 0)
|
||||
temporary_arena = Google::Protobuf::FFI.create_arena
|
||||
|
||||
mini_table_ptr = Google::Protobuf::FFI.get_mini_table(descriptor)
|
||||
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
pointer_ptr = ::FFI::MemoryPointer.new(:pointer, 1)
|
||||
encoding_status = Google::Protobuf::FFI.encode_message(msg, mini_table_ptr, encoding_options, temporary_arena, pointer_ptr.to_ptr, size_ptr)
|
||||
raise "Encoding failed due to #{encoding_status}" unless encoding_status == :Ok
|
||||
yield pointer_ptr.read(:pointer), size_ptr.read(:size_t), mini_table_ptr
|
||||
end
|
||||
|
||||
def method_missing_internal(method_name, *args, mode: nil)
|
||||
raise ArgumentError.new "method_missing_internal called with invalid mode #{mode.inspect}" unless [:respond_to_missing?, :method_missing].include? mode
|
||||
|
||||
#TODO not being allowed is not the same thing as not responding, but this is needed to pass tests
|
||||
if method_name.to_s.end_with? '='
|
||||
if self.class.send(:oneof_field_names).include? method_name.to_s[0..-2].to_sym
|
||||
return false if mode == :respond_to_missing?
|
||||
raise RuntimeError.new "Oneof accessors are read-only."
|
||||
end
|
||||
end
|
||||
|
||||
original_method_missing(method_name, *args) if mode == :method_missing
|
||||
end
|
||||
|
||||
def clear_internal(field_def)
|
||||
raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
|
||||
Google::Protobuf::FFI.clear_message_field(@msg, field_def)
|
||||
end
|
||||
|
||||
# Accessor for field by name. Does not delegate to methods setup by
|
||||
# self.setup_accessors! in order to avoid conflicts with bad field
|
||||
# names e.g. `dup` or `class` which are perfectly valid for proto
|
||||
# fields.
|
||||
def index_internal(name)
|
||||
field_descriptor = self.class.descriptor.lookup(name)
|
||||
get_field field_descriptor unless field_descriptor.nil?
|
||||
end
|
||||
|
||||
#TODO - well known types keeps us on our toes by overloading methods.
|
||||
# How much of the public API needs to be defended?
|
||||
def index_assign_internal(value, name: nil, field_descriptor: nil, wrap: false)
|
||||
raise FrozenError.new "can't modify frozen #{self.class}" if frozen?
|
||||
if field_descriptor.nil?
|
||||
field_descriptor = self.class.descriptor.lookup(name)
|
||||
if field_descriptor.nil?
|
||||
raise ArgumentError.new "Unknown field: #{name}"
|
||||
end
|
||||
end
|
||||
unless field_descriptor.send :set_value_on_message, value, @msg, @arena, wrap: wrap
|
||||
raise RuntimeError.new "allocation failed"
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# @param initial_value [Object] initial value of this Message
|
||||
# @param arena [Arena] Optional; Arena where this message will be allocated
|
||||
# @param msg [::FFI::Pointer] Optional; Message to initialize; creates
|
||||
# one if omitted or nil.
|
||||
def initialize(initial_value = nil, arena = nil, msg = nil)
|
||||
@arena = arena || Google::Protobuf::FFI.create_arena
|
||||
@msg = msg || Google::Protobuf::FFI.new_message_from_def(Google::Protobuf::FFI.get_mini_table(self.class.descriptor), @arena)
|
||||
|
||||
unless initial_value.nil?
|
||||
raise ArgumentError.new "Expected hash arguments or message, not #{initial_value.class}" unless initial_value.respond_to? :each
|
||||
|
||||
field_def_ptr = ::FFI::MemoryPointer.new :pointer
|
||||
oneof_def_ptr = ::FFI::MemoryPointer.new :pointer
|
||||
|
||||
initial_value.each do |key, value|
|
||||
raise ArgumentError.new "Expected string or symbols as hash keys when initializing proto from hash." unless [String, Symbol].include? key.class
|
||||
|
||||
unless Google::Protobuf::FFI.find_msg_def_by_name self.class.descriptor, key.to_s, key.to_s.bytesize, field_def_ptr, oneof_def_ptr
|
||||
raise ArgumentError.new "Unknown field name '#{key}' in initialization map entry."
|
||||
end
|
||||
raise NotImplementedError.new "Haven't added oneofsupport yet" unless oneof_def_ptr.get_pointer(0).null?
|
||||
raise NotImplementedError.new "Expected a field def" if field_def_ptr.get_pointer(0).null?
|
||||
|
||||
field_descriptor = FieldDescriptor.from_native field_def_ptr.get_pointer(0)
|
||||
|
||||
next if value.nil?
|
||||
if field_descriptor.map?
|
||||
index_assign_internal(Google::Protobuf::Map.send(:construct_for_field, field_descriptor, arena: @arena, value: value), name: key.to_s)
|
||||
elsif field_descriptor.repeated?
|
||||
index_assign_internal(RepeatedField.send(:construct_for_field, field_descriptor, arena: @arena, values: value), name: key.to_s)
|
||||
else
|
||||
index_assign_internal(value, name: key.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Should always be the last expression of the initializer to avoid
|
||||
# leaking references to this object before construction is complete.
|
||||
Google::Protobuf::OBJECT_CACHE.try_add @msg.address, self
|
||||
end
|
||||
|
||||
##
|
||||
# Gets a field of this message identified by FieldDescriptor.
|
||||
#
|
||||
# @param field [FieldDescriptor] Field to retrieve.
|
||||
# @param unwrap [Boolean](false) If true, unwraps wrappers.
|
||||
def get_field(field, unwrap: false)
|
||||
if field.map?
|
||||
map_from_field_descriptor field
|
||||
elsif field.repeated?
|
||||
repeated_field_from_field_descriptor field
|
||||
elsif field.sub_message?
|
||||
return nil unless Google::Protobuf::FFI.get_message_has @msg, field
|
||||
if unwrap
|
||||
if field.has?(self)
|
||||
sub_message_def = Google::Protobuf::FFI.get_subtype_as_message field
|
||||
wrapper_message_value = Google::Protobuf::FFI.get_message_value @msg, field
|
||||
fields = Google::Protobuf::FFI.field_count(sub_message_def)
|
||||
raise "Sub message has #{fields} fields! Expected exactly 1." unless fields == 1
|
||||
value_field_def = Google::Protobuf::FFI.get_field_by_number sub_message_def, 1
|
||||
message_value = Google::Protobuf::FFI.get_message_value wrapper_message_value[:msg_val], value_field_def
|
||||
convert_upb_to_ruby message_value, Google::Protobuf::FFI.get_c_type(value_field_def)
|
||||
else
|
||||
nil
|
||||
end
|
||||
else
|
||||
message_from_field_descriptor field
|
||||
end
|
||||
else
|
||||
scalar_from_field_descriptor field
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Gets a RepeatedField from the ObjectCache or creates a new one.
|
||||
# @param array [::FFI::Pointer] Pointer to the upb_Array
|
||||
# @param field [Google::Protobuf::FieldDescriptor] Type of the repeated field
|
||||
def get_repeated_field(array, field)
|
||||
return nil if array.nil? or array.null?
|
||||
repeated_field = OBJECT_CACHE.get(array.address)
|
||||
if repeated_field.nil?
|
||||
repeated_field = RepeatedField.send(:construct_for_field, field, arena: @arena, array: array)
|
||||
repeated_field.freeze if frozen?
|
||||
end
|
||||
repeated_field
|
||||
end
|
||||
|
||||
##
|
||||
# Gets a Map from the ObjectCache or creates a new one.
|
||||
# @param map [::FFI::Pointer] Pointer to the upb_Map
|
||||
# @param field [Google::Protobuf::FieldDescriptor] Type of the map field
|
||||
def get_map_field(map, field)
|
||||
return nil if map.nil? or map.null?
|
||||
map_field = OBJECT_CACHE.get(map.address)
|
||||
if map_field.nil?
|
||||
map_field = Google::Protobuf::Map.send(:construct_for_field, field, arena: @arena, map: map)
|
||||
map_field.freeze if frozen?
|
||||
end
|
||||
map_field
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Executable
+114
@@ -0,0 +1,114 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2024 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
class MethodDescriptor
|
||||
attr :method_def, :descriptor_pool
|
||||
|
||||
include Google::Protobuf::Internal::Convert
|
||||
|
||||
# FFI Interface methods and setup
|
||||
extend ::FFI::DataConverter
|
||||
native_type ::FFI::Type::POINTER
|
||||
|
||||
class << self
|
||||
prepend Google::Protobuf::Internal::TypeSafety
|
||||
include Google::Protobuf::Internal::PointerHelper
|
||||
|
||||
# @param value [MethodDescriptor] MethodDescriptor to convert to an FFI native type
|
||||
# @param _ [Object] Unused
|
||||
def to_native(value, _)
|
||||
method_def_ptr = value.nil? ? nil : value.instance_variable_get(:@method_def)
|
||||
return ::FFI::Pointer::NULL if method_def_ptr.nil?
|
||||
raise "Underlying method_def was null!" if method_def_ptr.null?
|
||||
method_def_ptr
|
||||
end
|
||||
|
||||
##
|
||||
# @param service_def [::FFI::Pointer] MethodDef pointer to be wrapped
|
||||
# @param _ [Object] Unused
|
||||
def from_native(method_def, _ = nil)
|
||||
return nil if method_def.nil? or method_def.null?
|
||||
service_def = Google::Protobuf::FFI.raw_service_def_by_raw_method_def(method_def)
|
||||
file_def = Google::Protobuf::FFI.file_def_by_raw_service_def(service_def)
|
||||
descriptor_from_file_def(file_def, method_def)
|
||||
end
|
||||
end
|
||||
|
||||
def self.new(*arguments, &block)
|
||||
raise "Descriptor objects may not be created from Ruby."
|
||||
end
|
||||
|
||||
def to_s
|
||||
inspect
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#{self.class.name}: #{name}"
|
||||
end
|
||||
|
||||
def name
|
||||
@name ||= Google::Protobuf::FFI.get_method_name(self)
|
||||
end
|
||||
|
||||
def options
|
||||
@options ||= begin
|
||||
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
temporary_arena = Google::Protobuf::FFI.create_arena
|
||||
buffer = Google::Protobuf::FFI.method_options(self, size_ptr, temporary_arena)
|
||||
Google::Protobuf::MethodOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze).freeze
|
||||
end
|
||||
end
|
||||
|
||||
def input_type
|
||||
@input_type ||= Google::Protobuf::FFI.method_input_type(self)
|
||||
end
|
||||
|
||||
def output_type
|
||||
@output_type ||= Google::Protobuf::FFI.method_output_type(self)
|
||||
end
|
||||
|
||||
def client_streaming
|
||||
@client_streaming ||= Google::Protobuf::FFI.method_client_streaming(self)
|
||||
end
|
||||
|
||||
def server_streaming
|
||||
@server_streaming ||= Google::Protobuf::FFI.method_server_streaming(self)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(method_def, descriptor_pool)
|
||||
@method_def = method_def
|
||||
@descriptor_pool = descriptor_pool
|
||||
end
|
||||
|
||||
def self.private_constructor(method_def, descriptor_pool)
|
||||
instance = allocate
|
||||
instance.send(:initialize, method_def, descriptor_pool)
|
||||
instance
|
||||
end
|
||||
|
||||
def c_type
|
||||
@c_type ||= Google::Protobuf::FFI.get_c_type(self)
|
||||
end
|
||||
end
|
||||
|
||||
class FFI
|
||||
# MethodDef
|
||||
attach_function :raw_service_def_by_raw_method_def, :upb_MethodDef_Service, [:pointer], :pointer
|
||||
attach_function :get_method_name, :upb_MethodDef_Name, [MethodDescriptor], :string
|
||||
attach_function :method_options, :MethodDescriptor_serialized_options, [MethodDescriptor, :pointer, Internal::Arena], :pointer
|
||||
attach_function :method_input_type, :upb_MethodDef_InputType, [MethodDescriptor], Descriptor
|
||||
attach_function :method_output_type, :upb_MethodDef_OutputType, [MethodDescriptor], Descriptor
|
||||
attach_function :method_client_streaming, :upb_MethodDef_ClientStreaming, [MethodDescriptor], :bool
|
||||
attach_function :method_server_streaming, :upb_MethodDef_ServerStreaming, [MethodDescriptor], :bool
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2022 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
private
|
||||
|
||||
SIZEOF_LONG = ::FFI::MemoryPointer.new(:long).size
|
||||
SIZEOF_VALUE = ::FFI::Pointer::SIZE
|
||||
|
||||
def self.interpreter_supports_non_finalized_keys_in_weak_map?
|
||||
! defined? JRUBY_VERSION
|
||||
end
|
||||
|
||||
def self.cache_implementation
|
||||
if interpreter_supports_non_finalized_keys_in_weak_map? and SIZEOF_LONG >= SIZEOF_VALUE
|
||||
Google::Protobuf::Internal::ObjectCache
|
||||
else
|
||||
Google::Protobuf::Internal::LegacyObjectCache
|
||||
end
|
||||
end
|
||||
|
||||
public
|
||||
OBJECT_CACHE = cache_implementation.new
|
||||
end
|
||||
end
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2022 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
class OneofDescriptor
|
||||
attr :descriptor_pool, :oneof_def
|
||||
include Enumerable
|
||||
|
||||
# FFI Interface methods and setup
|
||||
extend ::FFI::DataConverter
|
||||
native_type ::FFI::Type::POINTER
|
||||
|
||||
class << self
|
||||
prepend Google::Protobuf::Internal::TypeSafety
|
||||
include Google::Protobuf::Internal::PointerHelper
|
||||
|
||||
# @param value [OneofDescriptor] FieldDescriptor to convert to an FFI native type
|
||||
# @param _ [Object] Unused
|
||||
def to_native(value, _ = nil)
|
||||
value.instance_variable_get(:@oneof_def) || ::FFI::Pointer::NULL
|
||||
end
|
||||
|
||||
##
|
||||
# @param oneof_def [::FFI::Pointer] OneofDef pointer to be wrapped
|
||||
# @param _ [Object] Unused
|
||||
def from_native(oneof_def, _ = nil)
|
||||
return nil if oneof_def.nil? or oneof_def.null?
|
||||
message_descriptor = Google::Protobuf::FFI.get_oneof_containing_type oneof_def
|
||||
raise RuntimeError.new "Message Descriptor is nil" if message_descriptor.nil?
|
||||
file_def = Google::Protobuf::FFI.get_message_file_def message_descriptor.to_native
|
||||
descriptor_from_file_def(file_def, oneof_def)
|
||||
end
|
||||
end
|
||||
|
||||
def self.new(*arguments, &block)
|
||||
raise "OneofDescriptor objects may not be created from Ruby."
|
||||
end
|
||||
|
||||
def name
|
||||
Google::Protobuf::FFI.get_oneof_name(self)
|
||||
end
|
||||
|
||||
def each &block
|
||||
n = Google::Protobuf::FFI.get_oneof_field_count(self)
|
||||
0.upto(n-1) do |i|
|
||||
yield(Google::Protobuf::FFI.get_oneof_field_by_index(self, i))
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def options
|
||||
@options ||= begin
|
||||
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
temporary_arena = Google::Protobuf::FFI.create_arena
|
||||
buffer = Google::Protobuf::FFI.oneof_options(self, size_ptr, temporary_arena)
|
||||
opts = Google::Protobuf::OneofOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze)
|
||||
opts.clear_features()
|
||||
opts.freeze
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(oneof_def, descriptor_pool)
|
||||
@descriptor_pool = descriptor_pool
|
||||
@oneof_def = oneof_def
|
||||
end
|
||||
|
||||
def self.private_constructor(oneof_def, descriptor_pool)
|
||||
instance = allocate
|
||||
instance.send(:initialize, oneof_def, descriptor_pool)
|
||||
instance
|
||||
end
|
||||
end
|
||||
|
||||
class FFI
|
||||
# MessageDef
|
||||
attach_function :get_oneof_by_name, :upb_MessageDef_FindOneofByNameWithSize, [Descriptor, :string, :size_t], OneofDescriptor
|
||||
attach_function :get_oneof_by_index, :upb_MessageDef_Oneof, [Descriptor, :int], OneofDescriptor
|
||||
|
||||
# OneofDescriptor
|
||||
attach_function :get_oneof_name, :upb_OneofDef_Name, [OneofDescriptor], :string
|
||||
attach_function :get_oneof_field_count, :upb_OneofDef_FieldCount, [OneofDescriptor], :int
|
||||
attach_function :get_oneof_field_by_index, :upb_OneofDef_Field, [OneofDescriptor, :int], FieldDescriptor
|
||||
attach_function :get_oneof_containing_type,:upb_OneofDef_ContainingType, [:pointer], Descriptor
|
||||
attach_function :oneof_options, :OneOfDescriptor_serialized_options, [OneofDescriptor, :pointer, Internal::Arena], :pointer
|
||||
|
||||
# FieldDescriptor
|
||||
attach_function :real_containing_oneof, :upb_FieldDef_RealContainingOneof, [FieldDescriptor], OneofDescriptor
|
||||
end
|
||||
end
|
||||
end
|
||||
+411
@@ -0,0 +1,411 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2008 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#
|
||||
# This class makes RepeatedField act (almost-) like a Ruby Array.
|
||||
# It has convenience methods that extend the core C or Java based
|
||||
# methods.
|
||||
#
|
||||
# This is a best-effort to mirror Array behavior. Two comments:
|
||||
# 1) patches always welcome :)
|
||||
# 2) if performance is an issue, feel free to rewrite the method
|
||||
# in C. The source code has plenty of examples
|
||||
#
|
||||
# KNOWN ISSUES
|
||||
# - #[]= doesn't allow less used approaches such as `arr[1, 2] = 'fizz'`
|
||||
# - #concat should return the orig array
|
||||
# - #push should accept multiple arguments and push them all at the same time
|
||||
#
|
||||
module Google
|
||||
module Protobuf
|
||||
class FFI
|
||||
# Array
|
||||
attach_function :append_array, :upb_Array_Append, [:Array, MessageValue.by_value, Internal::Arena], :bool
|
||||
attach_function :get_msgval_at, :upb_Array_Get, [:Array, :size_t], MessageValue.by_value
|
||||
attach_function :create_array, :upb_Array_New, [Internal::Arena, CType], :Array
|
||||
attach_function :array_resize, :upb_Array_Resize, [:Array, :size_t, Internal::Arena], :bool
|
||||
attach_function :array_set, :upb_Array_Set, [:Array, :size_t, MessageValue.by_value], :void
|
||||
attach_function :array_size, :upb_Array_Size, [:Array], :size_t
|
||||
attach_function :array_freeze, :upb_Array_Freeze, [:Array, MiniTable.by_ref], :void
|
||||
attach_function :array_frozen?, :upb_Array_IsFrozen, [:Array], :bool
|
||||
end
|
||||
|
||||
class RepeatedField
|
||||
include Enumerable
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# RepeatedField.new(type, type_class = nil, initial_values = [])
|
||||
#
|
||||
# Creates a new repeated field. The provided type must be a Ruby symbol, and
|
||||
# an take on the same values as those accepted by FieldDescriptor#type=. If
|
||||
# the type is :message or :enum, type_class must be non-nil, and must be the
|
||||
# Ruby class or module returned by Descriptor#msgclass or
|
||||
# EnumDescriptor#enummodule, respectively. An initial list of elements may also
|
||||
# be provided.
|
||||
def self.new(type, type_class = nil, initial_values = [])
|
||||
instance = allocate
|
||||
# TODO This argument mangling doesn't agree with the type signature in the comments
|
||||
# but is required to make unit tests pass;
|
||||
if type_class.is_a?(Enumerable) and initial_values.empty? and ![:enum, :message].include?(type)
|
||||
initial_values = type_class
|
||||
type_class = nil
|
||||
end
|
||||
instance.send(:initialize, type, type_class: type_class, initial_values: initial_values)
|
||||
instance
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# RepeatedField.each(&block)
|
||||
#
|
||||
# Invokes the block once for each element of the repeated field. RepeatedField
|
||||
# also includes Enumerable; combined with this method, the repeated field thus
|
||||
# acts like an ordinary Ruby sequence.
|
||||
def each &block
|
||||
each_msg_val do |element|
|
||||
yield(convert_upb_to_ruby(element, type, descriptor, arena))
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def [](*args)
|
||||
count = length
|
||||
if args.size < 1
|
||||
raise ArgumentError.new "Index or range is a required argument."
|
||||
end
|
||||
if args[0].is_a? Range
|
||||
if args.size > 1
|
||||
raise ArgumentError.new "Expected 1 when passing Range argument, but got #{args.size}"
|
||||
end
|
||||
range = args[0]
|
||||
# Handle begin-less and/or endless ranges, when supported.
|
||||
index_of_first = range.respond_to?(:begin) ? range.begin : range.last
|
||||
index_of_first = 0 if index_of_first.nil?
|
||||
end_of_range = range.respond_to?(:end) ? range.end : range.last
|
||||
index_of_last = end_of_range.nil? ? -1 : end_of_range
|
||||
|
||||
if index_of_last < 0
|
||||
index_of_last += count
|
||||
end
|
||||
unless range.exclude_end? and !end_of_range.nil?
|
||||
index_of_last += 1
|
||||
end
|
||||
index_of_first += count if index_of_first < 0
|
||||
length = index_of_last - index_of_first
|
||||
return [] if length.zero?
|
||||
elsif args[0].is_a? Integer
|
||||
index_of_first = args[0]
|
||||
index_of_first += count if index_of_first < 0
|
||||
if args.size > 2
|
||||
raise ArgumentError.new "Expected 1 or 2 arguments, but got #{args.size}"
|
||||
end
|
||||
if args.size == 1 # No length specified, return one element
|
||||
if array.null? or index_of_first < 0 or index_of_first >= count
|
||||
return nil
|
||||
else
|
||||
return convert_upb_to_ruby(Google::Protobuf::FFI.get_msgval_at(array, index_of_first), type, descriptor, arena)
|
||||
end
|
||||
else
|
||||
length = [args[1],count].min
|
||||
end
|
||||
else
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
if array.null? or index_of_first < 0 or index_of_first >= count
|
||||
nil
|
||||
else
|
||||
if index_of_first + length > count
|
||||
length = count - index_of_first
|
||||
end
|
||||
if length < 0
|
||||
nil
|
||||
else
|
||||
subarray(index_of_first, length)
|
||||
end
|
||||
end
|
||||
end
|
||||
alias at []
|
||||
|
||||
|
||||
def []=(index, value)
|
||||
raise FrozenError if frozen?
|
||||
count = length
|
||||
index += count if index < 0
|
||||
return nil if index < 0
|
||||
if index >= count
|
||||
resize(index+1)
|
||||
empty_message_value = Google::Protobuf::FFI::MessageValue.new # Implicitly clear
|
||||
count.upto(index-1) do |i|
|
||||
Google::Protobuf::FFI.array_set(array, i, empty_message_value)
|
||||
end
|
||||
end
|
||||
Google::Protobuf::FFI.array_set(array, index, convert_ruby_to_upb(value, arena, type, descriptor))
|
||||
nil
|
||||
end
|
||||
|
||||
def push(*elements)
|
||||
raise FrozenError if frozen?
|
||||
internal_push(*elements)
|
||||
end
|
||||
|
||||
def <<(element)
|
||||
raise FrozenError if frozen?
|
||||
push element
|
||||
end
|
||||
|
||||
def replace(replacements)
|
||||
raise FrozenError if frozen?
|
||||
clear
|
||||
push(*replacements)
|
||||
end
|
||||
|
||||
def clear
|
||||
raise FrozenError if frozen?
|
||||
resize 0
|
||||
self
|
||||
end
|
||||
|
||||
def length
|
||||
array.null? ? 0 : Google::Protobuf::FFI.array_size(array)
|
||||
end
|
||||
alias size :length
|
||||
|
||||
##
|
||||
# Is this object frozen?
|
||||
# Returns true if either this Ruby wrapper or the underlying
|
||||
# representation are frozen. Freezes the wrapper if the underlying
|
||||
# representation is already frozen but this wrapper isn't.
|
||||
def frozen?
|
||||
unless Google::Protobuf::FFI.array_frozen? array
|
||||
raise RuntimeError.new "Ruby frozen RepeatedField with mutable representation" if super
|
||||
return false
|
||||
end
|
||||
method(:freeze).super_method.call unless super
|
||||
true
|
||||
end
|
||||
|
||||
##
|
||||
# Freezes the RepeatedField object. We have to intercept this so we can
|
||||
# freeze the underlying representation, not just the Ruby wrapper. Returns
|
||||
# self.
|
||||
def freeze
|
||||
if method(:frozen?).super_method.call
|
||||
unless Google::Protobuf::FFI.array_frozen? array
|
||||
raise RuntimeError.new "Underlying representation of repeated field still mutable despite frozen wrapper"
|
||||
end
|
||||
return self
|
||||
end
|
||||
unless Google::Protobuf::FFI.array_frozen? array
|
||||
mini_table = (type == :message) ? Google::Protobuf::FFI.get_mini_table(@descriptor) : nil
|
||||
Google::Protobuf::FFI.array_freeze(array, mini_table)
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def dup
|
||||
instance = self.class.allocate
|
||||
instance.send(:initialize, type, descriptor: descriptor, arena: arena)
|
||||
each_msg_val do |element|
|
||||
instance.send(:append_msg_val, element)
|
||||
end
|
||||
instance
|
||||
end
|
||||
alias clone dup
|
||||
|
||||
def ==(other)
|
||||
return true if other.object_id == object_id
|
||||
if other.is_a? RepeatedField
|
||||
return false unless other.length == length
|
||||
each_msg_val_with_index do |msg_val, i|
|
||||
other_msg_val = Google::Protobuf::FFI.get_msgval_at(other.send(:array), i)
|
||||
unless Google::Protobuf::FFI.message_value_equal(msg_val, other_msg_val, type, descriptor)
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
elsif other.is_a? Enumerable
|
||||
return to_ary == other.to_a
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# RepeatedField.to_ary => array
|
||||
#
|
||||
# Used when converted implicitly into array, e.g. compared to an Array.
|
||||
# Also called as a fallback of Object#to_a
|
||||
def to_ary
|
||||
return_value = []
|
||||
each do |element|
|
||||
return_value << element
|
||||
end
|
||||
return_value
|
||||
end
|
||||
|
||||
def hash
|
||||
return_value = 0
|
||||
each_msg_val do |msg_val|
|
||||
return_value = Google::Protobuf::FFI.message_value_hash(msg_val, type, descriptor, return_value)
|
||||
end
|
||||
return_value
|
||||
end
|
||||
|
||||
def +(other)
|
||||
if other.is_a? RepeatedField
|
||||
if type != other.instance_variable_get(:@type) or descriptor != other.instance_variable_get(:@descriptor)
|
||||
raise ArgumentError.new "Attempt to append RepeatedField with different element type."
|
||||
end
|
||||
fuse_arena(other.send(:arena))
|
||||
super_set = dup
|
||||
other.send(:each_msg_val) do |msg_val|
|
||||
super_set.send(:append_msg_val, msg_val)
|
||||
end
|
||||
super_set
|
||||
elsif other.is_a? Enumerable
|
||||
super_set = dup
|
||||
super_set.push(*other.to_a)
|
||||
else
|
||||
raise ArgumentError.new "Unknown type appending to RepeatedField"
|
||||
end
|
||||
end
|
||||
|
||||
def concat(other)
|
||||
raise ArgumentError.new "Expected Enumerable, but got #{other.class}" unless other.is_a? Enumerable
|
||||
push(*other.to_a)
|
||||
end
|
||||
|
||||
private
|
||||
include Google::Protobuf::Internal::Convert
|
||||
|
||||
attr :name, :arena, :array, :type, :descriptor
|
||||
|
||||
def internal_push(*elements)
|
||||
elements.each do |element|
|
||||
append_msg_val convert_ruby_to_upb(element, arena, type, descriptor)
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def pop_one
|
||||
raise FrozenError if frozen?
|
||||
count = length
|
||||
return nil if length.zero?
|
||||
last_element = Google::Protobuf::FFI.get_msgval_at(array, count-1)
|
||||
return_value = convert_upb_to_ruby(last_element, type, descriptor, arena)
|
||||
resize(count-1)
|
||||
return_value
|
||||
end
|
||||
|
||||
def subarray(start, length)
|
||||
return_result = []
|
||||
(start..(start + length - 1)).each do |i|
|
||||
element = Google::Protobuf::FFI.get_msgval_at(array, i)
|
||||
return_result << convert_upb_to_ruby(element, type, descriptor, arena)
|
||||
end
|
||||
return_result
|
||||
end
|
||||
|
||||
def each_msg_val_with_index &block
|
||||
n = array.null? ? 0 : Google::Protobuf::FFI.array_size(array)
|
||||
0.upto(n-1) do |i|
|
||||
yield Google::Protobuf::FFI.get_msgval_at(array, i), i
|
||||
end
|
||||
end
|
||||
|
||||
def each_msg_val &block
|
||||
each_msg_val_with_index do |msg_val, _|
|
||||
yield msg_val
|
||||
end
|
||||
end
|
||||
|
||||
# @param msg_val [Google::Protobuf::FFI::MessageValue] Value to append
|
||||
def append_msg_val(msg_val)
|
||||
unless Google::Protobuf::FFI.append_array(array, msg_val, arena)
|
||||
raise NoMemoryError.new "Could not allocate room for #{msg_val} in Arena"
|
||||
end
|
||||
end
|
||||
|
||||
# @param new_size [Integer] New size of the array
|
||||
def resize(new_size)
|
||||
unless Google::Protobuf::FFI.array_resize(array, new_size, arena)
|
||||
raise NoMemoryError.new "Array resize to #{new_size} failed!"
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(type, type_class: nil, initial_values: nil, name: nil, arena: nil, array: nil, descriptor: nil)
|
||||
@name = name || 'RepeatedField'
|
||||
raise ArgumentError.new "Expected argument type to be a Symbol" unless type.is_a? Symbol
|
||||
field_number = Google::Protobuf::FFI::FieldType[type]
|
||||
raise ArgumentError.new "Unsupported type '#{type}'" if field_number.nil?
|
||||
if !descriptor.nil?
|
||||
@descriptor = descriptor
|
||||
elsif [:message, :enum].include? type
|
||||
raise ArgumentError.new "Expected at least 2 arguments for message/enum." if type_class.nil?
|
||||
descriptor = type_class.respond_to?(:descriptor) ? type_class.descriptor : nil
|
||||
raise ArgumentError.new "Type class #{type_class} has no descriptor. Please pass a class or enum as returned by the DescriptorPool." if descriptor.nil?
|
||||
@descriptor = descriptor
|
||||
else
|
||||
@descriptor = nil
|
||||
end
|
||||
@type = type
|
||||
|
||||
@arena = arena || Google::Protobuf::FFI.create_arena
|
||||
@array = array || Google::Protobuf::FFI.create_array(@arena, @type)
|
||||
unless initial_values.nil?
|
||||
unless initial_values.is_a? Enumerable
|
||||
raise ArgumentError.new "Expected array as initializer value for repeated field '#{name}' (given #{initial_values.class})."
|
||||
end
|
||||
internal_push(*initial_values)
|
||||
end
|
||||
|
||||
# Should always be the last expression of the initializer to avoid
|
||||
# leaking references to this object before construction is complete.
|
||||
OBJECT_CACHE.try_add(@array.address, self)
|
||||
end
|
||||
|
||||
##
|
||||
# Constructor that uses the type information from the given
|
||||
# FieldDescriptor to configure the new RepeatedField instance.
|
||||
# @param field [FieldDescriptor] Type information for the new RepeatedField
|
||||
# @param arena [Arena] Owning message's arena
|
||||
# @param values [Enumerable] Initial values
|
||||
# @param array [::FFI::Pointer] Existing upb_Array
|
||||
def self.construct_for_field(field, arena: nil, values: nil, array: nil)
|
||||
instance = allocate
|
||||
options = {initial_values: values, name: field.name, arena: arena, array: array}
|
||||
if [:enum, :message].include? field.type
|
||||
options[:descriptor] = field.subtype
|
||||
end
|
||||
instance.send(:initialize, field.type, **options)
|
||||
instance
|
||||
end
|
||||
|
||||
def fuse_arena(arena)
|
||||
arena.fuse(arena)
|
||||
end
|
||||
|
||||
extend Google::Protobuf::Internal::Convert
|
||||
|
||||
def self.deep_copy(repeated_field)
|
||||
instance = allocate
|
||||
instance.send(:initialize, repeated_field.send(:type), descriptor: repeated_field.send(:descriptor))
|
||||
instance.send(:resize, repeated_field.length)
|
||||
new_array = instance.send(:array)
|
||||
repeated_field.send(:each_msg_val_with_index) do |element, i|
|
||||
Google::Protobuf::FFI.array_set(new_array, i, message_value_deep_copy(element, repeated_field.send(:type), repeated_field.send(:descriptor), instance.send(:arena)))
|
||||
end
|
||||
instance
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'google/protobuf/repeated_field'
|
||||
Executable
+107
@@ -0,0 +1,107 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2024 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
class ServiceDescriptor
|
||||
attr :service_def, :descriptor_pool
|
||||
include Enumerable
|
||||
|
||||
include Google::Protobuf::Internal::Convert
|
||||
|
||||
# FFI Interface methods and setup
|
||||
extend ::FFI::DataConverter
|
||||
native_type ::FFI::Type::POINTER
|
||||
|
||||
class << self
|
||||
prepend Google::Protobuf::Internal::TypeSafety
|
||||
include Google::Protobuf::Internal::PointerHelper
|
||||
|
||||
# @param value [ServiceDescriptor] ServiceDescriptor to convert to an FFI native type
|
||||
# @param _ [Object] Unused
|
||||
def to_native(value, _)
|
||||
service_def_ptr = value.nil? ? nil : value.instance_variable_get(:@service_def)
|
||||
return ::FFI::Pointer::NULL if service_def_ptr.nil?
|
||||
raise "Underlying service_def was null!" if service_def_ptr.null?
|
||||
service_def_ptr
|
||||
end
|
||||
|
||||
##
|
||||
# @param service_def [::FFI::Pointer] ServiceDef pointer to be wrapped
|
||||
# @param _ [Object] Unused
|
||||
def from_native(service_def, _ = nil)
|
||||
return nil if service_def.nil? or service_def.null?
|
||||
file_def = Google::Protobuf::FFI.file_def_by_raw_service_def(service_def)
|
||||
descriptor_from_file_def(file_def, service_def)
|
||||
end
|
||||
end
|
||||
|
||||
def self.new(*arguments, &block)
|
||||
raise "Descriptor objects may not be created from Ruby."
|
||||
end
|
||||
|
||||
def to_s
|
||||
inspect
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#{self.class.name}: #{name}"
|
||||
end
|
||||
|
||||
def name
|
||||
@name ||= Google::Protobuf::FFI.get_service_full_name(self)
|
||||
end
|
||||
|
||||
def file_descriptor
|
||||
@descriptor_pool.send(:get_file_descriptor, Google::Protobuf::FFI.file_def_by_raw_service_def(@service_def))
|
||||
end
|
||||
|
||||
def each &block
|
||||
n = Google::Protobuf::FFI.method_count(self)
|
||||
0.upto(n-1) do |i|
|
||||
yield(Google::Protobuf::FFI.get_method_by_index(self, i))
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def options
|
||||
@options ||= begin
|
||||
size_ptr = ::FFI::MemoryPointer.new(:size_t, 1)
|
||||
temporary_arena = Google::Protobuf::FFI.create_arena
|
||||
buffer = Google::Protobuf::FFI.service_options(self, size_ptr, temporary_arena)
|
||||
Google::Protobuf::ServiceOptions.decode(buffer.read_string_length(size_ptr.read(:size_t)).force_encoding("ASCII-8BIT").freeze).freeze
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(service_def, descriptor_pool)
|
||||
@service_def = service_def
|
||||
@descriptor_pool = descriptor_pool
|
||||
end
|
||||
|
||||
def self.private_constructor(service_def, descriptor_pool)
|
||||
instance = allocate
|
||||
instance.send(:initialize, service_def, descriptor_pool)
|
||||
instance
|
||||
end
|
||||
|
||||
def c_type
|
||||
@c_type ||= Google::Protobuf::FFI.get_c_type(self)
|
||||
end
|
||||
end
|
||||
|
||||
class FFI
|
||||
# ServiceDef
|
||||
attach_function :file_def_by_raw_service_def, :upb_ServiceDef_File, [:pointer], :FileDef
|
||||
attach_function :get_service_full_name, :upb_ServiceDef_FullName, [ServiceDescriptor], :string
|
||||
attach_function :method_count, :upb_ServiceDef_MethodCount, [ServiceDescriptor], :int
|
||||
attach_function :get_method_by_index, :upb_ServiceDef_Method, [ServiceDescriptor, :int], MethodDescriptor
|
||||
attach_function :service_options, :ServiceDescriptor_serialized_options, [ServiceDescriptor, :pointer, Internal::Arena], :pointer
|
||||
end
|
||||
end
|
||||
end
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: google/protobuf/field_mask.proto
|
||||
|
||||
require 'google/protobuf'
|
||||
|
||||
|
||||
descriptor_data = "\n google/protobuf/field_mask.proto\x12\x0fgoogle.protobuf\"\x1a\n\tFieldMask\x12\r\n\x05paths\x18\x01 \x03(\tB\x85\x01\n\x13\x63om.google.protobufB\x0e\x46ieldMaskProtoP\x01Z2google.golang.org/protobuf/types/known/fieldmaskpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3"
|
||||
|
||||
pool = Google::Protobuf::DescriptorPool.generated_pool
|
||||
pool.add_serialized_file(descriptor_data)
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
FieldMask = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.FieldMask").msgclass
|
||||
end
|
||||
end
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2023 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
module Internal
|
||||
# A pointer -> Ruby Object cache that keeps references to Ruby wrapper
|
||||
# objects. This allows us to look up any Ruby wrapper object by the address
|
||||
# of the object it is wrapping. That way we can avoid ever creating two
|
||||
# different wrapper objects for the same C object, which saves memory and
|
||||
# preserves object identity.
|
||||
#
|
||||
# We use WeakMap for the cache. If sizeof(long) > sizeof(VALUE), we also
|
||||
# need a secondary Hash to store WeakMap keys, because our pointer keys may
|
||||
# need to be stored as Bignum instead of Fixnum. Since WeakMap is weak for
|
||||
# both keys and values, a Bignum key will cause the WeakMap entry to be
|
||||
# collected immediately unless there is another reference to the Bignum.
|
||||
# This happens on 64-bit Windows, on which pointers are 64 bits but longs
|
||||
# are 32 bits. In this case, we enable the secondary Hash to hold the keys
|
||||
# and prevent them from being collected.
|
||||
class ObjectCache
|
||||
def initialize
|
||||
@map = ObjectSpace::WeakMap.new
|
||||
@mutex = Mutex.new
|
||||
end
|
||||
|
||||
def get(key)
|
||||
@map[key]
|
||||
end
|
||||
|
||||
def try_add(key, value)
|
||||
@map[key] || @mutex.synchronize do
|
||||
@map[key] ||= value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class LegacyObjectCache
|
||||
def initialize
|
||||
@secondary_map = {}
|
||||
@map = ObjectSpace::WeakMap.new
|
||||
@mutex = Mutex.new
|
||||
end
|
||||
|
||||
def get(key)
|
||||
value = if secondary_key = @secondary_map[key]
|
||||
@map[secondary_key]
|
||||
else
|
||||
@mutex.synchronize do
|
||||
@map[(@secondary_map[key] ||= Object.new)]
|
||||
end
|
||||
end
|
||||
|
||||
# GC if we could remove at least 2000 entries or 20% of the table size
|
||||
# (whichever is greater). Since the cost of the GC pass is O(N), we
|
||||
# want to make sure that we condition this on overall table size, to
|
||||
# avoid O(N^2) CPU costs.
|
||||
cutoff = (@secondary_map.size * 0.2).ceil
|
||||
cutoff = 2_000 if cutoff < 2_000
|
||||
if (@secondary_map.size - @map.size) > cutoff
|
||||
purge
|
||||
end
|
||||
|
||||
value
|
||||
end
|
||||
|
||||
def try_add(key, value)
|
||||
if secondary_key = @secondary_map[key]
|
||||
if old_value = @map[secondary_key]
|
||||
return old_value
|
||||
end
|
||||
end
|
||||
|
||||
@mutex.synchronize do
|
||||
secondary_key ||= (@secondary_map[key] ||= Object.new)
|
||||
@map[secondary_key] ||= value
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def purge
|
||||
@mutex.synchronize do
|
||||
@secondary_map.each do |key, secondary_key|
|
||||
unless @map.key?(secondary_key)
|
||||
@secondary_map.delete(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2008 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
module MessageExts
|
||||
|
||||
#this is only called in jruby; mri loades the ClassMethods differently
|
||||
def self.included(klass)
|
||||
klass.extend(ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
end
|
||||
|
||||
def to_json(options = {})
|
||||
self.class.encode_json(self, options)
|
||||
end
|
||||
|
||||
def to_proto(options = {})
|
||||
self.class.encode(self, options)
|
||||
end
|
||||
|
||||
end
|
||||
class AbstractMessage
|
||||
include MessageExts
|
||||
extend MessageExts::ClassMethods
|
||||
end
|
||||
private_constant :AbstractMessage
|
||||
end
|
||||
end
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# frozen_string_literal: true
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: google/protobuf/compiler/plugin.proto
|
||||
|
||||
require 'google/protobuf'
|
||||
|
||||
require 'google/protobuf/descriptor_pb'
|
||||
|
||||
|
||||
descriptor_data = "\n%google/protobuf/compiler/plugin.proto\x12\x18google.protobuf.compiler\x1a google/protobuf/descriptor.proto\"F\n\x07Version\x12\r\n\x05major\x18\x01 \x01(\x05\x12\r\n\x05minor\x18\x02 \x01(\x05\x12\r\n\x05patch\x18\x03 \x01(\x05\x12\x0e\n\x06suffix\x18\x04 \x01(\t\"\x81\x02\n\x14\x43odeGeneratorRequest\x12\x18\n\x10\x66ile_to_generate\x18\x01 \x03(\t\x12\x11\n\tparameter\x18\x02 \x01(\t\x12\x38\n\nproto_file\x18\x0f \x03(\x0b\x32$.google.protobuf.FileDescriptorProto\x12\x45\n\x17source_file_descriptors\x18\x11 \x03(\x0b\x32$.google.protobuf.FileDescriptorProto\x12;\n\x10\x63ompiler_version\x18\x03 \x01(\x0b\x32!.google.protobuf.compiler.Version\"\x92\x03\n\x15\x43odeGeneratorResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\x12\x1a\n\x12supported_features\x18\x02 \x01(\x04\x12\x17\n\x0fminimum_edition\x18\x03 \x01(\x05\x12\x17\n\x0fmaximum_edition\x18\x04 \x01(\x05\x12\x42\n\x04\x66ile\x18\x0f \x03(\x0b\x32\x34.google.protobuf.compiler.CodeGeneratorResponse.File\x1a\x7f\n\x04\x46ile\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x17\n\x0finsertion_point\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x0f \x01(\t\x12?\n\x13generated_code_info\x18\x10 \x01(\x0b\x32\".google.protobuf.GeneratedCodeInfo\"W\n\x07\x46\x65\x61ture\x12\x10\n\x0c\x46\x45\x41TURE_NONE\x10\x00\x12\x1b\n\x17\x46\x45\x41TURE_PROTO3_OPTIONAL\x10\x01\x12\x1d\n\x19\x46\x45\x41TURE_SUPPORTS_EDITIONS\x10\x02\x42r\n\x1c\x63om.google.protobuf.compilerB\x0cPluginProtosZ)google.golang.org/protobuf/types/pluginpb\xaa\x02\x18Google.Protobuf.Compiler"
|
||||
|
||||
pool = Google::Protobuf::DescriptorPool.generated_pool
|
||||
pool.add_serialized_file(descriptor_data)
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
module Compiler
|
||||
Version = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.compiler.Version").msgclass
|
||||
CodeGeneratorRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.compiler.CodeGeneratorRequest").msgclass
|
||||
CodeGeneratorResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.compiler.CodeGeneratorResponse").msgclass
|
||||
CodeGeneratorResponse::File = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.compiler.CodeGeneratorResponse.File").msgclass
|
||||
CodeGeneratorResponse::Feature = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.compiler.CodeGeneratorResponse.Feature").enummodule
|
||||
end
|
||||
end
|
||||
end
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2008 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
require 'forwardable'
|
||||
|
||||
#
|
||||
# This class makes RepeatedField act (almost-) like a Ruby Array.
|
||||
# It has convenience methods that extend the core C or Java based
|
||||
# methods.
|
||||
#
|
||||
# This is a best-effort to mirror Array behavior. Two comments:
|
||||
# 1) patches always welcome :)
|
||||
# 2) if performance is an issue, feel free to rewrite the method
|
||||
# in jruby and C. The source code has plenty of examples
|
||||
#
|
||||
# KNOWN ISSUES
|
||||
# - #[]= doesn't allow less used approaches such as `arr[1, 2] = 'fizz'`
|
||||
# - #concat should return the orig array
|
||||
# - #push should accept multiple arguments and push them all at the same time
|
||||
#
|
||||
module Google
|
||||
module Protobuf
|
||||
class RepeatedField
|
||||
extend Forwardable
|
||||
|
||||
# methods defined in C or Java:
|
||||
# +
|
||||
# [], at
|
||||
# []=
|
||||
# concat
|
||||
# clear
|
||||
# dup, clone
|
||||
# each
|
||||
# push, <<
|
||||
# replace
|
||||
# length, size
|
||||
# ==
|
||||
# to_ary, to_a
|
||||
# also all enumerable
|
||||
#
|
||||
# NOTE: using delegators rather than method_missing to make the
|
||||
# relationship explicit instead of implicit
|
||||
def_delegators :to_ary,
|
||||
:&, :*, :-, :'<=>',
|
||||
:assoc, :bsearch, :bsearch_index, :combination, :compact, :count,
|
||||
:cycle, :difference, :dig, :drop, :drop_while, :eql?, :fetch, :find_index, :flatten,
|
||||
:include?, :index, :inspect, :intersection, :join,
|
||||
:pack, :permutation, :product, :pretty_print, :pretty_print_cycle,
|
||||
:rassoc, :repeated_combination, :repeated_permutation, :reverse,
|
||||
:rindex, :rotate, :sample, :shuffle, :shelljoin,
|
||||
:to_s, :transpose, :union, :uniq, :|
|
||||
|
||||
|
||||
def first(n=nil)
|
||||
if n.nil?
|
||||
return self[0]
|
||||
elsif n < 0
|
||||
raise ArgumentError, "negative array size"
|
||||
else
|
||||
return self[0...n]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def last(n=nil)
|
||||
if n.nil?
|
||||
return self[-1]
|
||||
elsif n < 0
|
||||
raise ArgumentError, "negative array size"
|
||||
else
|
||||
start = [self.size-n, 0].max
|
||||
return self[start...self.size]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def pop(n=nil)
|
||||
if n
|
||||
results = []
|
||||
n.times{ results << pop_one }
|
||||
return results
|
||||
else
|
||||
return pop_one
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def empty?
|
||||
self.size == 0
|
||||
end
|
||||
|
||||
# array aliases into enumerable
|
||||
alias_method :slice, :[]
|
||||
alias_method :values_at, :select
|
||||
alias_method :map, :collect
|
||||
|
||||
|
||||
class << self
|
||||
def define_array_wrapper_method(method_name)
|
||||
define_method(method_name) do |*args, &block|
|
||||
arr = self.to_a
|
||||
result = arr.send(method_name, *args)
|
||||
self.replace(arr)
|
||||
return result if result
|
||||
return block ? block.call : result
|
||||
end
|
||||
end
|
||||
private :define_array_wrapper_method
|
||||
|
||||
|
||||
def define_array_wrapper_with_result_method(method_name)
|
||||
define_method(method_name) do |*args, &block|
|
||||
# result can be an Enumerator, Array, or nil
|
||||
# Enumerator can sometimes be returned if a block is an optional argument and it is not passed in
|
||||
# nil usually specifies that no change was made
|
||||
result = self.to_a.send(method_name, *args, &block)
|
||||
if result
|
||||
new_arr = result.to_a
|
||||
self.replace(new_arr)
|
||||
if result.is_a?(Enumerator)
|
||||
# generate a fresh enum; rewinding the exiting one, in Ruby 2.2, will
|
||||
# reset the enum with the same length, but all the #next calls will
|
||||
# return nil
|
||||
result = new_arr.to_enum
|
||||
# generate a wrapper enum so any changes which occur by a chained
|
||||
# enum can be captured
|
||||
ie = ProxyingEnumerator.new(self, result)
|
||||
result = ie.to_enum
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
private :define_array_wrapper_with_result_method
|
||||
end
|
||||
|
||||
|
||||
%w(delete delete_at shift slice! unshift).each do |method_name|
|
||||
define_array_wrapper_method(method_name)
|
||||
end
|
||||
|
||||
|
||||
%w(collect! compact! delete_if each_index fill flatten! insert reverse!
|
||||
rotate! select! shuffle! sort! sort_by! uniq!).each do |method_name|
|
||||
define_array_wrapper_with_result_method(method_name)
|
||||
end
|
||||
alias_method :keep_if, :select!
|
||||
alias_method :map!, :collect!
|
||||
alias_method :reject!, :delete_if
|
||||
|
||||
|
||||
# propagates changes made by user of enumerator back to the original repeated field.
|
||||
# This only applies in cases where the calling function which created the enumerator,
|
||||
# such as #sort!, modifies itself rather than a new array, such as #sort
|
||||
class ProxyingEnumerator < Struct.new(:repeated_field, :external_enumerator)
|
||||
def each(*args, &block)
|
||||
results = []
|
||||
external_enumerator.each_with_index do |val, i|
|
||||
result = yield(val)
|
||||
results << result
|
||||
#nil means no change occurred from yield; usually occurs when #to_a is called
|
||||
if result
|
||||
repeated_field[i] = result if result != val
|
||||
end
|
||||
end
|
||||
results
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: google/protobuf/source_context.proto
|
||||
|
||||
require 'google/protobuf'
|
||||
|
||||
|
||||
descriptor_data = "\n$google/protobuf/source_context.proto\x12\x0fgoogle.protobuf\"\"\n\rSourceContext\x12\x11\n\tfile_name\x18\x01 \x01(\tB\x8a\x01\n\x13\x63om.google.protobufB\x12SourceContextProtoP\x01Z6google.golang.org/protobuf/types/known/sourcecontextpb\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3"
|
||||
|
||||
pool = Google::Protobuf::DescriptorPool.generated_pool
|
||||
pool.add_serialized_file(descriptor_data)
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
SourceContext = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.SourceContext").msgclass
|
||||
end
|
||||
end
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: google/protobuf/struct.proto
|
||||
|
||||
require 'google/protobuf'
|
||||
|
||||
|
||||
descriptor_data = "\n\x1cgoogle/protobuf/struct.proto\x12\x0fgoogle.protobuf\"\x84\x01\n\x06Struct\x12\x33\n\x06\x66ields\x18\x01 \x03(\x0b\x32#.google.protobuf.Struct.FieldsEntry\x1a\x45\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12%\n\x05value\x18\x02 \x01(\x0b\x32\x16.google.protobuf.Value:\x02\x38\x01\"\xea\x01\n\x05Value\x12\x30\n\nnull_value\x18\x01 \x01(\x0e\x32\x1a.google.protobuf.NullValueH\x00\x12\x16\n\x0cnumber_value\x18\x02 \x01(\x01H\x00\x12\x16\n\x0cstring_value\x18\x03 \x01(\tH\x00\x12\x14\n\nbool_value\x18\x04 \x01(\x08H\x00\x12/\n\x0cstruct_value\x18\x05 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x12\x30\n\nlist_value\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.ListValueH\x00\x42\x06\n\x04kind\"3\n\tListValue\x12&\n\x06values\x18\x01 \x03(\x0b\x32\x16.google.protobuf.Value*\x1b\n\tNullValue\x12\x0e\n\nNULL_VALUE\x10\x00\x42\x7f\n\x13\x63om.google.protobufB\x0bStructProtoP\x01Z/google.golang.org/protobuf/types/known/structpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3"
|
||||
|
||||
pool = Google::Protobuf::DescriptorPool.generated_pool
|
||||
pool.add_serialized_file(descriptor_data)
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
Struct = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Struct").msgclass
|
||||
Value = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Value").msgclass
|
||||
ListValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.ListValue").msgclass
|
||||
NullValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.NullValue").enummodule
|
||||
end
|
||||
end
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: google/protobuf/timestamp.proto
|
||||
|
||||
require 'google/protobuf'
|
||||
|
||||
|
||||
descriptor_data = "\n\x1fgoogle/protobuf/timestamp.proto\x12\x0fgoogle.protobuf\"+\n\tTimestamp\x12\x0f\n\x07seconds\x18\x01 \x01(\x03\x12\r\n\x05nanos\x18\x02 \x01(\x05\x42\x85\x01\n\x13\x63om.google.protobufB\x0eTimestampProtoP\x01Z2google.golang.org/protobuf/types/known/timestamppb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3"
|
||||
|
||||
pool = Google::Protobuf::DescriptorPool.generated_pool
|
||||
pool.add_serialized_file(descriptor_data)
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
Timestamp = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Timestamp").msgclass
|
||||
end
|
||||
end
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: google/protobuf/type.proto
|
||||
|
||||
require 'google/protobuf'
|
||||
|
||||
require 'google/protobuf/any_pb'
|
||||
require 'google/protobuf/source_context_pb'
|
||||
|
||||
|
||||
descriptor_data = "\n\x1agoogle/protobuf/type.proto\x12\x0fgoogle.protobuf\x1a\x19google/protobuf/any.proto\x1a$google/protobuf/source_context.proto\"\xe8\x01\n\x04Type\x12\x0c\n\x04name\x18\x01 \x01(\t\x12&\n\x06\x66ields\x18\x02 \x03(\x0b\x32\x16.google.protobuf.Field\x12\x0e\n\x06oneofs\x18\x03 \x03(\t\x12(\n\x07options\x18\x04 \x03(\x0b\x32\x17.google.protobuf.Option\x12\x36\n\x0esource_context\x18\x05 \x01(\x0b\x32\x1e.google.protobuf.SourceContext\x12\'\n\x06syntax\x18\x06 \x01(\x0e\x32\x17.google.protobuf.Syntax\x12\x0f\n\x07\x65\x64ition\x18\x07 \x01(\t\"\xd5\x05\n\x05\x46ield\x12)\n\x04kind\x18\x01 \x01(\x0e\x32\x1b.google.protobuf.Field.Kind\x12\x37\n\x0b\x63\x61rdinality\x18\x02 \x01(\x0e\x32\".google.protobuf.Field.Cardinality\x12\x0e\n\x06number\x18\x03 \x01(\x05\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x10\n\x08type_url\x18\x06 \x01(\t\x12\x13\n\x0boneof_index\x18\x07 \x01(\x05\x12\x0e\n\x06packed\x18\x08 \x01(\x08\x12(\n\x07options\x18\t \x03(\x0b\x32\x17.google.protobuf.Option\x12\x11\n\tjson_name\x18\n \x01(\t\x12\x15\n\rdefault_value\x18\x0b \x01(\t\"\xc8\x02\n\x04Kind\x12\x10\n\x0cTYPE_UNKNOWN\x10\x00\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"t\n\x0b\x43\x61rdinality\x12\x17\n\x13\x43\x41RDINALITY_UNKNOWN\x10\x00\x12\x18\n\x14\x43\x41RDINALITY_OPTIONAL\x10\x01\x12\x18\n\x14\x43\x41RDINALITY_REQUIRED\x10\x02\x12\x18\n\x14\x43\x41RDINALITY_REPEATED\x10\x03\"\xdf\x01\n\x04\x45num\x12\x0c\n\x04name\x18\x01 \x01(\t\x12-\n\tenumvalue\x18\x02 \x03(\x0b\x32\x1a.google.protobuf.EnumValue\x12(\n\x07options\x18\x03 \x03(\x0b\x32\x17.google.protobuf.Option\x12\x36\n\x0esource_context\x18\x04 \x01(\x0b\x32\x1e.google.protobuf.SourceContext\x12\'\n\x06syntax\x18\x05 \x01(\x0e\x32\x17.google.protobuf.Syntax\x12\x0f\n\x07\x65\x64ition\x18\x06 \x01(\t\"S\n\tEnumValue\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\x05\x12(\n\x07options\x18\x03 \x03(\x0b\x32\x17.google.protobuf.Option\";\n\x06Option\x12\x0c\n\x04name\x18\x01 \x01(\t\x12#\n\x05value\x18\x02 \x01(\x0b\x32\x14.google.protobuf.Any*C\n\x06Syntax\x12\x11\n\rSYNTAX_PROTO2\x10\x00\x12\x11\n\rSYNTAX_PROTO3\x10\x01\x12\x13\n\x0fSYNTAX_EDITIONS\x10\x02\x42{\n\x13\x63om.google.protobufB\tTypeProtoP\x01Z-google.golang.org/protobuf/types/known/typepb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3"
|
||||
|
||||
pool = Google::Protobuf::DescriptorPool.generated_pool
|
||||
pool.add_serialized_file(descriptor_data)
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
Type = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Type").msgclass
|
||||
Field = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Field").msgclass
|
||||
Field::Kind = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Field.Kind").enummodule
|
||||
Field::Cardinality = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Field.Cardinality").enummodule
|
||||
Enum = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Enum").msgclass
|
||||
EnumValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.EnumValue").msgclass
|
||||
Option = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Option").msgclass
|
||||
Syntax = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Syntax").enummodule
|
||||
end
|
||||
end
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
#!/usr/bin/ruby
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2008 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
require 'google/protobuf/any_pb'
|
||||
require 'google/protobuf/duration_pb'
|
||||
require 'google/protobuf/field_mask_pb'
|
||||
require 'google/protobuf/struct_pb'
|
||||
require 'google/protobuf/timestamp_pb'
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
|
||||
Any.class_eval do
|
||||
def self.pack(msg, type_url_prefix='type.googleapis.com/')
|
||||
any = self.new
|
||||
any.pack(msg, type_url_prefix)
|
||||
any
|
||||
end
|
||||
|
||||
def pack(msg, type_url_prefix='type.googleapis.com/')
|
||||
if type_url_prefix.empty? or type_url_prefix[-1] != '/' then
|
||||
self.type_url = "#{type_url_prefix}/#{msg.class.descriptor.name}"
|
||||
else
|
||||
self.type_url = "#{type_url_prefix}#{msg.class.descriptor.name}"
|
||||
end
|
||||
self.value = msg.to_proto
|
||||
end
|
||||
|
||||
def unpack(klass)
|
||||
if self.is(klass) then
|
||||
klass.decode(self.value)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def type_name
|
||||
return self.type_url.split("/")[-1]
|
||||
end
|
||||
|
||||
def is(klass)
|
||||
return self.type_name == klass.descriptor.name
|
||||
end
|
||||
end
|
||||
|
||||
Timestamp.class_eval do
|
||||
def to_time
|
||||
Time.at(seconds, nanos, :nanosecond)
|
||||
end
|
||||
|
||||
def self.from_time(time)
|
||||
new.from_time(time)
|
||||
end
|
||||
|
||||
def from_time(time)
|
||||
self.seconds = time.to_i
|
||||
self.nanos = time.nsec
|
||||
self
|
||||
end
|
||||
|
||||
def to_i
|
||||
self.seconds
|
||||
end
|
||||
|
||||
def to_f
|
||||
self.seconds + (self.nanos.quo(1_000_000_000))
|
||||
end
|
||||
end
|
||||
|
||||
Duration.class_eval do
|
||||
def to_f
|
||||
self.seconds + (self.nanos.to_f / 1_000_000_000)
|
||||
end
|
||||
end
|
||||
|
||||
class UnexpectedStructType < Google::Protobuf::Error; end
|
||||
|
||||
Value.class_eval do
|
||||
def to_ruby(recursive = false)
|
||||
case self.kind
|
||||
when :struct_value
|
||||
if recursive
|
||||
self.struct_value.to_h
|
||||
else
|
||||
self.struct_value
|
||||
end
|
||||
when :list_value
|
||||
if recursive
|
||||
self.list_value.to_a
|
||||
else
|
||||
self.list_value
|
||||
end
|
||||
when :null_value
|
||||
nil
|
||||
when :number_value
|
||||
self.number_value
|
||||
when :string_value
|
||||
self.string_value
|
||||
when :bool_value
|
||||
self.bool_value
|
||||
else
|
||||
raise UnexpectedStructType
|
||||
end
|
||||
end
|
||||
|
||||
def self.from_ruby(value)
|
||||
self.new.from_ruby(value)
|
||||
end
|
||||
|
||||
def from_ruby(value)
|
||||
case value
|
||||
when NilClass
|
||||
self.null_value = :NULL_VALUE
|
||||
when Numeric
|
||||
self.number_value = value
|
||||
when String
|
||||
self.string_value = value
|
||||
when TrueClass
|
||||
self.bool_value = true
|
||||
when FalseClass
|
||||
self.bool_value = false
|
||||
when Struct
|
||||
self.struct_value = value
|
||||
when Hash
|
||||
self.struct_value = Struct.from_hash(value)
|
||||
when ListValue
|
||||
self.list_value = value
|
||||
when Array
|
||||
self.list_value = ListValue.from_a(value)
|
||||
else
|
||||
raise UnexpectedStructType
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
Struct.class_eval do
|
||||
def [](key)
|
||||
self.fields[key].to_ruby
|
||||
rescue NoMethodError
|
||||
nil
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
unless key.is_a?(String)
|
||||
raise UnexpectedStructType, "Struct keys must be strings."
|
||||
end
|
||||
self.fields[key] ||= Google::Protobuf::Value.new
|
||||
self.fields[key].from_ruby(value)
|
||||
end
|
||||
|
||||
def to_h
|
||||
ret = {}
|
||||
self.fields.each { |key, val| ret[key] = val.to_ruby(true) }
|
||||
ret
|
||||
end
|
||||
|
||||
def self.from_hash(hash)
|
||||
ret = Struct.new
|
||||
hash.each { |key, val| ret[key] = val }
|
||||
ret
|
||||
end
|
||||
|
||||
def has_key?(key)
|
||||
self.fields.has_key?(key)
|
||||
end
|
||||
end
|
||||
|
||||
ListValue.class_eval do
|
||||
include Enumerable
|
||||
|
||||
def length
|
||||
self.values.length
|
||||
end
|
||||
|
||||
def [](index)
|
||||
self.values[index].to_ruby
|
||||
end
|
||||
|
||||
def []=(index, value)
|
||||
self.values[index].from_ruby(value)
|
||||
end
|
||||
|
||||
def <<(value)
|
||||
wrapper = Google::Protobuf::Value.new
|
||||
wrapper.from_ruby(value)
|
||||
self.values << wrapper
|
||||
end
|
||||
|
||||
def each
|
||||
self.values.each { |x| yield(x.to_ruby) }
|
||||
end
|
||||
|
||||
def to_a
|
||||
self.values.map { |x| x.to_ruby(true) }
|
||||
end
|
||||
|
||||
def self.from_a(arr)
|
||||
ret = ListValue.new
|
||||
arr.each { |val| ret << val }
|
||||
ret
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# frozen_string_literal: true
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: google/protobuf/wrappers.proto
|
||||
|
||||
require 'google/protobuf'
|
||||
|
||||
|
||||
descriptor_data = "\n\x1egoogle/protobuf/wrappers.proto\x12\x0fgoogle.protobuf\"\x1c\n\x0b\x44oubleValue\x12\r\n\x05value\x18\x01 \x01(\x01\"\x1b\n\nFloatValue\x12\r\n\x05value\x18\x01 \x01(\x02\"\x1b\n\nInt64Value\x12\r\n\x05value\x18\x01 \x01(\x03\"\x1c\n\x0bUInt64Value\x12\r\n\x05value\x18\x01 \x01(\x04\"\x1b\n\nInt32Value\x12\r\n\x05value\x18\x01 \x01(\x05\"\x1c\n\x0bUInt32Value\x12\r\n\x05value\x18\x01 \x01(\r\"\x1a\n\tBoolValue\x12\r\n\x05value\x18\x01 \x01(\x08\"\x1c\n\x0bStringValue\x12\r\n\x05value\x18\x01 \x01(\t\"\x1b\n\nBytesValue\x12\r\n\x05value\x18\x01 \x01(\x0c\x42\x83\x01\n\x13\x63om.google.protobufB\rWrappersProtoP\x01Z1google.golang.org/protobuf/types/known/wrapperspb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3"
|
||||
|
||||
pool = Google::Protobuf::DescriptorPool.generated_pool
|
||||
pool.add_serialized_file(descriptor_data)
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
DoubleValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.DoubleValue").msgclass
|
||||
FloatValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.FloatValue").msgclass
|
||||
Int64Value = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Int64Value").msgclass
|
||||
UInt64Value = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.UInt64Value").msgclass
|
||||
Int32Value = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Int32Value").msgclass
|
||||
UInt32Value = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.UInt32Value").msgclass
|
||||
BoolValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.BoolValue").msgclass
|
||||
StringValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.StringValue").msgclass
|
||||
BytesValue = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.BytesValue").msgclass
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,51 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2023 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
require 'ffi-compiler/loader'
|
||||
require 'google/protobuf/ffi/ffi'
|
||||
require 'google/protobuf/ffi/internal/type_safety'
|
||||
require 'google/protobuf/ffi/internal/pointer_helper'
|
||||
require 'google/protobuf/ffi/internal/arena'
|
||||
require 'google/protobuf/ffi/internal/convert'
|
||||
require 'google/protobuf/ffi/descriptor'
|
||||
require 'google/protobuf/ffi/enum_descriptor'
|
||||
require 'google/protobuf/ffi/field_descriptor'
|
||||
require 'google/protobuf/ffi/oneof_descriptor'
|
||||
require 'google/protobuf/ffi/method_descriptor'
|
||||
require 'google/protobuf/ffi/service_descriptor'
|
||||
require 'google/protobuf/ffi/descriptor_pool'
|
||||
require 'google/protobuf/ffi/file_descriptor'
|
||||
require 'google/protobuf/ffi/map'
|
||||
require 'google/protobuf/ffi/object_cache'
|
||||
require 'google/protobuf/ffi/repeated_field'
|
||||
require 'google/protobuf/ffi/message'
|
||||
|
||||
module Google
|
||||
module Protobuf
|
||||
def self.deep_copy(object)
|
||||
case object
|
||||
when RepeatedField
|
||||
RepeatedField.send(:deep_copy, object)
|
||||
when Google::Protobuf::Map
|
||||
Google::Protobuf::Map.deep_copy(object)
|
||||
when Google::Protobuf::MessageExts
|
||||
object.class.send(:deep_copy, object.instance_variable_get(:@msg))
|
||||
else
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
|
||||
def self.discard_unknown(message)
|
||||
raise FrozenError if message.frozen?
|
||||
raise ArgumentError.new "Expected message, got #{message.class} instead." if message.instance_variable_get(:@msg).nil?
|
||||
unless Google::Protobuf::FFI.message_discard_unknown(message.instance_variable_get(:@msg), message.class.descriptor, 128)
|
||||
raise RuntimeError.new "Messages nested too deeply."
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2023 Google Inc. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
if RUBY_PLATFORM == "java"
|
||||
require 'json'
|
||||
require 'google/protobuf_java'
|
||||
else
|
||||
begin
|
||||
require "google/#{RUBY_VERSION.sub(/\.\d+$/, '')}/protobuf_c"
|
||||
rescue LoadError
|
||||
require 'google/protobuf_c'
|
||||
end
|
||||
end
|
||||
|
||||
require 'google/protobuf/repeated_field'
|
||||
@@ -0,0 +1,100 @@
|
||||
# # @param task [FFI::Compiler::CompileTask] task to configure
|
||||
def configure_common_compile_task(task)
|
||||
if FileUtils.pwd.include? 'ext'
|
||||
src_dir = '.'
|
||||
third_party_path = 'third_party/utf8_range'
|
||||
else
|
||||
src_dir = 'ext/google/protobuf_c'
|
||||
third_party_path = 'ext/google/protobuf_c/third_party/utf8_range'
|
||||
end
|
||||
|
||||
task.add_include_path third_party_path
|
||||
task.add_define 'NDEBUG'
|
||||
task.cflags << "-std=gnu99 -O3"
|
||||
[
|
||||
:convert, :defs, :map, :message, :protobuf, :repeated_field, :wrap_memcpy
|
||||
].each { |file| task.exclude << "/#{file}.c" }
|
||||
task.ext_dir = src_dir
|
||||
task.source_dirs = [src_dir]
|
||||
if RbConfig::CONFIG['target_os'] =~ /darwin|linux/
|
||||
task.cflags << "-Wall -Wsign-compare -Wno-declaration-after-statement"
|
||||
end
|
||||
end
|
||||
|
||||
# FFI::CompilerTask's constructor walks the filesystem at task definition time
|
||||
# to create subtasks for each source file, so files from third_party must be
|
||||
# copied into place before the task is defined for it to work correctly.
|
||||
# TODO Is there a sane way to check for generated protos under lib too?
|
||||
def with_generated_files
|
||||
expected_path = FileUtils.pwd.include?('ext') ? 'third_party/utf8_range' : 'ext/google/protobuf_c/third_party/utf8_range'
|
||||
if File.directory?(expected_path)
|
||||
yield
|
||||
else
|
||||
task :default do
|
||||
# It is possible, especially in cases like the first invocation of
|
||||
# `rake test` following `rake clean` or a fresh checkout that the
|
||||
# `copy_third_party` task has been executed since initial task definition.
|
||||
# If so, run the task definition block now and invoke it explicitly.
|
||||
if File.directory?(expected_path)
|
||||
yield
|
||||
Rake::Task[:default].invoke
|
||||
else
|
||||
raise "Missing directory #{File.absolute_path(expected_path)}." +
|
||||
" Did you forget to run `rake copy_third_party` before building" +
|
||||
" native extensions?"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
require "ffi-compiler/compile_task"
|
||||
|
||||
desc "Compile Protobuf library for FFI"
|
||||
namespace "ffi-protobuf" do
|
||||
with_generated_files do
|
||||
# Compile Ruby UPB separately in order to limit use of -DUPB_BUILD_API to one
|
||||
# compilation unit.
|
||||
desc "Compile UPB library for FFI"
|
||||
namespace "ffi-upb" do
|
||||
with_generated_files do
|
||||
FFI::Compiler::CompileTask.new('ruby-upb') do |c|
|
||||
configure_common_compile_task c
|
||||
c.add_define "UPB_BUILD_API"
|
||||
c.exclude << "/glue.c"
|
||||
c.exclude << "/shared_message.c"
|
||||
c.exclude << "/shared_convert.c"
|
||||
if RbConfig::CONFIG['target_os'] =~ /darwin|linux/
|
||||
c.cflags << "-fvisibility=hidden"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
FFI::Compiler::CompileTask.new 'protobuf_c_ffi' do |c|
|
||||
configure_common_compile_task c
|
||||
# Ruby UPB was already compiled with different flags.
|
||||
c.exclude << "/utf8_range.c"
|
||||
c.exclude << "/ruby-upb.c"
|
||||
end
|
||||
|
||||
# Setup dependencies so that the .o files generated by building ffi-upb are
|
||||
# available to link here.
|
||||
# TODO Can this be simplified? Can the single shared library be used
|
||||
# instead of the object files?
|
||||
protobuf_c_task = Rake::Task[:default]
|
||||
protobuf_c_shared_lib_task = Rake::Task[protobuf_c_task.prereqs.last]
|
||||
ruby_upb_shared_lib_task = Rake::Task[:"ffi-upb:default"].prereqs.first
|
||||
Rake::Task[ruby_upb_shared_lib_task].prereqs.each do |dependency|
|
||||
protobuf_c_shared_lib_task.prereqs.prepend dependency
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue LoadError
|
||||
desc "Compile Protobuf library for FFI"
|
||||
namespace "ffi-protobuf" do
|
||||
task :default do
|
||||
warn "Skipping build of FFI; `gem install ffi-compiler` to enable."
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user