Protocol Buffers - Google's data interchange format (grpc依赖) https://developers.google.com/protocol-buffers/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

332 lines
13 KiB

# Protocol Buffers - Google's data interchange format
# Copyright 2022 Google Inc. All rights reserved.
# https://developers.google.com/protocol-buffers/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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::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
# @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
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(jatl) 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 :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 :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