@ -31,6 +31,7 @@
# include "absl/strings/str_format.h"
# include "absl/strings/string_view.h"
# include "absl/synchronization/mutex.h"
# include "google/protobuf/arena.h"
# include "google/protobuf/descriptor.h"
# include "google/protobuf/descriptor.pb.h"
# include "google/protobuf/descriptor_lite.h"
@ -79,6 +80,11 @@ namespace protobuf {
namespace {
bool IsMapFieldInApi ( const FieldDescriptor * field ) { return field - > is_map ( ) ; }
bool IsMapEntry ( const FieldDescriptor * field ) {
return ( field - > containing_type ( ) ! = nullptr & &
field - > containing_type ( ) - > options ( ) . map_entry ( ) ) ;
}
Message * MaybeForceCopy ( Arena * arena , Message * msg ) {
if ( arena ! = nullptr | | msg = = nullptr ) return msg ;
@ -1178,7 +1184,7 @@ void Reflection::SwapFieldsImpl(
// oneof already. This has to be done after SwapField, because SwapField
// may depend on the information in has bits.
if ( ! field - > is_repeated ( ) ) {
SwapHasBit ( message1 , message2 , field ) ;
Naive SwapHasBit( message1 , message2 , field ) ;
if ( field - > cpp_type ( ) = = FieldDescriptor : : CPPTYPE_STRING & &
field - > cpp_string_type ( ) = =
FieldDescriptor : : CppStringType : : kString & &
@ -1749,7 +1755,8 @@ void Reflection::ListFields(const Message& message,
}
} else if ( has_bits & & has_bits_indices [ i ] ! = static_cast < uint32_t > ( - 1 ) ) {
// Equivalent to: HasFieldSingular(message, field)
if ( IsIndexInHasBitSet ( has_bits , has_bits_indices [ i ] ) ) {
if ( IsFieldPresentGivenHasbits ( message , field , has_bits ,
has_bits_indices [ i ] ) ) {
append_to_output ( field ) ;
}
} else if ( HasFieldSingular ( message , field ) ) {
@ -2989,74 +2996,127 @@ void Reflection::SwapInlinedStringDonated(Message* lhs, Message* rhs,
}
}
// Simple accessors for manipulating has_bits_.
bool Reflection : : IsSingularFieldNonEmpty ( const Message & message ,
const FieldDescriptor * field ) const {
ABSL_DCHECK ( IsMapEntry ( field ) | | ! field - > has_presence ( ) ) ;
ABSL_DCHECK ( ! field - > is_repeated ( ) ) ;
ABSL_DCHECK ( ! field - > is_map ( ) ) ;
ABSL_DCHECK ( field - > cpp_type ( ) ! = FieldDescriptor : : CPPTYPE_MESSAGE ) ;
// Scalar primitive (numeric or string/bytes) fields are present if
// their value is non-zero (numeric) or non-empty (string/bytes). N.B.:
// we must use this definition here, rather than the "scalar fields
// always present" in the proto3 docs, because MergeFrom() semantics
// require presence as "present on wire", and reflection-based merge
// (which uses HasField()) needs to be consistent with this.
switch ( field - > cpp_type ( ) ) {
case FieldDescriptor : : CPPTYPE_BOOL :
return GetRaw < bool > ( message , field ) ! = false ;
case FieldDescriptor : : CPPTYPE_INT32 :
return GetRaw < int32_t > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_INT64 :
return GetRaw < int64_t > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_UINT32 :
return GetRaw < uint32_t > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_UINT64 :
return GetRaw < uint64_t > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_FLOAT :
static_assert ( sizeof ( uint32_t ) = = sizeof ( float ) ,
" Code assumes uint32_t and float are the same size. " ) ;
return GetRaw < uint32_t > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_DOUBLE :
static_assert ( sizeof ( uint64_t ) = = sizeof ( double ) ,
" Code assumes uint64_t and double are the same size. " ) ;
return GetRaw < uint64_t > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_ENUM :
return GetRaw < int > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_STRING :
switch ( field - > cpp_string_type ( ) ) {
case FieldDescriptor : : CppStringType : : kCord :
return ! GetField < const absl : : Cord > ( message , field ) . empty ( ) ;
case FieldDescriptor : : CppStringType : : kView :
case FieldDescriptor : : CppStringType : : kString : {
if ( IsInlined ( field ) ) {
return ! GetField < InlinedStringField > ( message , field )
. GetNoArena ( )
. empty ( ) ;
}
return ! GetField < ArenaStringPtr > ( message , field ) . Get ( ) . empty ( ) ;
}
default :
internal : : Unreachable ( ) ;
}
case FieldDescriptor : : CPPTYPE_MESSAGE :
default :
internal : : Unreachable ( ) ;
}
}
bool Reflection : : IsFieldPresentGivenHasbits ( const Message & message ,
const FieldDescriptor * field ,
const uint32_t * hasbits ,
uint32_t hasbit_index ) const {
// If hasbit exists but is not set, field is guaranteed to be missing.
if ( ! IsIndexInHasBitSet ( hasbits , hasbit_index ) ) {
return false ;
}
// For explicit-presence fields, a set hasbit indicates a present field.
if ( field - > has_presence ( ) ) {
return true ;
}
// proto3: hasbits are present, but an additional zero check must be
// performed because hasbit can be set to true while field is zero.
// Repeated fields do not have hasbits enabled in proto3.
ABSL_DCHECK ( ! field - > is_repeated ( ) )
< < " repeated fields do not have hasbits in proto3. " ;
// Handling map entries in proto3:
// Implicit presence map fields are represented as a native C++ map, but their
// corresponding MapEntry messages (e.g. if we want to access them as repeated
// MapEntry fields) will unconditionally be generated with hasbits. MapEntrys
// behave like explicit presence fields. That is, in MapEntry's C++
// implementation...
// - key can be null, empty, or nonempty;
// - value can be null, empty, or nonempty.
if ( IsMapEntry ( field ) ) {
return true ;
}
// This is the vanilla case: for a non-repeated primitive or string field,
// returns if the field is nonzero (i.e. present in proto3 semantics).
return IsSingularFieldNonEmpty ( message , field ) ;
}
bool Reflection : : HasFieldSingular ( const Message & message ,
const FieldDescriptor * field ) const {
ABSL_DCHECK ( ! field - > options ( ) . weak ( ) ) ;
if ( schema_ . HasBitIndex ( field ) ! = static_cast < uint32_t > ( - 1 ) ) {
return IsIndexInHasBitSet ( GetHasBits ( message ) , schema_ . HasBitIndex ( field ) ) ;
return IsFieldPresentGivenHasbits ( message , field , GetHasBits ( message ) ,
schema_ . HasBitIndex ( field ) ) ;
}
// proto3: no has-bits. All fields present except messages, which are
// The python implementation traditionally assumes that proto3 messages don't
// have hasbits. As a result, proto3 objects created through dynamic message
// in Python won't have hasbits. We need the following code to preserve
// compatibility.
// NOTE: It would be nice to be able to remove it, but we need one
// or more breaking changes in order to do so.
//
// proto3 with no has-bits. All fields present except messages, which are
// present only if their message-field pointer is non-null.
if ( field - > cpp_type ( ) = = FieldDescriptor : : CPPTYPE_MESSAGE ) {
return ! schema_ . IsDefaultInstance ( message ) & &
GetRaw < const Message * > ( message , field ) ! = nullptr ;
} else {
// Non-message field (and non-oneof, since that was handled in HasField()
// before calling us), and singular (again, checked in HasField). So, this
// field must be a scalar.
// Scalar primitive (numeric or string/bytes) fields are present if
// their value is non-zero (numeric) or non-empty (string/bytes). N.B.:
// we must use this definition here, rather than the "scalar fields
// always present" in the proto3 docs, because MergeFrom() semantics
// require presence as "present on wire", and reflection-based merge
// (which uses HasField()) needs to be consistent with this.
switch ( field - > cpp_type ( ) ) {
case FieldDescriptor : : CPPTYPE_STRING :
switch ( field - > cpp_string_type ( ) ) {
case FieldDescriptor : : CppStringType : : kCord :
return ! GetField < const absl : : Cord > ( message , field ) . empty ( ) ;
case FieldDescriptor : : CppStringType : : kView :
case FieldDescriptor : : CppStringType : : kString : {
if ( IsInlined ( field ) ) {
return ! GetField < InlinedStringField > ( message , field )
. GetNoArena ( )
. empty ( ) ;
}
return GetField < ArenaStringPtr > ( message , field ) . Get ( ) . size ( ) > 0 ;
}
}
internal : : Unreachable ( ) ;
case FieldDescriptor : : CPPTYPE_BOOL :
return GetRaw < bool > ( message , field ) ! = false ;
case FieldDescriptor : : CPPTYPE_INT32 :
return GetRaw < int32_t > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_INT64 :
return GetRaw < int64_t > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_UINT32 :
return GetRaw < uint32_t > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_UINT64 :
return GetRaw < uint64_t > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_FLOAT :
static_assert ( sizeof ( uint32_t ) = = sizeof ( float ) ,
" Code assumes uint32_t and float are the same size. " ) ;
return GetRaw < uint32_t > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_DOUBLE :
static_assert ( sizeof ( uint64_t ) = = sizeof ( double ) ,
" Code assumes uint64_t and double are the same size. " ) ;
return GetRaw < uint64_t > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_ENUM :
return GetRaw < int > ( message , field ) ! = 0 ;
case FieldDescriptor : : CPPTYPE_MESSAGE :
// handled above; avoid warning
break ;
}
ABSL_LOG ( FATAL ) < < " Reached impossible case in HasFieldSingular(). " ;
return false ;
}
// Non-message field (and non-oneof, since that was handled in HasField()
// before calling us), and singular (again, checked in HasField). So, this
// field must be a scalar.
return IsSingularFieldNonEmpty ( message , field ) ;
}
void Reflection : : SetHasBit ( Message * message ,
@ -3077,23 +3137,31 @@ void Reflection::ClearHasBit(Message* message,
~ ( static_cast < uint32_t > ( 1 ) < < ( index % 32 ) ) ;
}
void Reflection : : SwapHasBit ( Message * message1 , Message * message2 ,
const FieldDescriptor * field ) const {
void Reflection : : Naive SwapHasBit( Message * message1 , Message * message2 ,
const FieldDescriptor * field ) const {
ABSL_DCHECK ( ! field - > options ( ) . weak ( ) ) ;
if ( ! schema_ . HasHasbits ( ) ) {
return ;
}
bool temp_is_present = HasFieldSingular ( * message1 , field ) ;
if ( HasFieldSingular ( * message2 , field ) ) {
SetHasBit ( message1 , field ) ;
} else {
ClearHasBit ( message1 , field ) ;
}
if ( temp_is_present ) {
const Reflection * r1 = message1 - > GetReflection ( ) ;
const Reflection * r2 = message2 - > GetReflection ( ) ;
bool is_m1_hasbit_set = IsIndexInHasBitSet ( r1 - > GetHasBits ( * message1 ) ,
r1 - > schema_ . HasBitIndex ( field ) ) ;
bool is_m2_hasbit_set = IsIndexInHasBitSet ( r2 - > GetHasBits ( * message2 ) ,
r2 - > schema_ . HasBitIndex ( field ) ) ;
if ( is_m1_hasbit_set ) {
SetHasBit ( message2 , field ) ;
} else {
ClearHasBit ( message2 , field ) ;
}
if ( is_m2_hasbit_set ) {
SetHasBit ( message1 , field ) ;
} else {
ClearHasBit ( message1 , field ) ;
}
}
bool Reflection : : HasOneof ( const Message & message ,