diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index f38b326c0dca..b00a11519d22 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -722,12 +722,20 @@ PHP_METHOD(Message, mergeFromJsonString) { } upb_Status_Clear(&status); - if (!upb_JsonDecode(data, data_len, intern->msg, intern->desc->msgdef, - DescriptorPool_GetSymbolTable(), options, arena, - &status)) { - zend_throw_exception_ex(NULL, 0, "Error occurred during parsing: %s", - upb_Status_ErrorMessage(&status)); - return; + int result = upb_JsonDecodeDetectingNonconformance( + data, data_len, intern->msg, intern->desc->msgdef, + DescriptorPool_GetSymbolTable(), options, arena, &status); + + switch (result) { + case kUpb_JsonDecodeResult_Ok: + break; + case kUpb_JsonDecodeResult_OkWithEmptyStringNumerics: + zend_error(E_USER_WARNING, "%s", upb_Status_ErrorMessage(&status)); + return; + case kUpb_JsonDecodeResult_Error: + zend_throw_exception_ex(NULL, 0, "Error occurred during parsing: %s", + upb_Status_ErrorMessage(&status)); + return; } } diff --git a/php/ext/google/protobuf/php-upb.c b/php/ext/google/protobuf/php-upb.c index d3528b0f54a8..5e09474c7e85 100644 --- a/php/ext/google/protobuf/php-upb.c +++ b/php/ext/google/protobuf/php-upb.c @@ -523,6 +523,7 @@ typedef struct { upb_Arena* arena; /* TODO: should we have a tmp arena for tmp data? */ const upb_DefPool* symtab; int depth; + int result; upb_Status* status; jmp_buf err; int line; @@ -1152,6 +1153,16 @@ static int64_t jsondec_strtoint64(jsondec* d, upb_StringView str) { return ret; } +static void jsondec_checkempty(jsondec* d, upb_StringView str, + const upb_FieldDef* f) { + if (str.size != 0) return; + d->result = kUpb_JsonDecodeResult_OkWithEmptyStringNumerics; + upb_Status_SetErrorFormat(d->status, + "Empty string is not a valid number (field: %s). " + "This will be an error in a future version.", + upb_FieldDef_FullName(f)); +} + /* Primitive value types ******************************************************/ /* Parse INT32 or INT64 value. */ @@ -1173,6 +1184,7 @@ static upb_MessageValue jsondec_int(jsondec* d, const upb_FieldDef* f) { } case JD_STRING: { upb_StringView str = jsondec_string(d); + jsondec_checkempty(d, str, f); val.int64_val = jsondec_strtoint64(d, str); break; } @@ -1210,6 +1222,7 @@ static upb_MessageValue jsondec_uint(jsondec* d, const upb_FieldDef* f) { } case JD_STRING: { upb_StringView str = jsondec_string(d); + jsondec_checkempty(d, str, f); val.uint64_val = jsondec_strtouint64(d, str); break; } @@ -1238,14 +1251,26 @@ static upb_MessageValue jsondec_double(jsondec* d, const upb_FieldDef* f) { break; case JD_STRING: str = jsondec_string(d); - if (jsondec_streql(str, "NaN")) { + if (str.size == 0) { + jsondec_checkempty(d, str, f); + val.double_val = 0.0; + } else if (jsondec_streql(str, "NaN")) { val.double_val = NAN; } else if (jsondec_streql(str, "Infinity")) { val.double_val = INFINITY; } else if (jsondec_streql(str, "-Infinity")) { val.double_val = -INFINITY; } else { - val.double_val = strtod(str.data, NULL); + char* end; + val.double_val = strtod(str.data, &end); + if (end != str.data + str.size) { + d->result = kUpb_JsonDecodeResult_OkWithEmptyStringNumerics; + upb_Status_SetErrorFormat( + d->status, + "Non-number characters in quoted number (field: %s). " + "This will be an error in a future version.", + upb_FieldDef_FullName(f)); + } } break; default: @@ -1987,10 +2012,10 @@ static void jsondec_wellknown(jsondec* d, upb_Message* msg, } } -static bool upb_JsonDecoder_Decode(jsondec* const d, upb_Message* const msg, - const upb_MessageDef* const m) { +static int upb_JsonDecoder_Decode(jsondec* const d, upb_Message* const msg, + const upb_MessageDef* const m) { UPB_ASSERT(!upb_Message_IsFrozen(msg)); - if (UPB_SETJMP(d->err)) return false; + if (UPB_SETJMP(d->err)) return kUpb_JsonDecodeResult_Error; jsondec_tomsg(d, msg, m); @@ -1999,16 +2024,19 @@ static bool upb_JsonDecoder_Decode(jsondec* const d, upb_Message* const msg, jsondec_consumews(d); if (d->ptr == d->end) { - return true; + return d->result; } else { jsondec_seterrmsg(d, "unexpected trailing characters"); - return false; + return kUpb_JsonDecodeResult_Error; } } -bool upb_JsonDecode(const char* buf, size_t size, upb_Message* msg, - const upb_MessageDef* m, const upb_DefPool* symtab, - int options, upb_Arena* arena, upb_Status* status) { +int upb_JsonDecodeDetectingNonconformance(const char* buf, size_t size, + upb_Message* msg, + const upb_MessageDef* m, + const upb_DefPool* symtab, + int options, upb_Arena* arena, + upb_Status* status) { UPB_ASSERT(!upb_Message_IsFrozen(msg)); jsondec d; @@ -2021,6 +2049,7 @@ bool upb_JsonDecode(const char* buf, size_t size, upb_Message* msg, d.status = status; d.options = options; d.depth = 64; + d.result = kUpb_JsonDecodeResult_Ok; d.line = 1; d.line_begin = d.ptr; d.debug_field = NULL; diff --git a/php/ext/google/protobuf/php-upb.h b/php/ext/google/protobuf/php-upb.h index f1052ab6d884..fa4c27d3da7a 100644 --- a/php/ext/google/protobuf/php-upb.h +++ b/php/ext/google/protobuf/php-upb.h @@ -1131,16 +1131,47 @@ UPB_FORCEINLINE bool upb_EpsCopyInputStream_TryParseDelimitedFast( #ifndef UPB_JSON_DECODE_H_ #define UPB_JSON_DECODE_H_ +#include -#ifndef UPB_REFLECTION_DEF_H_ -#define UPB_REFLECTION_DEF_H_ -// IWYU pragma: begin_exports +// Public APIs for message operations that do not depend on the schema. +// +// MiniTable-based accessors live in accessors.h. -// IWYU pragma: private, include "upb/reflection/def.h" +#ifndef UPB_MESSAGE_MESSAGE_H_ +#define UPB_MESSAGE_MESSAGE_H_ -#ifndef UPB_REFLECTION_DEF_POOL_H_ -#define UPB_REFLECTION_DEF_POOL_H_ +#include + + +/* +** Our memory representation for parsing tables and messages themselves. +** Functions in this file are used by generated code and possibly reflection. +** +** The definitions in this file are internal to upb. +**/ + +#ifndef UPB_MESSAGE_INTERNAL_MESSAGE_H_ +#define UPB_MESSAGE_INTERNAL_MESSAGE_H_ + +#include +#include + + +#ifndef UPB_MESSAGE_INTERNAL_EXTENSION_H_ +#define UPB_MESSAGE_INTERNAL_EXTENSION_H_ + +#include + + +// Users should include array.h or map.h instead. +// IWYU pragma: private, include "upb/message/array.h" + +#ifndef UPB_MESSAGE_VALUE_H_ +#define UPB_MESSAGE_VALUE_H_ + +#include +#include #ifndef UPB_BASE_STRING_VIEW_H_ #define UPB_BASE_STRING_VIEW_H_ @@ -1194,74 +1225,63 @@ UPB_INLINE bool upb_StringView_IsEqual(upb_StringView a, upb_StringView b) { #endif /* UPB_BASE_STRING_VIEW_H_ */ -// IWYU pragma: private, include "upb/reflection/def.h" - -// Declarations common to all public def types. - -#ifndef UPB_REFLECTION_COMMON_H_ -#define UPB_REFLECTION_COMMON_H_ - -#ifndef THIRD_PARTY_UPB_UPB_REFLECTION_DESCRIPTOR_BOOTSTRAP_H_ -#define THIRD_PARTY_UPB_UPB_REFLECTION_DESCRIPTOR_BOOTSTRAP_H_ - -// IWYU pragma: begin_exports - -#if defined(UPB_BOOTSTRAP_STAGE) && UPB_BOOTSTRAP_STAGE == 0 -// This header is checked in. -#elif UPB_BOOTSTRAP_STAGE == 1 -// This header is generated at build time by the bootstrapping process. -#else -// This is the normal header, generated by upb_c_proto_library(). -/* This file was generated by upb_generator from the input file: - * - * google/protobuf/descriptor.proto - * - * Do not edit -- your changes will be discarded when the file is - * regenerated. - * NO CHECKED-IN PROTOBUF GENCODE */ - -#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPB_H__UPB_H_ -#define GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPB_H__UPB_H_ +// Must be last. +#ifdef __cplusplus +extern "C" { +#endif -#ifndef UPB_GENERATED_CODE_SUPPORT_H_ -#define UPB_GENERATED_CODE_SUPPORT_H_ +typedef union { + bool bool_val; + float float_val; + double double_val; + int32_t int32_val; + int64_t int64_val; + uint32_t uint32_val; + uint64_t uint64_val; + const struct upb_Array* array_val; + const struct upb_Map* map_val; + const struct upb_Message* msg_val; + upb_StringView str_val; -// IWYU pragma: begin_exports + // EXPERIMENTAL: A tagged upb_Message*. Users must use this instead of + // msg_val if unlinked sub-messages may possibly be in use. See the + // documentation in kUpb_DecodeOption_ExperimentalAllowUnlinked for more + // information. + uintptr_t tagged_msg_val; // upb_TaggedMessagePtr +} upb_MessageValue; -#ifndef UPB_BASE_UPCAST_H_ -#define UPB_BASE_UPCAST_H_ +UPB_API_INLINE upb_MessageValue upb_MessageValue_Zero(void) { + upb_MessageValue zero; + memset(&zero, 0, sizeof(zero)); + return zero; +} -// Must be last. +typedef union { + struct upb_Array* array; + struct upb_Map* map; + struct upb_Message* msg; +} upb_MutableMessageValue; -// This macro provides a way to upcast message pointers in a way that is -// somewhat more bulletproof than blindly casting a pointer. Example: -// -// typedef struct { -// upb_Message UPB_PRIVATE(base); -// } pkg_FooMessage; -// -// void f(pkg_FooMessage* msg) { -// upb_Decode(UPB_UPCAST(msg), ...); -// } +UPB_API_INLINE upb_MutableMessageValue upb_MutableMessageValue_Zero(void) { + upb_MutableMessageValue zero; + memset(&zero, 0, sizeof(zero)); + return zero; +} -#define UPB_UPCAST(x) (&(x)->base##_dont_copy_me__upb_internal_use_only) +#ifdef __cplusplus +} /* extern "C" */ +#endif -#endif /* UPB_BASE_UPCAST_H_ */ +#endif /* UPB_MESSAGE_VALUE_H_ */ -#ifndef UPB_MESSAGE_ACCESSORS_H_ -#define UPB_MESSAGE_ACCESSORS_H_ +#ifndef UPB_MINI_TABLE_EXTENSION_H_ +#define UPB_MINI_TABLE_EXTENSION_H_ #include -#ifndef UPB_MESSAGE_ARRAY_H_ -#define UPB_MESSAGE_ARRAY_H_ - -#include - - #ifndef UPB_BASE_DESCRIPTOR_CONSTANTS_H_ #define UPB_BASE_DESCRIPTOR_CONSTANTS_H_ @@ -1365,290 +1385,94 @@ UPB_INLINE bool upb_FieldType_IsPackable(upb_FieldType field_type) { #endif /* UPB_BASE_DESCRIPTOR_CONSTANTS_H_ */ -#ifndef UPB_MESSAGE_INTERNAL_ARRAY_H_ -#define UPB_MESSAGE_INTERNAL_ARRAY_H_ +#ifndef UPB_MINI_TABLE_INTERNAL_EXTENSION_H_ +#define UPB_MINI_TABLE_INTERNAL_EXTENSION_H_ +#include #include -#include -// Must be last. +#ifndef UPB_MINI_TABLE_INTERNAL_FIELD_H_ +#define UPB_MINI_TABLE_INTERNAL_FIELD_H_ -#define _UPB_ARRAY_MASK_IMM 0x4 // Frozen/immutable bit. -#define _UPB_ARRAY_MASK_LG2 0x3 // Encoded elem size. -#define _UPB_ARRAY_MASK_ALL (_UPB_ARRAY_MASK_IMM | _UPB_ARRAY_MASK_LG2) +#include +#include + + +#ifndef UPB_MINI_TABLE_INTERNAL_SIZE_LOG2_H_ +#define UPB_MINI_TABLE_INTERNAL_SIZE_LOG2_H_ + +#include +#include + + +// Must be last. #ifdef __cplusplus extern "C" { #endif -// LINT.IfChange(upb_Array) +// Return the log2 of the storage size in bytes for a upb_CType +UPB_INLINE int UPB_PRIVATE(_upb_CType_SizeLg2)(upb_CType c_type) { + static const int8_t size[] = { + 0, // kUpb_CType_Bool + 2, // kUpb_CType_Float + 2, // kUpb_CType_Int32 + 2, // kUpb_CType_UInt32 + 2, // kUpb_CType_Enum + UPB_SIZE(2, 3), // kUpb_CType_Message + 3, // kUpb_CType_Double + 3, // kUpb_CType_Int64 + 3, // kUpb_CType_UInt64 + UPB_SIZE(3, 4), // kUpb_CType_String + UPB_SIZE(3, 4), // kUpb_CType_Bytes + }; -// Our internal representation for repeated fields. -struct upb_Array { - // This is a tagged pointer. Bits #0 and #1 encode the elem size as follows: - // 0 maps to elem size 1 - // 1 maps to elem size 4 - // 2 maps to elem size 8 - // 3 maps to elem size 16 - // - // Bit #2 contains the frozen/immutable flag. - uintptr_t UPB_ONLYBITS(data); + // -1 here because the enum is one-based but the table is zero-based. + return size[c_type - 1]; +} - size_t UPB_ONLYBITS(size); // The number of elements in the array. - size_t UPB_PRIVATE(capacity); // Allocated storage. Measured in elements. -}; +// Return the log2 of the storage size in bytes for a upb_FieldType +UPB_INLINE int UPB_PRIVATE(_upb_FieldType_SizeLg2)(upb_FieldType field_type) { + static const int8_t size[] = { + 3, // kUpb_FieldType_Double + 2, // kUpb_FieldType_Float + 3, // kUpb_FieldType_Int64 + 3, // kUpb_FieldType_UInt64 + 2, // kUpb_FieldType_Int32 + 3, // kUpb_FieldType_Fixed64 + 2, // kUpb_FieldType_Fixed32 + 0, // kUpb_FieldType_Bool + UPB_SIZE(3, 4), // kUpb_FieldType_String + UPB_SIZE(2, 3), // kUpb_FieldType_Group + UPB_SIZE(2, 3), // kUpb_FieldType_Message + UPB_SIZE(3, 4), // kUpb_FieldType_Bytes + 2, // kUpb_FieldType_UInt32 + 2, // kUpb_FieldType_Enum + 2, // kUpb_FieldType_SFixed32 + 3, // kUpb_FieldType_SFixed64 + 2, // kUpb_FieldType_SInt32 + 3, // kUpb_FieldType_SInt64 + }; -UPB_INLINE void UPB_PRIVATE(_upb_Array_ShallowFreeze)(struct upb_Array* arr) { - arr->UPB_ONLYBITS(data) |= _UPB_ARRAY_MASK_IMM; + // -1 here because the enum is one-based but the table is zero-based. + return size[field_type - 1]; } -UPB_API_INLINE bool upb_Array_IsFrozen(const struct upb_Array* arr) { - return (arr->UPB_ONLYBITS(data) & _UPB_ARRAY_MASK_IMM) != 0; -} +#ifdef __cplusplus +} /* extern "C" */ +#endif -UPB_INLINE void UPB_PRIVATE(_upb_Array_SetTaggedPtr)(struct upb_Array* array, - void* data, size_t lg2) { - UPB_ASSERT(lg2 != 1); - UPB_ASSERT(lg2 <= 4); - const size_t bits = lg2 - (lg2 != 0); - array->UPB_ONLYBITS(data) = (uintptr_t)data | bits; -} -UPB_INLINE size_t -UPB_PRIVATE(_upb_Array_ElemSizeLg2)(const struct upb_Array* array) { - const size_t bits = array->UPB_ONLYBITS(data) & _UPB_ARRAY_MASK_LG2; - const size_t lg2 = bits + (bits != 0); - return lg2; -} +#endif /* UPB_MINI_TABLE_INTERNAL_SIZE_LOG2_H_ */ -UPB_API_INLINE const void* upb_Array_DataPtr(const struct upb_Array* array) { - UPB_PRIVATE(_upb_Array_ElemSizeLg2)(array); // Check assertions. - return (void*)(array->UPB_ONLYBITS(data) & ~(uintptr_t)_UPB_ARRAY_MASK_ALL); -} +// Must be last. -UPB_API_INLINE void* upb_Array_MutableDataPtr(struct upb_Array* array) { - return (void*)upb_Array_DataPtr(array); -} - -UPB_INLINE struct upb_Array* UPB_PRIVATE(_upb_Array_New)(upb_Arena* arena, - size_t init_capacity, - int elem_size_lg2) { - UPB_ASSERT(elem_size_lg2 != 1); - UPB_ASSERT(elem_size_lg2 <= 4); - const size_t array_size = - UPB_ALIGN_UP(sizeof(struct upb_Array), UPB_MALLOC_ALIGN); - const size_t bytes = array_size + (init_capacity << elem_size_lg2); - struct upb_Array* array = (struct upb_Array*)upb_Arena_Malloc(arena, bytes); - if (!array) return NULL; - UPB_PRIVATE(_upb_Array_SetTaggedPtr) - (array, UPB_PTR_AT(array, array_size, void), elem_size_lg2); - array->UPB_ONLYBITS(size) = 0; - array->UPB_PRIVATE(capacity) = init_capacity; - return array; -} - -// Resizes the capacity of the array to be at least min_size. -bool UPB_PRIVATE(_upb_Array_Realloc)(struct upb_Array* array, size_t min_size, - upb_Arena* arena); - -UPB_API_INLINE bool upb_Array_Reserve(struct upb_Array* array, size_t size, - upb_Arena* arena) { - UPB_ASSERT(!upb_Array_IsFrozen(array)); - if (array->UPB_PRIVATE(capacity) < size) - return UPB_PRIVATE(_upb_Array_Realloc)(array, size, arena); - return true; -} - -// Resize without initializing new elements. -UPB_INLINE bool UPB_PRIVATE(_upb_Array_ResizeUninitialized)( - struct upb_Array* array, size_t size, upb_Arena* arena) { - UPB_ASSERT(!upb_Array_IsFrozen(array)); - UPB_ASSERT(size <= array->UPB_ONLYBITS(size) || - arena); // Allow NULL arena when shrinking. - if (!upb_Array_Reserve(array, size, arena)) return false; - array->UPB_ONLYBITS(size) = size; - return true; -} - -// This function is intended for situations where elem_size is compile-time -// constant or a known expression of the form (1 << lg2), so that the expression -// i*elem_size does not result in an actual multiplication. -UPB_INLINE void UPB_PRIVATE(_upb_Array_Set)(struct upb_Array* array, size_t i, - const void* data, - size_t elem_size) { - UPB_ASSERT(!upb_Array_IsFrozen(array)); - UPB_ASSERT(i < array->UPB_ONLYBITS(size)); - UPB_ASSERT(elem_size == 1U << UPB_PRIVATE(_upb_Array_ElemSizeLg2)(array)); - char* arr_data = (char*)upb_Array_MutableDataPtr(array); - memcpy(arr_data + (i * elem_size), data, elem_size); -} - -UPB_API_INLINE size_t upb_Array_Size(const struct upb_Array* arr) { - return arr->UPB_ONLYBITS(size); -} - -// LINT.ThenChange(GoogleInternalName0) - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#undef _UPB_ARRAY_MASK_IMM -#undef _UPB_ARRAY_MASK_LG2 -#undef _UPB_ARRAY_MASK_ALL - - -#endif /* UPB_MESSAGE_INTERNAL_ARRAY_H_ */ - -// Users should include array.h or map.h instead. -// IWYU pragma: private, include "upb/message/array.h" - -#ifndef UPB_MESSAGE_VALUE_H_ -#define UPB_MESSAGE_VALUE_H_ - -#include -#include - - -// Must be last. - -#ifdef __cplusplus -extern "C" { -#endif - -typedef union { - bool bool_val; - float float_val; - double double_val; - int32_t int32_val; - int64_t int64_val; - uint32_t uint32_val; - uint64_t uint64_val; - const struct upb_Array* array_val; - const struct upb_Map* map_val; - const struct upb_Message* msg_val; - upb_StringView str_val; - - // EXPERIMENTAL: A tagged upb_Message*. Users must use this instead of - // msg_val if unlinked sub-messages may possibly be in use. See the - // documentation in kUpb_DecodeOption_ExperimentalAllowUnlinked for more - // information. - uintptr_t tagged_msg_val; // upb_TaggedMessagePtr -} upb_MessageValue; - -UPB_API_INLINE upb_MessageValue upb_MessageValue_Zero(void) { - upb_MessageValue zero; - memset(&zero, 0, sizeof(zero)); - return zero; -} - -typedef union { - struct upb_Array* array; - struct upb_Map* map; - struct upb_Message* msg; -} upb_MutableMessageValue; - -UPB_API_INLINE upb_MutableMessageValue upb_MutableMessageValue_Zero(void) { - upb_MutableMessageValue zero; - memset(&zero, 0, sizeof(zero)); - return zero; -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif - - -#endif /* UPB_MESSAGE_VALUE_H_ */ - -#ifndef UPB_MINI_TABLE_FIELD_H_ -#define UPB_MINI_TABLE_FIELD_H_ - -#include - - -#ifndef UPB_MINI_TABLE_INTERNAL_FIELD_H_ -#define UPB_MINI_TABLE_INTERNAL_FIELD_H_ - -#include -#include - - -#ifndef UPB_MINI_TABLE_INTERNAL_SIZE_LOG2_H_ -#define UPB_MINI_TABLE_INTERNAL_SIZE_LOG2_H_ - -#include -#include - - -// Must be last. - -#ifdef __cplusplus -extern "C" { -#endif - -// Return the log2 of the storage size in bytes for a upb_CType -UPB_INLINE int UPB_PRIVATE(_upb_CType_SizeLg2)(upb_CType c_type) { - static const int8_t size[] = { - 0, // kUpb_CType_Bool - 2, // kUpb_CType_Float - 2, // kUpb_CType_Int32 - 2, // kUpb_CType_UInt32 - 2, // kUpb_CType_Enum - UPB_SIZE(2, 3), // kUpb_CType_Message - 3, // kUpb_CType_Double - 3, // kUpb_CType_Int64 - 3, // kUpb_CType_UInt64 - UPB_SIZE(3, 4), // kUpb_CType_String - UPB_SIZE(3, 4), // kUpb_CType_Bytes - }; - - // -1 here because the enum is one-based but the table is zero-based. - return size[c_type - 1]; -} - -// Return the log2 of the storage size in bytes for a upb_FieldType -UPB_INLINE int UPB_PRIVATE(_upb_FieldType_SizeLg2)(upb_FieldType field_type) { - static const int8_t size[] = { - 3, // kUpb_FieldType_Double - 2, // kUpb_FieldType_Float - 3, // kUpb_FieldType_Int64 - 3, // kUpb_FieldType_UInt64 - 2, // kUpb_FieldType_Int32 - 3, // kUpb_FieldType_Fixed64 - 2, // kUpb_FieldType_Fixed32 - 0, // kUpb_FieldType_Bool - UPB_SIZE(3, 4), // kUpb_FieldType_String - UPB_SIZE(2, 3), // kUpb_FieldType_Group - UPB_SIZE(2, 3), // kUpb_FieldType_Message - UPB_SIZE(3, 4), // kUpb_FieldType_Bytes - 2, // kUpb_FieldType_UInt32 - 2, // kUpb_FieldType_Enum - 2, // kUpb_FieldType_SFixed32 - 3, // kUpb_FieldType_SFixed64 - 2, // kUpb_FieldType_SInt32 - 3, // kUpb_FieldType_SInt64 - }; - - // -1 here because the enum is one-based but the table is zero-based. - return size[field_type - 1]; -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif - - -#endif /* UPB_MINI_TABLE_INTERNAL_SIZE_LOG2_H_ */ - -// Must be last. - -// LINT.IfChange(struct_definition) -struct upb_MiniTableField { - uint32_t UPB_ONLYBITS(number); - uint16_t UPB_ONLYBITS(offset); - int16_t presence; // If >0, hasbit_index. If <0, ~oneof_index +// LINT.IfChange(struct_definition) +struct upb_MiniTableField { + uint32_t UPB_ONLYBITS(number); + uint16_t UPB_ONLYBITS(offset); + int16_t presence; // If >0, hasbit_index. If <0, ~oneof_index // Indexes into `upb_MiniTable.subs` // Will be set to `kUpb_NoSub` if `descriptortype` != MESSAGE/GROUP/ENUM @@ -1848,47 +1672,104 @@ UPB_INLINE size_t UPB_PRIVATE(_upb_MiniTableField_ElemSizeLg2)( #endif /* UPB_MINI_TABLE_INTERNAL_FIELD_H_ */ +#ifndef UPB_MINI_TABLE_INTERNAL_SUB_H_ +#define UPB_MINI_TABLE_INTERNAL_SUB_H_ + // Must be last. -typedef struct upb_MiniTableField upb_MiniTableField; +typedef union { + const struct upb_MiniTable* const* UPB_PRIVATE(submsg); + const struct upb_MiniTableEnum* UPB_PRIVATE(subenum); +} upb_MiniTableSubInternal; + +union upb_MiniTableSub { + const struct upb_MiniTable* UPB_PRIVATE(submsg); + const struct upb_MiniTableEnum* UPB_PRIVATE(subenum); +}; #ifdef __cplusplus extern "C" { #endif -UPB_API_INLINE upb_CType upb_MiniTableField_CType(const upb_MiniTableField* f); +UPB_API_INLINE union upb_MiniTableSub upb_MiniTableSub_FromEnum( + const struct upb_MiniTableEnum* subenum) { + union upb_MiniTableSub out; + out.UPB_PRIVATE(subenum) = subenum; + return out; +} -UPB_API_INLINE bool upb_MiniTableField_HasPresence(const upb_MiniTableField* f); +UPB_API_INLINE union upb_MiniTableSub upb_MiniTableSub_FromMessage( + const struct upb_MiniTable* submsg) { + union upb_MiniTableSub out; + out.UPB_PRIVATE(submsg) = submsg; + return out; +} -UPB_API_INLINE bool upb_MiniTableField_IsArray(const upb_MiniTableField* f); +UPB_API_INLINE const struct upb_MiniTableEnum* upb_MiniTableSub_Enum( + const union upb_MiniTableSub sub) { + return sub.UPB_PRIVATE(subenum); +} -UPB_API_INLINE bool upb_MiniTableField_IsClosedEnum( - const upb_MiniTableField* f); +UPB_API_INLINE const struct upb_MiniTable* upb_MiniTableSub_Message( + const union upb_MiniTableSub sub) { + return sub.UPB_PRIVATE(submsg); +} -UPB_API_INLINE bool upb_MiniTableField_IsExtension(const upb_MiniTableField* f); +#ifdef __cplusplus +} /* extern "C" */ +#endif -UPB_API_INLINE bool upb_MiniTableField_IsInOneof(const upb_MiniTableField* f); -UPB_API_INLINE bool upb_MiniTableField_IsMap(const upb_MiniTableField* f); +#endif /* UPB_MINI_TABLE_INTERNAL_SUB_H_ */ -UPB_API_INLINE bool upb_MiniTableField_IsPacked(const upb_MiniTableField* f); +// Must be last. -UPB_API_INLINE bool upb_MiniTableField_IsScalar(const upb_MiniTableField* f); +struct upb_MiniTableExtension { + // Do not move this field. We need to be able to alias pointers. + struct upb_MiniTableField UPB_PRIVATE(field); -UPB_API_INLINE bool upb_MiniTableField_IsSubMessage( - const upb_MiniTableField* f); + const struct upb_MiniTable* UPB_PRIVATE(extendee); + union upb_MiniTableSub UPB_PRIVATE(sub); // NULL unless submsg or proto2 enum +}; -UPB_API_INLINE uint32_t upb_MiniTableField_Number(const upb_MiniTableField* f); +#ifdef __cplusplus +extern "C" { +#endif -UPB_API_INLINE upb_FieldType -upb_MiniTableField_Type(const upb_MiniTableField* f); +UPB_API_INLINE upb_CType +upb_MiniTableExtension_CType(const struct upb_MiniTableExtension* e) { + return upb_MiniTableField_CType(&e->UPB_PRIVATE(field)); +} + +UPB_API_INLINE uint32_t +upb_MiniTableExtension_Number(const struct upb_MiniTableExtension* e) { + return e->UPB_PRIVATE(field).UPB_ONLYBITS(number); +} + +UPB_API_INLINE const struct upb_MiniTable* upb_MiniTableExtension_GetSubMessage( + const struct upb_MiniTableExtension* e) { + if (upb_MiniTableExtension_CType(e) != kUpb_CType_Message) { + return NULL; + } + return upb_MiniTableSub_Message(e->UPB_PRIVATE(sub)); +} + +UPB_API_INLINE void upb_MiniTableExtension_SetSubMessage( + struct upb_MiniTableExtension* e, const struct upb_MiniTable* m) { + e->UPB_PRIVATE(sub).UPB_PRIVATE(submsg) = m; +} + +UPB_INLINE upb_FieldRep UPB_PRIVATE(_upb_MiniTableExtension_GetRep)( + const struct upb_MiniTableExtension* e) { + return UPB_PRIVATE(_upb_MiniTableField_GetRep)(&e->UPB_PRIVATE(field)); +} #ifdef __cplusplus } /* extern "C" */ #endif -#endif /* UPB_MINI_TABLE_FIELD_H_ */ +#endif /* UPB_MINI_TABLE_INTERNAL_EXTENSION_H_ */ #ifndef UPB_MINI_TABLE_MESSAGE_H_ #define UPB_MINI_TABLE_MESSAGE_H_ @@ -1968,62 +1849,60 @@ UPB_API_INLINE bool upb_MiniTableEnum_CheckValue(const upb_MiniTableEnum* e, #endif /* UPB_MINI_TABLE_ENUM_H_ */ -#ifndef UPB_MINI_TABLE_INTERNAL_MESSAGE_H_ -#define UPB_MINI_TABLE_INTERNAL_MESSAGE_H_ +#ifndef UPB_MINI_TABLE_FIELD_H_ +#define UPB_MINI_TABLE_FIELD_H_ -#include #include -#ifndef UPB_MINI_TABLE_INTERNAL_SUB_H_ -#define UPB_MINI_TABLE_INTERNAL_SUB_H_ - // Must be last. -typedef union { - const struct upb_MiniTable* const* UPB_PRIVATE(submsg); - const struct upb_MiniTableEnum* UPB_PRIVATE(subenum); -} upb_MiniTableSubInternal; - -union upb_MiniTableSub { - const struct upb_MiniTable* UPB_PRIVATE(submsg); - const struct upb_MiniTableEnum* UPB_PRIVATE(subenum); -}; +typedef struct upb_MiniTableField upb_MiniTableField; #ifdef __cplusplus extern "C" { #endif -UPB_API_INLINE union upb_MiniTableSub upb_MiniTableSub_FromEnum( - const struct upb_MiniTableEnum* subenum) { - union upb_MiniTableSub out; - out.UPB_PRIVATE(subenum) = subenum; - return out; -} +UPB_API_INLINE upb_CType upb_MiniTableField_CType(const upb_MiniTableField* f); -UPB_API_INLINE union upb_MiniTableSub upb_MiniTableSub_FromMessage( - const struct upb_MiniTable* submsg) { - union upb_MiniTableSub out; - out.UPB_PRIVATE(submsg) = submsg; - return out; -} +UPB_API_INLINE bool upb_MiniTableField_HasPresence(const upb_MiniTableField* f); -UPB_API_INLINE const struct upb_MiniTableEnum* upb_MiniTableSub_Enum( - const union upb_MiniTableSub sub) { - return sub.UPB_PRIVATE(subenum); -} +UPB_API_INLINE bool upb_MiniTableField_IsArray(const upb_MiniTableField* f); -UPB_API_INLINE const struct upb_MiniTable* upb_MiniTableSub_Message( - const union upb_MiniTableSub sub) { - return sub.UPB_PRIVATE(submsg); -} +UPB_API_INLINE bool upb_MiniTableField_IsClosedEnum( + const upb_MiniTableField* f); + +UPB_API_INLINE bool upb_MiniTableField_IsExtension(const upb_MiniTableField* f); + +UPB_API_INLINE bool upb_MiniTableField_IsInOneof(const upb_MiniTableField* f); + +UPB_API_INLINE bool upb_MiniTableField_IsMap(const upb_MiniTableField* f); + +UPB_API_INLINE bool upb_MiniTableField_IsPacked(const upb_MiniTableField* f); + +UPB_API_INLINE bool upb_MiniTableField_IsScalar(const upb_MiniTableField* f); + +UPB_API_INLINE bool upb_MiniTableField_IsSubMessage( + const upb_MiniTableField* f); + +UPB_API_INLINE uint32_t upb_MiniTableField_Number(const upb_MiniTableField* f); + +UPB_API_INLINE upb_FieldType +upb_MiniTableField_Type(const upb_MiniTableField* f); #ifdef __cplusplus } /* extern "C" */ #endif -#endif /* UPB_MINI_TABLE_INTERNAL_SUB_H_ */ +#endif /* UPB_MINI_TABLE_FIELD_H_ */ + +#ifndef UPB_MINI_TABLE_INTERNAL_MESSAGE_H_ +#define UPB_MINI_TABLE_INTERNAL_MESSAGE_H_ + +#include +#include + // Must be last. @@ -2292,90 +2171,73 @@ bool upb_MiniTable_NextOneofField(const upb_MiniTable* m, // Must be last. -typedef struct upb_Array upb_Array; +typedef struct upb_MiniTableExtension upb_MiniTableExtension; #ifdef __cplusplus extern "C" { #endif -// Creates a new array on the given arena that holds elements of this type. -UPB_API upb_Array* upb_Array_New(upb_Arena* a, upb_CType type); - -// Returns the number of elements in the array. -UPB_API_INLINE size_t upb_Array_Size(const upb_Array* arr); - -// Returns the given element, which must be within the array's current size. -UPB_API upb_MessageValue upb_Array_Get(const upb_Array* arr, size_t i); +UPB_API_INLINE upb_CType +upb_MiniTableExtension_CType(const upb_MiniTableExtension* e); -// Returns a mutating pointer to the given element, which must be within the -// array's current size. -UPB_API upb_MutableMessageValue upb_Array_GetMutable(upb_Array* arr, size_t i); +UPB_API_INLINE uint32_t +upb_MiniTableExtension_Number(const upb_MiniTableExtension* e); -// Sets the given element, which must be within the array's current size. -UPB_API void upb_Array_Set(upb_Array* arr, size_t i, upb_MessageValue val); +UPB_API_INLINE const upb_MiniTable* upb_MiniTableExtension_GetSubMessage( + const upb_MiniTableExtension* e); -// Appends an element to the array. Returns false on allocation failure. -UPB_API bool upb_Array_Append(upb_Array* array, upb_MessageValue val, - upb_Arena* arena); +UPB_API_INLINE void upb_MiniTableExtension_SetSubMessage( + upb_MiniTableExtension* e, const upb_MiniTable* m); -// Moves elements within the array using memmove(). -// Like memmove(), the source and destination elements may be overlapping. -UPB_API void upb_Array_Move(upb_Array* array, size_t dst_idx, size_t src_idx, - size_t count); +#ifdef __cplusplus +} /* extern "C" */ +#endif -// Inserts one or more empty elements into the array. -// Existing elements are shifted right. -// The new elements have undefined state and must be set with `upb_Array_Set()`. -// REQUIRES: `i <= upb_Array_Size(arr)` -UPB_API bool upb_Array_Insert(upb_Array* array, size_t i, size_t count, - upb_Arena* arena); -// Deletes one or more elements from the array. -// Existing elements are shifted left. -// REQUIRES: `i + count <= upb_Array_Size(arr)` -UPB_API void upb_Array_Delete(upb_Array* array, size_t i, size_t count); +#endif /* UPB_MINI_TABLE_EXTENSION_H_ */ -// Reserves |size| elements of storage for the array. -UPB_API_INLINE bool upb_Array_Reserve(struct upb_Array* array, size_t size, - upb_Arena* arena); +// Must be last. -// Changes the size of a vector. New elements are initialized to NULL/0. -// Returns false on allocation failure. -UPB_API bool upb_Array_Resize(upb_Array* array, size_t size, upb_Arena* arena); +// The internal representation of an extension is self-describing: it contains +// enough information that we can serialize it to binary format without needing +// to look it up in a upb_ExtensionRegistry. +// +// This representation allocates 16 bytes to data on 64-bit platforms. +// This is rather wasteful for scalars (in the extreme case of bool, +// it wastes 15 bytes). We accept this because we expect messages to be +// the most common extension type. +typedef struct { + const upb_MiniTableExtension* ext; + upb_MessageValue data; +} upb_Extension; -// Returns pointer to array data. -UPB_API_INLINE const void* upb_Array_DataPtr(const upb_Array* arr); +#ifdef __cplusplus +extern "C" { +#endif -// Returns mutable pointer to array data. -UPB_API_INLINE void* upb_Array_MutableDataPtr(upb_Array* arr); +// Adds the given extension data to the given message. +// |ext| is copied into the message instance. +// This logically replaces any previously-added extension with this number. +upb_Extension* UPB_PRIVATE(_upb_Message_GetOrCreateExtension)( + struct upb_Message* msg, const upb_MiniTableExtension* ext, + upb_Arena* arena); -// Mark an array and all of its descendents as frozen/immutable. -// If the array elements are messages then |m| must point to the minitable for -// those messages. Otherwise |m| must be NULL. -UPB_API void upb_Array_Freeze(upb_Array* arr, const upb_MiniTable* m); +// Returns an array of extensions for this message. +// Note: the array is ordered in reverse relative to the order of creation. +const upb_Extension* UPB_PRIVATE(_upb_Message_Getexts)( + const struct upb_Message* msg, size_t* count); -// Returns whether an array has been frozen. -UPB_API_INLINE bool upb_Array_IsFrozen(const upb_Array* arr); +// Returns an extension for a message with a given mini table, +// or NULL if no extension exists with this mini table. +const upb_Extension* UPB_PRIVATE(_upb_Message_Getext)( + const struct upb_Message* msg, const upb_MiniTableExtension* ext); #ifdef __cplusplus } /* extern "C" */ #endif -#endif /* UPB_MESSAGE_ARRAY_H_ */ - -#ifndef UPB_MESSAGE_INTERNAL_ACCESSORS_H_ -#define UPB_MESSAGE_INTERNAL_ACCESSORS_H_ - -#include -#include -#include - - -#ifndef UPB_BASE_INTERNAL_ENDIAN_H_ -#define UPB_BASE_INTERNAL_ENDIAN_H_ - -#include +#endif /* UPB_MESSAGE_INTERNAL_EXTENSION_H_ */ // Must be last. @@ -2383,170 +2245,501 @@ UPB_API_INLINE bool upb_Array_IsFrozen(const upb_Array* arr); extern "C" { #endif -UPB_INLINE bool upb_IsLittleEndian(void) { - const int x = 1; - return *(char*)&x == 1; -} - -UPB_INLINE uint32_t upb_BigEndian32(uint32_t val) { - if (upb_IsLittleEndian()) return val; - - return ((val & 0xff) << 24) | ((val & 0xff00) << 8) | - ((val & 0xff0000) >> 8) | ((val & 0xff000000) >> 24); -} +extern const float kUpb_FltInfinity; +extern const double kUpb_Infinity; +extern const double kUpb_NaN; -UPB_INLINE uint64_t upb_BigEndian64(uint64_t val) { - if (upb_IsLittleEndian()) return val; +// Internal members of a upb_Message that track unknown fields and/or +// extensions. We can change this without breaking binary compatibility. - const uint64_t hi = ((uint64_t)upb_BigEndian32((uint32_t)val)) << 32; - const uint64_t lo = upb_BigEndian32((uint32_t)(val >> 32)); - return hi | lo; -} +typedef struct upb_Message_Internal { + // Total size of this structure, including the data that follows. + // Must be aligned to 8, which is alignof(upb_Extension) + uint32_t size; -#ifdef __cplusplus -} /* extern "C" */ + /* Offsets relative to the beginning of this structure. + * + * Unknown data grows forward from the beginning to unknown_end. + * Extension data grows backward from size to ext_begin. + * When the two meet, we're out of data and have to realloc. + * + * If we imagine that the final member of this struct is: + * char data[size - overhead]; // overhead = sizeof(upb_Message_Internal) + * + * Then we have: + * unknown data: data[0 .. (unknown_end - overhead)] + * extensions data: data[(ext_begin - overhead) .. (size - overhead)] */ + uint32_t unknown_end; + uint32_t ext_begin; + // Data follows, as if there were an array: + // char data[size - sizeof(upb_Message_Internal)]; +} upb_Message_Internal; + +#ifdef UPB_TRACING_ENABLED +UPB_API void upb_Message_LogNewMessage(const upb_MiniTable* m, + const upb_Arena* arena); +UPB_API void upb_Message_SetNewMessageTraceHandler( + void (*handler)(const upb_MiniTable*, const upb_Arena*)); +#endif // UPB_TRACING_ENABLED + +// Inline version upb_Message_New(), for internal use. +UPB_INLINE struct upb_Message* _upb_Message_New(const upb_MiniTable* m, + upb_Arena* a) { +#ifdef UPB_TRACING_ENABLED + upb_Message_LogNewMessage(m, a); +#endif // UPB_TRACING_ENABLED + + const int size = m->UPB_PRIVATE(size); + struct upb_Message* msg = (struct upb_Message*)upb_Arena_Malloc(a, size); + if (UPB_UNLIKELY(!msg)) return NULL; + memset(msg, 0, size); + return msg; +} + +// Discards the unknown fields for this message only. +void _upb_Message_DiscardUnknown_shallow(struct upb_Message* msg); + +// Adds unknown data (serialized protobuf data) to the given message. +// The data is copied into the message instance. +bool UPB_PRIVATE(_upb_Message_AddUnknown)(struct upb_Message* msg, + const char* data, size_t len, + upb_Arena* arena); + +bool UPB_PRIVATE(_upb_Message_Realloc)(struct upb_Message* msg, size_t need, + upb_Arena* arena); + +#ifdef __cplusplus +} /* extern "C" */ #endif -#endif /* UPB_BASE_INTERNAL_ENDIAN_H_ */ +#endif /* UPB_MESSAGE_INTERNAL_MESSAGE_H_ */ -#ifndef UPB_MESSAGE_INTERNAL_EXTENSION_H_ -#define UPB_MESSAGE_INTERNAL_EXTENSION_H_ +#ifndef UPB_MESSAGE_INTERNAL_TYPES_H_ +#define UPB_MESSAGE_INTERNAL_TYPES_H_ -#include +#include + +// Must be last. +#define UPB_OPAQUE(x) x##_opaque -#ifndef UPB_MINI_TABLE_EXTENSION_H_ -#define UPB_MINI_TABLE_EXTENSION_H_ +struct upb_Message { + union { + uintptr_t UPB_OPAQUE(internal); // tagged pointer, low bit == frozen + double d; // Forces same size for 32-bit/64-bit builds + }; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +UPB_INLINE void UPB_PRIVATE(_upb_Message_ShallowFreeze)( + struct upb_Message* msg) { + msg->UPB_OPAQUE(internal) |= 1ULL; +} + +UPB_API_INLINE bool upb_Message_IsFrozen(const struct upb_Message* msg) { + return (msg->UPB_OPAQUE(internal) & 1ULL) != 0; +} + +UPB_INLINE struct upb_Message_Internal* UPB_PRIVATE(_upb_Message_GetInternal)( + const struct upb_Message* msg) { + const uintptr_t tmp = msg->UPB_OPAQUE(internal) & ~1ULL; + return (struct upb_Message_Internal*)tmp; +} + +UPB_INLINE void UPB_PRIVATE(_upb_Message_SetInternal)( + struct upb_Message* msg, struct upb_Message_Internal* internal) { + UPB_ASSERT(!upb_Message_IsFrozen(msg)); + msg->UPB_OPAQUE(internal) = (uintptr_t)internal; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#undef UPB_OPAQUE + + +#endif /* UPB_MESSAGE_INTERNAL_TYPES_H_ */ + +// Must be last. + +typedef struct upb_Message upb_Message; + +#ifdef __cplusplus +extern "C" { +#endif + +// Creates a new message with the given mini_table on the given arena. +UPB_API upb_Message* upb_Message_New(const upb_MiniTable* m, upb_Arena* arena); + +// Returns a reference to the message's unknown data. +const char* upb_Message_GetUnknown(const upb_Message* msg, size_t* len); + +// Removes partial unknown data from message. +void upb_Message_DeleteUnknown(upb_Message* msg, const char* data, size_t len); + +// Returns the number of extensions present in this message. +size_t upb_Message_ExtensionCount(const upb_Message* msg); + +// Mark a message and all of its descendents as frozen/immutable. +UPB_API void upb_Message_Freeze(upb_Message* msg, const upb_MiniTable* m); + +// Returns whether a message has been frozen. +UPB_API_INLINE bool upb_Message_IsFrozen(const upb_Message* msg); + +#ifdef UPB_TRACING_ENABLED +UPB_API void upb_Message_LogNewMessage(const upb_MiniTable* m, + const upb_Arena* arena); + +UPB_API void upb_Message_SetNewMessageTraceHandler( + void (*handler)(const upb_MiniTable* m, const upb_Arena* arena)); +#endif // UPB_TRACING_ENABLED + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* UPB_MESSAGE_MESSAGE_H_ */ + +#ifndef UPB_REFLECTION_DEF_H_ +#define UPB_REFLECTION_DEF_H_ + +// IWYU pragma: begin_exports + +// IWYU pragma: private, include "upb/reflection/def.h" + +#ifndef UPB_REFLECTION_DEF_POOL_H_ +#define UPB_REFLECTION_DEF_POOL_H_ + + +// IWYU pragma: private, include "upb/reflection/def.h" + +// Declarations common to all public def types. + +#ifndef UPB_REFLECTION_COMMON_H_ +#define UPB_REFLECTION_COMMON_H_ + +#ifndef THIRD_PARTY_UPB_UPB_REFLECTION_DESCRIPTOR_BOOTSTRAP_H_ +#define THIRD_PARTY_UPB_UPB_REFLECTION_DESCRIPTOR_BOOTSTRAP_H_ + +// IWYU pragma: begin_exports + +#if defined(UPB_BOOTSTRAP_STAGE) && UPB_BOOTSTRAP_STAGE == 0 +// This header is checked in. +#elif UPB_BOOTSTRAP_STAGE == 1 +// This header is generated at build time by the bootstrapping process. +#else +// This is the normal header, generated by upb_c_proto_library(). +/* This file was generated by upb_generator from the input file: + * + * google/protobuf/descriptor.proto + * + * Do not edit -- your changes will be discarded when the file is + * regenerated. + * NO CHECKED-IN PROTOBUF GENCODE */ + +#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPB_H__UPB_H_ +#define GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPB_H__UPB_H_ + + +#ifndef UPB_GENERATED_CODE_SUPPORT_H_ +#define UPB_GENERATED_CODE_SUPPORT_H_ + +// IWYU pragma: begin_exports + +#ifndef UPB_BASE_UPCAST_H_ +#define UPB_BASE_UPCAST_H_ + +// Must be last. + +// This macro provides a way to upcast message pointers in a way that is +// somewhat more bulletproof than blindly casting a pointer. Example: +// +// typedef struct { +// upb_Message UPB_PRIVATE(base); +// } pkg_FooMessage; +// +// void f(pkg_FooMessage* msg) { +// upb_Decode(UPB_UPCAST(msg), ...); +// } + +#define UPB_UPCAST(x) (&(x)->base##_dont_copy_me__upb_internal_use_only) + + +#endif /* UPB_BASE_UPCAST_H_ */ + +#ifndef UPB_MESSAGE_ACCESSORS_H_ +#define UPB_MESSAGE_ACCESSORS_H_ #include -#ifndef UPB_MINI_TABLE_INTERNAL_EXTENSION_H_ -#define UPB_MINI_TABLE_INTERNAL_EXTENSION_H_ +#ifndef UPB_MESSAGE_ARRAY_H_ +#define UPB_MESSAGE_ARRAY_H_ #include + + +#ifndef UPB_MESSAGE_INTERNAL_ARRAY_H_ +#define UPB_MESSAGE_INTERNAL_ARRAY_H_ + #include +#include // Must be last. -struct upb_MiniTableExtension { - // Do not move this field. We need to be able to alias pointers. - struct upb_MiniTableField UPB_PRIVATE(field); - - const struct upb_MiniTable* UPB_PRIVATE(extendee); - union upb_MiniTableSub UPB_PRIVATE(sub); // NULL unless submsg or proto2 enum -}; +#define _UPB_ARRAY_MASK_IMM 0x4 // Frozen/immutable bit. +#define _UPB_ARRAY_MASK_LG2 0x3 // Encoded elem size. +#define _UPB_ARRAY_MASK_ALL (_UPB_ARRAY_MASK_IMM | _UPB_ARRAY_MASK_LG2) #ifdef __cplusplus extern "C" { #endif -UPB_API_INLINE upb_CType -upb_MiniTableExtension_CType(const struct upb_MiniTableExtension* e) { - return upb_MiniTableField_CType(&e->UPB_PRIVATE(field)); +// LINT.IfChange(upb_Array) + +// Our internal representation for repeated fields. +struct upb_Array { + // This is a tagged pointer. Bits #0 and #1 encode the elem size as follows: + // 0 maps to elem size 1 + // 1 maps to elem size 4 + // 2 maps to elem size 8 + // 3 maps to elem size 16 + // + // Bit #2 contains the frozen/immutable flag. + uintptr_t UPB_ONLYBITS(data); + + size_t UPB_ONLYBITS(size); // The number of elements in the array. + size_t UPB_PRIVATE(capacity); // Allocated storage. Measured in elements. +}; + +UPB_INLINE void UPB_PRIVATE(_upb_Array_ShallowFreeze)(struct upb_Array* arr) { + arr->UPB_ONLYBITS(data) |= _UPB_ARRAY_MASK_IMM; } -UPB_API_INLINE uint32_t -upb_MiniTableExtension_Number(const struct upb_MiniTableExtension* e) { - return e->UPB_PRIVATE(field).UPB_ONLYBITS(number); +UPB_API_INLINE bool upb_Array_IsFrozen(const struct upb_Array* arr) { + return (arr->UPB_ONLYBITS(data) & _UPB_ARRAY_MASK_IMM) != 0; } -UPB_API_INLINE const struct upb_MiniTable* upb_MiniTableExtension_GetSubMessage( - const struct upb_MiniTableExtension* e) { - if (upb_MiniTableExtension_CType(e) != kUpb_CType_Message) { - return NULL; - } - return upb_MiniTableSub_Message(e->UPB_PRIVATE(sub)); +UPB_INLINE void UPB_PRIVATE(_upb_Array_SetTaggedPtr)(struct upb_Array* array, + void* data, size_t lg2) { + UPB_ASSERT(lg2 != 1); + UPB_ASSERT(lg2 <= 4); + const size_t bits = lg2 - (lg2 != 0); + array->UPB_ONLYBITS(data) = (uintptr_t)data | bits; } -UPB_API_INLINE void upb_MiniTableExtension_SetSubMessage( - struct upb_MiniTableExtension* e, const struct upb_MiniTable* m) { - e->UPB_PRIVATE(sub).UPB_PRIVATE(submsg) = m; +UPB_INLINE size_t +UPB_PRIVATE(_upb_Array_ElemSizeLg2)(const struct upb_Array* array) { + const size_t bits = array->UPB_ONLYBITS(data) & _UPB_ARRAY_MASK_LG2; + const size_t lg2 = bits + (bits != 0); + return lg2; } -UPB_INLINE upb_FieldRep UPB_PRIVATE(_upb_MiniTableExtension_GetRep)( - const struct upb_MiniTableExtension* e) { - return UPB_PRIVATE(_upb_MiniTableField_GetRep)(&e->UPB_PRIVATE(field)); +UPB_API_INLINE const void* upb_Array_DataPtr(const struct upb_Array* array) { + UPB_PRIVATE(_upb_Array_ElemSizeLg2)(array); // Check assertions. + return (void*)(array->UPB_ONLYBITS(data) & ~(uintptr_t)_UPB_ARRAY_MASK_ALL); +} + +UPB_API_INLINE void* upb_Array_MutableDataPtr(struct upb_Array* array) { + return (void*)upb_Array_DataPtr(array); +} + +UPB_INLINE struct upb_Array* UPB_PRIVATE(_upb_Array_New)(upb_Arena* arena, + size_t init_capacity, + int elem_size_lg2) { + UPB_ASSERT(elem_size_lg2 != 1); + UPB_ASSERT(elem_size_lg2 <= 4); + const size_t array_size = + UPB_ALIGN_UP(sizeof(struct upb_Array), UPB_MALLOC_ALIGN); + const size_t bytes = array_size + (init_capacity << elem_size_lg2); + struct upb_Array* array = (struct upb_Array*)upb_Arena_Malloc(arena, bytes); + if (!array) return NULL; + UPB_PRIVATE(_upb_Array_SetTaggedPtr) + (array, UPB_PTR_AT(array, array_size, void), elem_size_lg2); + array->UPB_ONLYBITS(size) = 0; + array->UPB_PRIVATE(capacity) = init_capacity; + return array; +} + +// Resizes the capacity of the array to be at least min_size. +bool UPB_PRIVATE(_upb_Array_Realloc)(struct upb_Array* array, size_t min_size, + upb_Arena* arena); + +UPB_API_INLINE bool upb_Array_Reserve(struct upb_Array* array, size_t size, + upb_Arena* arena) { + UPB_ASSERT(!upb_Array_IsFrozen(array)); + if (array->UPB_PRIVATE(capacity) < size) + return UPB_PRIVATE(_upb_Array_Realloc)(array, size, arena); + return true; +} + +// Resize without initializing new elements. +UPB_INLINE bool UPB_PRIVATE(_upb_Array_ResizeUninitialized)( + struct upb_Array* array, size_t size, upb_Arena* arena) { + UPB_ASSERT(!upb_Array_IsFrozen(array)); + UPB_ASSERT(size <= array->UPB_ONLYBITS(size) || + arena); // Allow NULL arena when shrinking. + if (!upb_Array_Reserve(array, size, arena)) return false; + array->UPB_ONLYBITS(size) = size; + return true; } +// This function is intended for situations where elem_size is compile-time +// constant or a known expression of the form (1 << lg2), so that the expression +// i*elem_size does not result in an actual multiplication. +UPB_INLINE void UPB_PRIVATE(_upb_Array_Set)(struct upb_Array* array, size_t i, + const void* data, + size_t elem_size) { + UPB_ASSERT(!upb_Array_IsFrozen(array)); + UPB_ASSERT(i < array->UPB_ONLYBITS(size)); + UPB_ASSERT(elem_size == 1U << UPB_PRIVATE(_upb_Array_ElemSizeLg2)(array)); + char* arr_data = (char*)upb_Array_MutableDataPtr(array); + memcpy(arr_data + (i * elem_size), data, elem_size); +} + +UPB_API_INLINE size_t upb_Array_Size(const struct upb_Array* arr) { + return arr->UPB_ONLYBITS(size); +} + +// LINT.ThenChange(GoogleInternalName0) + #ifdef __cplusplus } /* extern "C" */ #endif +#undef _UPB_ARRAY_MASK_IMM +#undef _UPB_ARRAY_MASK_LG2 +#undef _UPB_ARRAY_MASK_ALL -#endif /* UPB_MINI_TABLE_INTERNAL_EXTENSION_H_ */ + +#endif /* UPB_MESSAGE_INTERNAL_ARRAY_H_ */ // Must be last. -typedef struct upb_MiniTableExtension upb_MiniTableExtension; +typedef struct upb_Array upb_Array; #ifdef __cplusplus extern "C" { #endif -UPB_API_INLINE upb_CType -upb_MiniTableExtension_CType(const upb_MiniTableExtension* e); +// Creates a new array on the given arena that holds elements of this type. +UPB_API upb_Array* upb_Array_New(upb_Arena* a, upb_CType type); -UPB_API_INLINE uint32_t -upb_MiniTableExtension_Number(const upb_MiniTableExtension* e); +// Returns the number of elements in the array. +UPB_API_INLINE size_t upb_Array_Size(const upb_Array* arr); -UPB_API_INLINE const upb_MiniTable* upb_MiniTableExtension_GetSubMessage( - const upb_MiniTableExtension* e); +// Returns the given element, which must be within the array's current size. +UPB_API upb_MessageValue upb_Array_Get(const upb_Array* arr, size_t i); + +// Returns a mutating pointer to the given element, which must be within the +// array's current size. +UPB_API upb_MutableMessageValue upb_Array_GetMutable(upb_Array* arr, size_t i); + +// Sets the given element, which must be within the array's current size. +UPB_API void upb_Array_Set(upb_Array* arr, size_t i, upb_MessageValue val); + +// Appends an element to the array. Returns false on allocation failure. +UPB_API bool upb_Array_Append(upb_Array* array, upb_MessageValue val, + upb_Arena* arena); + +// Moves elements within the array using memmove(). +// Like memmove(), the source and destination elements may be overlapping. +UPB_API void upb_Array_Move(upb_Array* array, size_t dst_idx, size_t src_idx, + size_t count); + +// Inserts one or more empty elements into the array. +// Existing elements are shifted right. +// The new elements have undefined state and must be set with `upb_Array_Set()`. +// REQUIRES: `i <= upb_Array_Size(arr)` +UPB_API bool upb_Array_Insert(upb_Array* array, size_t i, size_t count, + upb_Arena* arena); + +// Deletes one or more elements from the array. +// Existing elements are shifted left. +// REQUIRES: `i + count <= upb_Array_Size(arr)` +UPB_API void upb_Array_Delete(upb_Array* array, size_t i, size_t count); + +// Reserves |size| elements of storage for the array. +UPB_API_INLINE bool upb_Array_Reserve(struct upb_Array* array, size_t size, + upb_Arena* arena); + +// Changes the size of a vector. New elements are initialized to NULL/0. +// Returns false on allocation failure. +UPB_API bool upb_Array_Resize(upb_Array* array, size_t size, upb_Arena* arena); + +// Returns pointer to array data. +UPB_API_INLINE const void* upb_Array_DataPtr(const upb_Array* arr); + +// Returns mutable pointer to array data. +UPB_API_INLINE void* upb_Array_MutableDataPtr(upb_Array* arr); + +// Mark an array and all of its descendents as frozen/immutable. +// If the array elements are messages then |m| must point to the minitable for +// those messages. Otherwise |m| must be NULL. +UPB_API void upb_Array_Freeze(upb_Array* arr, const upb_MiniTable* m); + +// Returns whether an array has been frozen. +UPB_API_INLINE bool upb_Array_IsFrozen(const upb_Array* arr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* UPB_MESSAGE_ARRAY_H_ */ + +#ifndef UPB_MESSAGE_INTERNAL_ACCESSORS_H_ +#define UPB_MESSAGE_INTERNAL_ACCESSORS_H_ -UPB_API_INLINE void upb_MiniTableExtension_SetSubMessage( - upb_MiniTableExtension* e, const upb_MiniTable* m); +#include +#include +#include -#ifdef __cplusplus -} /* extern "C" */ -#endif +#ifndef UPB_BASE_INTERNAL_ENDIAN_H_ +#define UPB_BASE_INTERNAL_ENDIAN_H_ -#endif /* UPB_MINI_TABLE_EXTENSION_H_ */ +#include // Must be last. -// The internal representation of an extension is self-describing: it contains -// enough information that we can serialize it to binary format without needing -// to look it up in a upb_ExtensionRegistry. -// -// This representation allocates 16 bytes to data on 64-bit platforms. -// This is rather wasteful for scalars (in the extreme case of bool, -// it wastes 15 bytes). We accept this because we expect messages to be -// the most common extension type. -typedef struct { - const upb_MiniTableExtension* ext; - upb_MessageValue data; -} upb_Extension; - #ifdef __cplusplus extern "C" { #endif -// Adds the given extension data to the given message. -// |ext| is copied into the message instance. -// This logically replaces any previously-added extension with this number. -upb_Extension* UPB_PRIVATE(_upb_Message_GetOrCreateExtension)( - struct upb_Message* msg, const upb_MiniTableExtension* ext, - upb_Arena* arena); +UPB_INLINE bool upb_IsLittleEndian(void) { + const int x = 1; + return *(char*)&x == 1; +} -// Returns an array of extensions for this message. -// Note: the array is ordered in reverse relative to the order of creation. -const upb_Extension* UPB_PRIVATE(_upb_Message_Getexts)( - const struct upb_Message* msg, size_t* count); +UPB_INLINE uint32_t upb_BigEndian32(uint32_t val) { + if (upb_IsLittleEndian()) return val; -// Returns an extension for a message with a given mini table, -// or NULL if no extension exists with this mini table. -const upb_Extension* UPB_PRIVATE(_upb_Message_Getext)( - const struct upb_Message* msg, const upb_MiniTableExtension* ext); + return ((val & 0xff) << 24) | ((val & 0xff00) << 8) | + ((val & 0xff0000) >> 8) | ((val & 0xff000000) >> 24); +} + +UPB_INLINE uint64_t upb_BigEndian64(uint64_t val) { + if (upb_IsLittleEndian()) return val; + + const uint64_t hi = ((uint64_t)upb_BigEndian32((uint32_t)val)) << 32; + const uint64_t lo = upb_BigEndian32((uint32_t)(val >> 32)); + return hi | lo; +} #ifdef __cplusplus } /* extern "C" */ #endif -#endif /* UPB_MESSAGE_INTERNAL_EXTENSION_H_ */ +#endif /* UPB_BASE_INTERNAL_ENDIAN_H_ */ #ifndef UPB_MESSAGE_INTERNAL_MAP_H_ #define UPB_MESSAGE_INTERNAL_MAP_H_ @@ -3008,96 +3201,6 @@ struct upb_Map* _upb_Map_New(upb_Arena* a, size_t key_size, size_t value_size); #endif /* UPB_MESSAGE_INTERNAL_MAP_H_ */ -/* -** Our memory representation for parsing tables and messages themselves. -** Functions in this file are used by generated code and possibly reflection. -** -** The definitions in this file are internal to upb. -**/ - -#ifndef UPB_MESSAGE_INTERNAL_MESSAGE_H_ -#define UPB_MESSAGE_INTERNAL_MESSAGE_H_ - -#include -#include - - -// Must be last. - -#ifdef __cplusplus -extern "C" { -#endif - -extern const float kUpb_FltInfinity; -extern const double kUpb_Infinity; -extern const double kUpb_NaN; - -// Internal members of a upb_Message that track unknown fields and/or -// extensions. We can change this without breaking binary compatibility. - -typedef struct upb_Message_Internal { - // Total size of this structure, including the data that follows. - // Must be aligned to 8, which is alignof(upb_Extension) - uint32_t size; - - /* Offsets relative to the beginning of this structure. - * - * Unknown data grows forward from the beginning to unknown_end. - * Extension data grows backward from size to ext_begin. - * When the two meet, we're out of data and have to realloc. - * - * If we imagine that the final member of this struct is: - * char data[size - overhead]; // overhead = sizeof(upb_Message_Internal) - * - * Then we have: - * unknown data: data[0 .. (unknown_end - overhead)] - * extensions data: data[(ext_begin - overhead) .. (size - overhead)] */ - uint32_t unknown_end; - uint32_t ext_begin; - // Data follows, as if there were an array: - // char data[size - sizeof(upb_Message_Internal)]; -} upb_Message_Internal; - -#ifdef UPB_TRACING_ENABLED -UPB_API void upb_Message_LogNewMessage(const upb_MiniTable* m, - const upb_Arena* arena); -UPB_API void upb_Message_SetNewMessageTraceHandler( - void (*handler)(const upb_MiniTable*, const upb_Arena*)); -#endif // UPB_TRACING_ENABLED - -// Inline version upb_Message_New(), for internal use. -UPB_INLINE struct upb_Message* _upb_Message_New(const upb_MiniTable* m, - upb_Arena* a) { -#ifdef UPB_TRACING_ENABLED - upb_Message_LogNewMessage(m, a); -#endif // UPB_TRACING_ENABLED - - const int size = m->UPB_PRIVATE(size); - struct upb_Message* msg = (struct upb_Message*)upb_Arena_Malloc(a, size); - if (UPB_UNLIKELY(!msg)) return NULL; - memset(msg, 0, size); - return msg; -} - -// Discards the unknown fields for this message only. -void _upb_Message_DiscardUnknown_shallow(struct upb_Message* msg); - -// Adds unknown data (serialized protobuf data) to the given message. -// The data is copied into the message instance. -bool UPB_PRIVATE(_upb_Message_AddUnknown)(struct upb_Message* msg, - const char* data, size_t len, - upb_Arena* arena); - -bool UPB_PRIVATE(_upb_Message_Realloc)(struct upb_Message* msg, size_t need, - upb_Arena* arena); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - - -#endif /* UPB_MESSAGE_INTERNAL_MESSAGE_H_ */ - #ifndef UPB_MINI_TABLE_INTERNAL_TAGGED_PTR_H_ #define UPB_MINI_TABLE_INTERNAL_TAGGED_PTR_H_ @@ -3145,56 +3248,6 @@ UPB_INLINE struct upb_Message* UPB_PRIVATE( #endif /* UPB_MINI_TABLE_INTERNAL_TAGGED_PTR_H_ */ -#ifndef UPB_MESSAGE_INTERNAL_TYPES_H_ -#define UPB_MESSAGE_INTERNAL_TYPES_H_ - -#include - -// Must be last. - -#define UPB_OPAQUE(x) x##_opaque - -struct upb_Message { - union { - uintptr_t UPB_OPAQUE(internal); // tagged pointer, low bit == frozen - double d; // Forces same size for 32-bit/64-bit builds - }; -}; - -#ifdef __cplusplus -extern "C" { -#endif - -UPB_INLINE void UPB_PRIVATE(_upb_Message_ShallowFreeze)( - struct upb_Message* msg) { - msg->UPB_OPAQUE(internal) |= 1ULL; -} - -UPB_API_INLINE bool upb_Message_IsFrozen(const struct upb_Message* msg) { - return (msg->UPB_OPAQUE(internal) & 1ULL) != 0; -} - -UPB_INLINE struct upb_Message_Internal* UPB_PRIVATE(_upb_Message_GetInternal)( - const struct upb_Message* msg) { - const uintptr_t tmp = msg->UPB_OPAQUE(internal) & ~1ULL; - return (struct upb_Message_Internal*)tmp; -} - -UPB_INLINE void UPB_PRIVATE(_upb_Message_SetInternal)( - struct upb_Message* msg, struct upb_Message_Internal* internal) { - UPB_ASSERT(!upb_Message_IsFrozen(msg)); - msg->UPB_OPAQUE(internal) = (uintptr_t)internal; -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#undef UPB_OPAQUE - - -#endif /* UPB_MESSAGE_INTERNAL_TYPES_H_ */ - // Must be last. #if defined(__GNUC__) && !defined(__clang__) @@ -4201,57 +4254,6 @@ UPB_API_INLINE bool upb_Map_IsFrozen(const upb_Map* map); #endif /* UPB_MESSAGE_MAP_H_ */ -// Public APIs for message operations that do not depend on the schema. -// -// MiniTable-based accessors live in accessors.h. - -#ifndef UPB_MESSAGE_MESSAGE_H_ -#define UPB_MESSAGE_MESSAGE_H_ - -#include - - -// Must be last. - -typedef struct upb_Message upb_Message; - -#ifdef __cplusplus -extern "C" { -#endif - -// Creates a new message with the given mini_table on the given arena. -UPB_API upb_Message* upb_Message_New(const upb_MiniTable* m, upb_Arena* arena); - -// Returns a reference to the message's unknown data. -const char* upb_Message_GetUnknown(const upb_Message* msg, size_t* len); - -// Removes partial unknown data from message. -void upb_Message_DeleteUnknown(upb_Message* msg, const char* data, size_t len); - -// Returns the number of extensions present in this message. -size_t upb_Message_ExtensionCount(const upb_Message* msg); - -// Mark a message and all of its descendents as frozen/immutable. -UPB_API void upb_Message_Freeze(upb_Message* msg, const upb_MiniTable* m); - -// Returns whether a message has been frozen. -UPB_API_INLINE bool upb_Message_IsFrozen(const upb_Message* msg); - -#ifdef UPB_TRACING_ENABLED -UPB_API void upb_Message_LogNewMessage(const upb_MiniTable* m, - const upb_Arena* arena); - -UPB_API void upb_Message_SetNewMessageTraceHandler( - void (*handler)(const upb_MiniTable* m, const upb_Arena* arena)); -#endif // UPB_TRACING_ENABLED - -#ifdef __cplusplus -} /* extern "C" */ -#endif - - -#endif /* UPB_MESSAGE_MESSAGE_H_ */ - #ifndef UPB_MINI_TABLE_TAGGED_PTR_H_ #define UPB_MINI_TABLE_TAGGED_PTR_H_ @@ -13017,9 +13019,27 @@ extern "C" { enum { upb_JsonDecode_IgnoreUnknown = 1 }; -UPB_API bool upb_JsonDecode(const char* buf, size_t size, upb_Message* msg, - const upb_MessageDef* m, const upb_DefPool* symtab, - int options, upb_Arena* arena, upb_Status* status); +enum { + kUpb_JsonDecodeResult_Ok = 0, + kUpb_JsonDecodeResult_OkWithEmptyStringNumerics = 1, + kUpb_JsonDecodeResult_Error = 2, +}; + +UPB_API int upb_JsonDecodeDetectingNonconformance(const char* buf, size_t size, + upb_Message* msg, + const upb_MessageDef* m, + const upb_DefPool* symtab, + int options, upb_Arena* arena, + upb_Status* status); + +UPB_API_INLINE bool upb_JsonDecode(const char* buf, size_t size, + upb_Message* msg, const upb_MessageDef* m, + const upb_DefPool* symtab, int options, + upb_Arena* arena, upb_Status* status) { + return upb_JsonDecodeDetectingNonconformance(buf, size, msg, m, symtab, + options, arena, status) == + kUpb_JsonDecodeResult_Ok; +} #ifdef __cplusplus } /* extern "C" */ diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index d6204cb33e89..37f4dc70d011 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -1038,11 +1038,21 @@ static VALUE Message_decode_json(int argc, VALUE* argv, VALUE klass) { upb_Status_Clear(&status); const upb_DefPool* pool = upb_FileDef_Pool(upb_MessageDef_File(msg->msgdef)); - if (!upb_JsonDecode(RSTRING_PTR(data), RSTRING_LEN(data), - (upb_Message*)msg->msg, msg->msgdef, pool, options, - Arena_get(msg->arena), &status)) { - rb_raise(cParseError, "Error occurred during parsing: %s", - upb_Status_ErrorMessage(&status)); + + int result = upb_JsonDecodeDetectingNonconformance( + RSTRING_PTR(data), RSTRING_LEN(data), (upb_Message*)msg->msg, + msg->msgdef, pool, options, Arena_get(msg->arena), &status); + + switch (result) { + case kUpb_JsonDecodeResult_Ok: + break; + case kUpb_JsonDecodeResult_OkWithEmptyStringNumerics: + rb_warn("%s", upb_Status_ErrorMessage(&status)); + break; + case kUpb_JsonDecodeResult_Error: + rb_raise(cParseError, "Error occurred during parsing: %s", + upb_Status_ErrorMessage(&status)); + break; } return msg_rb; diff --git a/ruby/ext/google/protobuf_c/ruby-upb.c b/ruby/ext/google/protobuf_c/ruby-upb.c index e408728a9d5b..c7c4526b6f25 100644 --- a/ruby/ext/google/protobuf_c/ruby-upb.c +++ b/ruby/ext/google/protobuf_c/ruby-upb.c @@ -523,6 +523,7 @@ typedef struct { upb_Arena* arena; /* TODO: should we have a tmp arena for tmp data? */ const upb_DefPool* symtab; int depth; + int result; upb_Status* status; jmp_buf err; int line; @@ -1152,6 +1153,16 @@ static int64_t jsondec_strtoint64(jsondec* d, upb_StringView str) { return ret; } +static void jsondec_checkempty(jsondec* d, upb_StringView str, + const upb_FieldDef* f) { + if (str.size != 0) return; + d->result = kUpb_JsonDecodeResult_OkWithEmptyStringNumerics; + upb_Status_SetErrorFormat(d->status, + "Empty string is not a valid number (field: %s). " + "This will be an error in a future version.", + upb_FieldDef_FullName(f)); +} + /* Primitive value types ******************************************************/ /* Parse INT32 or INT64 value. */ @@ -1173,6 +1184,7 @@ static upb_MessageValue jsondec_int(jsondec* d, const upb_FieldDef* f) { } case JD_STRING: { upb_StringView str = jsondec_string(d); + jsondec_checkempty(d, str, f); val.int64_val = jsondec_strtoint64(d, str); break; } @@ -1210,6 +1222,7 @@ static upb_MessageValue jsondec_uint(jsondec* d, const upb_FieldDef* f) { } case JD_STRING: { upb_StringView str = jsondec_string(d); + jsondec_checkempty(d, str, f); val.uint64_val = jsondec_strtouint64(d, str); break; } @@ -1238,14 +1251,26 @@ static upb_MessageValue jsondec_double(jsondec* d, const upb_FieldDef* f) { break; case JD_STRING: str = jsondec_string(d); - if (jsondec_streql(str, "NaN")) { + if (str.size == 0) { + jsondec_checkempty(d, str, f); + val.double_val = 0.0; + } else if (jsondec_streql(str, "NaN")) { val.double_val = NAN; } else if (jsondec_streql(str, "Infinity")) { val.double_val = INFINITY; } else if (jsondec_streql(str, "-Infinity")) { val.double_val = -INFINITY; } else { - val.double_val = strtod(str.data, NULL); + char* end; + val.double_val = strtod(str.data, &end); + if (end != str.data + str.size) { + d->result = kUpb_JsonDecodeResult_OkWithEmptyStringNumerics; + upb_Status_SetErrorFormat( + d->status, + "Non-number characters in quoted number (field: %s). " + "This will be an error in a future version.", + upb_FieldDef_FullName(f)); + } } break; default: @@ -1987,10 +2012,10 @@ static void jsondec_wellknown(jsondec* d, upb_Message* msg, } } -static bool upb_JsonDecoder_Decode(jsondec* const d, upb_Message* const msg, - const upb_MessageDef* const m) { +static int upb_JsonDecoder_Decode(jsondec* const d, upb_Message* const msg, + const upb_MessageDef* const m) { UPB_ASSERT(!upb_Message_IsFrozen(msg)); - if (UPB_SETJMP(d->err)) return false; + if (UPB_SETJMP(d->err)) return kUpb_JsonDecodeResult_Error; jsondec_tomsg(d, msg, m); @@ -1999,16 +2024,19 @@ static bool upb_JsonDecoder_Decode(jsondec* const d, upb_Message* const msg, jsondec_consumews(d); if (d->ptr == d->end) { - return true; + return d->result; } else { jsondec_seterrmsg(d, "unexpected trailing characters"); - return false; + return kUpb_JsonDecodeResult_Error; } } -bool upb_JsonDecode(const char* buf, size_t size, upb_Message* msg, - const upb_MessageDef* m, const upb_DefPool* symtab, - int options, upb_Arena* arena, upb_Status* status) { +int upb_JsonDecodeDetectingNonconformance(const char* buf, size_t size, + upb_Message* msg, + const upb_MessageDef* m, + const upb_DefPool* symtab, + int options, upb_Arena* arena, + upb_Status* status) { UPB_ASSERT(!upb_Message_IsFrozen(msg)); jsondec d; @@ -2021,6 +2049,7 @@ bool upb_JsonDecode(const char* buf, size_t size, upb_Message* msg, d.status = status; d.options = options; d.depth = 64; + d.result = kUpb_JsonDecodeResult_Ok; d.line = 1; d.line_begin = d.ptr; d.debug_field = NULL; diff --git a/ruby/ext/google/protobuf_c/ruby-upb.h b/ruby/ext/google/protobuf_c/ruby-upb.h index 8aeee57ec549..3b6866adca05 100755 --- a/ruby/ext/google/protobuf_c/ruby-upb.h +++ b/ruby/ext/google/protobuf_c/ruby-upb.h @@ -1133,16 +1133,47 @@ UPB_FORCEINLINE bool upb_EpsCopyInputStream_TryParseDelimitedFast( #ifndef UPB_JSON_DECODE_H_ #define UPB_JSON_DECODE_H_ +#include -#ifndef UPB_REFLECTION_DEF_H_ -#define UPB_REFLECTION_DEF_H_ -// IWYU pragma: begin_exports +// Public APIs for message operations that do not depend on the schema. +// +// MiniTable-based accessors live in accessors.h. -// IWYU pragma: private, include "upb/reflection/def.h" +#ifndef UPB_MESSAGE_MESSAGE_H_ +#define UPB_MESSAGE_MESSAGE_H_ -#ifndef UPB_REFLECTION_DEF_POOL_H_ -#define UPB_REFLECTION_DEF_POOL_H_ +#include + + +/* +** Our memory representation for parsing tables and messages themselves. +** Functions in this file are used by generated code and possibly reflection. +** +** The definitions in this file are internal to upb. +**/ + +#ifndef UPB_MESSAGE_INTERNAL_MESSAGE_H_ +#define UPB_MESSAGE_INTERNAL_MESSAGE_H_ + +#include +#include + + +#ifndef UPB_MESSAGE_INTERNAL_EXTENSION_H_ +#define UPB_MESSAGE_INTERNAL_EXTENSION_H_ + +#include + + +// Users should include array.h or map.h instead. +// IWYU pragma: private, include "upb/message/array.h" + +#ifndef UPB_MESSAGE_VALUE_H_ +#define UPB_MESSAGE_VALUE_H_ + +#include +#include #ifndef UPB_BASE_STRING_VIEW_H_ #define UPB_BASE_STRING_VIEW_H_ @@ -1196,74 +1227,63 @@ UPB_INLINE bool upb_StringView_IsEqual(upb_StringView a, upb_StringView b) { #endif /* UPB_BASE_STRING_VIEW_H_ */ -// IWYU pragma: private, include "upb/reflection/def.h" - -// Declarations common to all public def types. - -#ifndef UPB_REFLECTION_COMMON_H_ -#define UPB_REFLECTION_COMMON_H_ - -#ifndef THIRD_PARTY_UPB_UPB_REFLECTION_DESCRIPTOR_BOOTSTRAP_H_ -#define THIRD_PARTY_UPB_UPB_REFLECTION_DESCRIPTOR_BOOTSTRAP_H_ - -// IWYU pragma: begin_exports - -#if defined(UPB_BOOTSTRAP_STAGE) && UPB_BOOTSTRAP_STAGE == 0 -// This header is checked in. -#elif UPB_BOOTSTRAP_STAGE == 1 -// This header is generated at build time by the bootstrapping process. -#else -// This is the normal header, generated by upb_c_proto_library(). -/* This file was generated by upb_generator from the input file: - * - * google/protobuf/descriptor.proto - * - * Do not edit -- your changes will be discarded when the file is - * regenerated. - * NO CHECKED-IN PROTOBUF GENCODE */ - -#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPB_H__UPB_H_ -#define GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPB_H__UPB_H_ +// Must be last. +#ifdef __cplusplus +extern "C" { +#endif -#ifndef UPB_GENERATED_CODE_SUPPORT_H_ -#define UPB_GENERATED_CODE_SUPPORT_H_ +typedef union { + bool bool_val; + float float_val; + double double_val; + int32_t int32_val; + int64_t int64_val; + uint32_t uint32_val; + uint64_t uint64_val; + const struct upb_Array* array_val; + const struct upb_Map* map_val; + const struct upb_Message* msg_val; + upb_StringView str_val; -// IWYU pragma: begin_exports + // EXPERIMENTAL: A tagged upb_Message*. Users must use this instead of + // msg_val if unlinked sub-messages may possibly be in use. See the + // documentation in kUpb_DecodeOption_ExperimentalAllowUnlinked for more + // information. + uintptr_t tagged_msg_val; // upb_TaggedMessagePtr +} upb_MessageValue; -#ifndef UPB_BASE_UPCAST_H_ -#define UPB_BASE_UPCAST_H_ +UPB_API_INLINE upb_MessageValue upb_MessageValue_Zero(void) { + upb_MessageValue zero; + memset(&zero, 0, sizeof(zero)); + return zero; +} -// Must be last. +typedef union { + struct upb_Array* array; + struct upb_Map* map; + struct upb_Message* msg; +} upb_MutableMessageValue; -// This macro provides a way to upcast message pointers in a way that is -// somewhat more bulletproof than blindly casting a pointer. Example: -// -// typedef struct { -// upb_Message UPB_PRIVATE(base); -// } pkg_FooMessage; -// -// void f(pkg_FooMessage* msg) { -// upb_Decode(UPB_UPCAST(msg), ...); -// } +UPB_API_INLINE upb_MutableMessageValue upb_MutableMessageValue_Zero(void) { + upb_MutableMessageValue zero; + memset(&zero, 0, sizeof(zero)); + return zero; +} -#define UPB_UPCAST(x) (&(x)->base##_dont_copy_me__upb_internal_use_only) +#ifdef __cplusplus +} /* extern "C" */ +#endif -#endif /* UPB_BASE_UPCAST_H_ */ +#endif /* UPB_MESSAGE_VALUE_H_ */ -#ifndef UPB_MESSAGE_ACCESSORS_H_ -#define UPB_MESSAGE_ACCESSORS_H_ +#ifndef UPB_MINI_TABLE_EXTENSION_H_ +#define UPB_MINI_TABLE_EXTENSION_H_ #include -#ifndef UPB_MESSAGE_ARRAY_H_ -#define UPB_MESSAGE_ARRAY_H_ - -#include - - #ifndef UPB_BASE_DESCRIPTOR_CONSTANTS_H_ #define UPB_BASE_DESCRIPTOR_CONSTANTS_H_ @@ -1367,290 +1387,94 @@ UPB_INLINE bool upb_FieldType_IsPackable(upb_FieldType field_type) { #endif /* UPB_BASE_DESCRIPTOR_CONSTANTS_H_ */ -#ifndef UPB_MESSAGE_INTERNAL_ARRAY_H_ -#define UPB_MESSAGE_INTERNAL_ARRAY_H_ +#ifndef UPB_MINI_TABLE_INTERNAL_EXTENSION_H_ +#define UPB_MINI_TABLE_INTERNAL_EXTENSION_H_ +#include #include -#include -// Must be last. +#ifndef UPB_MINI_TABLE_INTERNAL_FIELD_H_ +#define UPB_MINI_TABLE_INTERNAL_FIELD_H_ -#define _UPB_ARRAY_MASK_IMM 0x4 // Frozen/immutable bit. -#define _UPB_ARRAY_MASK_LG2 0x3 // Encoded elem size. -#define _UPB_ARRAY_MASK_ALL (_UPB_ARRAY_MASK_IMM | _UPB_ARRAY_MASK_LG2) +#include +#include + + +#ifndef UPB_MINI_TABLE_INTERNAL_SIZE_LOG2_H_ +#define UPB_MINI_TABLE_INTERNAL_SIZE_LOG2_H_ + +#include +#include + + +// Must be last. #ifdef __cplusplus extern "C" { #endif -// LINT.IfChange(upb_Array) +// Return the log2 of the storage size in bytes for a upb_CType +UPB_INLINE int UPB_PRIVATE(_upb_CType_SizeLg2)(upb_CType c_type) { + static const int8_t size[] = { + 0, // kUpb_CType_Bool + 2, // kUpb_CType_Float + 2, // kUpb_CType_Int32 + 2, // kUpb_CType_UInt32 + 2, // kUpb_CType_Enum + UPB_SIZE(2, 3), // kUpb_CType_Message + 3, // kUpb_CType_Double + 3, // kUpb_CType_Int64 + 3, // kUpb_CType_UInt64 + UPB_SIZE(3, 4), // kUpb_CType_String + UPB_SIZE(3, 4), // kUpb_CType_Bytes + }; -// Our internal representation for repeated fields. -struct upb_Array { - // This is a tagged pointer. Bits #0 and #1 encode the elem size as follows: - // 0 maps to elem size 1 - // 1 maps to elem size 4 - // 2 maps to elem size 8 - // 3 maps to elem size 16 - // - // Bit #2 contains the frozen/immutable flag. - uintptr_t UPB_ONLYBITS(data); + // -1 here because the enum is one-based but the table is zero-based. + return size[c_type - 1]; +} - size_t UPB_ONLYBITS(size); // The number of elements in the array. - size_t UPB_PRIVATE(capacity); // Allocated storage. Measured in elements. -}; +// Return the log2 of the storage size in bytes for a upb_FieldType +UPB_INLINE int UPB_PRIVATE(_upb_FieldType_SizeLg2)(upb_FieldType field_type) { + static const int8_t size[] = { + 3, // kUpb_FieldType_Double + 2, // kUpb_FieldType_Float + 3, // kUpb_FieldType_Int64 + 3, // kUpb_FieldType_UInt64 + 2, // kUpb_FieldType_Int32 + 3, // kUpb_FieldType_Fixed64 + 2, // kUpb_FieldType_Fixed32 + 0, // kUpb_FieldType_Bool + UPB_SIZE(3, 4), // kUpb_FieldType_String + UPB_SIZE(2, 3), // kUpb_FieldType_Group + UPB_SIZE(2, 3), // kUpb_FieldType_Message + UPB_SIZE(3, 4), // kUpb_FieldType_Bytes + 2, // kUpb_FieldType_UInt32 + 2, // kUpb_FieldType_Enum + 2, // kUpb_FieldType_SFixed32 + 3, // kUpb_FieldType_SFixed64 + 2, // kUpb_FieldType_SInt32 + 3, // kUpb_FieldType_SInt64 + }; -UPB_INLINE void UPB_PRIVATE(_upb_Array_ShallowFreeze)(struct upb_Array* arr) { - arr->UPB_ONLYBITS(data) |= _UPB_ARRAY_MASK_IMM; + // -1 here because the enum is one-based but the table is zero-based. + return size[field_type - 1]; } -UPB_API_INLINE bool upb_Array_IsFrozen(const struct upb_Array* arr) { - return (arr->UPB_ONLYBITS(data) & _UPB_ARRAY_MASK_IMM) != 0; -} +#ifdef __cplusplus +} /* extern "C" */ +#endif -UPB_INLINE void UPB_PRIVATE(_upb_Array_SetTaggedPtr)(struct upb_Array* array, - void* data, size_t lg2) { - UPB_ASSERT(lg2 != 1); - UPB_ASSERT(lg2 <= 4); - const size_t bits = lg2 - (lg2 != 0); - array->UPB_ONLYBITS(data) = (uintptr_t)data | bits; -} -UPB_INLINE size_t -UPB_PRIVATE(_upb_Array_ElemSizeLg2)(const struct upb_Array* array) { - const size_t bits = array->UPB_ONLYBITS(data) & _UPB_ARRAY_MASK_LG2; - const size_t lg2 = bits + (bits != 0); - return lg2; -} +#endif /* UPB_MINI_TABLE_INTERNAL_SIZE_LOG2_H_ */ -UPB_API_INLINE const void* upb_Array_DataPtr(const struct upb_Array* array) { - UPB_PRIVATE(_upb_Array_ElemSizeLg2)(array); // Check assertions. - return (void*)(array->UPB_ONLYBITS(data) & ~(uintptr_t)_UPB_ARRAY_MASK_ALL); -} +// Must be last. -UPB_API_INLINE void* upb_Array_MutableDataPtr(struct upb_Array* array) { - return (void*)upb_Array_DataPtr(array); -} - -UPB_INLINE struct upb_Array* UPB_PRIVATE(_upb_Array_New)(upb_Arena* arena, - size_t init_capacity, - int elem_size_lg2) { - UPB_ASSERT(elem_size_lg2 != 1); - UPB_ASSERT(elem_size_lg2 <= 4); - const size_t array_size = - UPB_ALIGN_UP(sizeof(struct upb_Array), UPB_MALLOC_ALIGN); - const size_t bytes = array_size + (init_capacity << elem_size_lg2); - struct upb_Array* array = (struct upb_Array*)upb_Arena_Malloc(arena, bytes); - if (!array) return NULL; - UPB_PRIVATE(_upb_Array_SetTaggedPtr) - (array, UPB_PTR_AT(array, array_size, void), elem_size_lg2); - array->UPB_ONLYBITS(size) = 0; - array->UPB_PRIVATE(capacity) = init_capacity; - return array; -} - -// Resizes the capacity of the array to be at least min_size. -bool UPB_PRIVATE(_upb_Array_Realloc)(struct upb_Array* array, size_t min_size, - upb_Arena* arena); - -UPB_API_INLINE bool upb_Array_Reserve(struct upb_Array* array, size_t size, - upb_Arena* arena) { - UPB_ASSERT(!upb_Array_IsFrozen(array)); - if (array->UPB_PRIVATE(capacity) < size) - return UPB_PRIVATE(_upb_Array_Realloc)(array, size, arena); - return true; -} - -// Resize without initializing new elements. -UPB_INLINE bool UPB_PRIVATE(_upb_Array_ResizeUninitialized)( - struct upb_Array* array, size_t size, upb_Arena* arena) { - UPB_ASSERT(!upb_Array_IsFrozen(array)); - UPB_ASSERT(size <= array->UPB_ONLYBITS(size) || - arena); // Allow NULL arena when shrinking. - if (!upb_Array_Reserve(array, size, arena)) return false; - array->UPB_ONLYBITS(size) = size; - return true; -} - -// This function is intended for situations where elem_size is compile-time -// constant or a known expression of the form (1 << lg2), so that the expression -// i*elem_size does not result in an actual multiplication. -UPB_INLINE void UPB_PRIVATE(_upb_Array_Set)(struct upb_Array* array, size_t i, - const void* data, - size_t elem_size) { - UPB_ASSERT(!upb_Array_IsFrozen(array)); - UPB_ASSERT(i < array->UPB_ONLYBITS(size)); - UPB_ASSERT(elem_size == 1U << UPB_PRIVATE(_upb_Array_ElemSizeLg2)(array)); - char* arr_data = (char*)upb_Array_MutableDataPtr(array); - memcpy(arr_data + (i * elem_size), data, elem_size); -} - -UPB_API_INLINE size_t upb_Array_Size(const struct upb_Array* arr) { - return arr->UPB_ONLYBITS(size); -} - -// LINT.ThenChange(GoogleInternalName0) - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#undef _UPB_ARRAY_MASK_IMM -#undef _UPB_ARRAY_MASK_LG2 -#undef _UPB_ARRAY_MASK_ALL - - -#endif /* UPB_MESSAGE_INTERNAL_ARRAY_H_ */ - -// Users should include array.h or map.h instead. -// IWYU pragma: private, include "upb/message/array.h" - -#ifndef UPB_MESSAGE_VALUE_H_ -#define UPB_MESSAGE_VALUE_H_ - -#include -#include - - -// Must be last. - -#ifdef __cplusplus -extern "C" { -#endif - -typedef union { - bool bool_val; - float float_val; - double double_val; - int32_t int32_val; - int64_t int64_val; - uint32_t uint32_val; - uint64_t uint64_val; - const struct upb_Array* array_val; - const struct upb_Map* map_val; - const struct upb_Message* msg_val; - upb_StringView str_val; - - // EXPERIMENTAL: A tagged upb_Message*. Users must use this instead of - // msg_val if unlinked sub-messages may possibly be in use. See the - // documentation in kUpb_DecodeOption_ExperimentalAllowUnlinked for more - // information. - uintptr_t tagged_msg_val; // upb_TaggedMessagePtr -} upb_MessageValue; - -UPB_API_INLINE upb_MessageValue upb_MessageValue_Zero(void) { - upb_MessageValue zero; - memset(&zero, 0, sizeof(zero)); - return zero; -} - -typedef union { - struct upb_Array* array; - struct upb_Map* map; - struct upb_Message* msg; -} upb_MutableMessageValue; - -UPB_API_INLINE upb_MutableMessageValue upb_MutableMessageValue_Zero(void) { - upb_MutableMessageValue zero; - memset(&zero, 0, sizeof(zero)); - return zero; -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif - - -#endif /* UPB_MESSAGE_VALUE_H_ */ - -#ifndef UPB_MINI_TABLE_FIELD_H_ -#define UPB_MINI_TABLE_FIELD_H_ - -#include - - -#ifndef UPB_MINI_TABLE_INTERNAL_FIELD_H_ -#define UPB_MINI_TABLE_INTERNAL_FIELD_H_ - -#include -#include - - -#ifndef UPB_MINI_TABLE_INTERNAL_SIZE_LOG2_H_ -#define UPB_MINI_TABLE_INTERNAL_SIZE_LOG2_H_ - -#include -#include - - -// Must be last. - -#ifdef __cplusplus -extern "C" { -#endif - -// Return the log2 of the storage size in bytes for a upb_CType -UPB_INLINE int UPB_PRIVATE(_upb_CType_SizeLg2)(upb_CType c_type) { - static const int8_t size[] = { - 0, // kUpb_CType_Bool - 2, // kUpb_CType_Float - 2, // kUpb_CType_Int32 - 2, // kUpb_CType_UInt32 - 2, // kUpb_CType_Enum - UPB_SIZE(2, 3), // kUpb_CType_Message - 3, // kUpb_CType_Double - 3, // kUpb_CType_Int64 - 3, // kUpb_CType_UInt64 - UPB_SIZE(3, 4), // kUpb_CType_String - UPB_SIZE(3, 4), // kUpb_CType_Bytes - }; - - // -1 here because the enum is one-based but the table is zero-based. - return size[c_type - 1]; -} - -// Return the log2 of the storage size in bytes for a upb_FieldType -UPB_INLINE int UPB_PRIVATE(_upb_FieldType_SizeLg2)(upb_FieldType field_type) { - static const int8_t size[] = { - 3, // kUpb_FieldType_Double - 2, // kUpb_FieldType_Float - 3, // kUpb_FieldType_Int64 - 3, // kUpb_FieldType_UInt64 - 2, // kUpb_FieldType_Int32 - 3, // kUpb_FieldType_Fixed64 - 2, // kUpb_FieldType_Fixed32 - 0, // kUpb_FieldType_Bool - UPB_SIZE(3, 4), // kUpb_FieldType_String - UPB_SIZE(2, 3), // kUpb_FieldType_Group - UPB_SIZE(2, 3), // kUpb_FieldType_Message - UPB_SIZE(3, 4), // kUpb_FieldType_Bytes - 2, // kUpb_FieldType_UInt32 - 2, // kUpb_FieldType_Enum - 2, // kUpb_FieldType_SFixed32 - 3, // kUpb_FieldType_SFixed64 - 2, // kUpb_FieldType_SInt32 - 3, // kUpb_FieldType_SInt64 - }; - - // -1 here because the enum is one-based but the table is zero-based. - return size[field_type - 1]; -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif - - -#endif /* UPB_MINI_TABLE_INTERNAL_SIZE_LOG2_H_ */ - -// Must be last. - -// LINT.IfChange(struct_definition) -struct upb_MiniTableField { - uint32_t UPB_ONLYBITS(number); - uint16_t UPB_ONLYBITS(offset); - int16_t presence; // If >0, hasbit_index. If <0, ~oneof_index +// LINT.IfChange(struct_definition) +struct upb_MiniTableField { + uint32_t UPB_ONLYBITS(number); + uint16_t UPB_ONLYBITS(offset); + int16_t presence; // If >0, hasbit_index. If <0, ~oneof_index // Indexes into `upb_MiniTable.subs` // Will be set to `kUpb_NoSub` if `descriptortype` != MESSAGE/GROUP/ENUM @@ -1850,47 +1674,104 @@ UPB_INLINE size_t UPB_PRIVATE(_upb_MiniTableField_ElemSizeLg2)( #endif /* UPB_MINI_TABLE_INTERNAL_FIELD_H_ */ +#ifndef UPB_MINI_TABLE_INTERNAL_SUB_H_ +#define UPB_MINI_TABLE_INTERNAL_SUB_H_ + // Must be last. -typedef struct upb_MiniTableField upb_MiniTableField; +typedef union { + const struct upb_MiniTable* const* UPB_PRIVATE(submsg); + const struct upb_MiniTableEnum* UPB_PRIVATE(subenum); +} upb_MiniTableSubInternal; + +union upb_MiniTableSub { + const struct upb_MiniTable* UPB_PRIVATE(submsg); + const struct upb_MiniTableEnum* UPB_PRIVATE(subenum); +}; #ifdef __cplusplus extern "C" { #endif -UPB_API_INLINE upb_CType upb_MiniTableField_CType(const upb_MiniTableField* f); +UPB_API_INLINE union upb_MiniTableSub upb_MiniTableSub_FromEnum( + const struct upb_MiniTableEnum* subenum) { + union upb_MiniTableSub out; + out.UPB_PRIVATE(subenum) = subenum; + return out; +} -UPB_API_INLINE bool upb_MiniTableField_HasPresence(const upb_MiniTableField* f); +UPB_API_INLINE union upb_MiniTableSub upb_MiniTableSub_FromMessage( + const struct upb_MiniTable* submsg) { + union upb_MiniTableSub out; + out.UPB_PRIVATE(submsg) = submsg; + return out; +} -UPB_API_INLINE bool upb_MiniTableField_IsArray(const upb_MiniTableField* f); +UPB_API_INLINE const struct upb_MiniTableEnum* upb_MiniTableSub_Enum( + const union upb_MiniTableSub sub) { + return sub.UPB_PRIVATE(subenum); +} -UPB_API_INLINE bool upb_MiniTableField_IsClosedEnum( - const upb_MiniTableField* f); +UPB_API_INLINE const struct upb_MiniTable* upb_MiniTableSub_Message( + const union upb_MiniTableSub sub) { + return sub.UPB_PRIVATE(submsg); +} -UPB_API_INLINE bool upb_MiniTableField_IsExtension(const upb_MiniTableField* f); +#ifdef __cplusplus +} /* extern "C" */ +#endif -UPB_API_INLINE bool upb_MiniTableField_IsInOneof(const upb_MiniTableField* f); -UPB_API_INLINE bool upb_MiniTableField_IsMap(const upb_MiniTableField* f); +#endif /* UPB_MINI_TABLE_INTERNAL_SUB_H_ */ -UPB_API_INLINE bool upb_MiniTableField_IsPacked(const upb_MiniTableField* f); +// Must be last. -UPB_API_INLINE bool upb_MiniTableField_IsScalar(const upb_MiniTableField* f); +struct upb_MiniTableExtension { + // Do not move this field. We need to be able to alias pointers. + struct upb_MiniTableField UPB_PRIVATE(field); -UPB_API_INLINE bool upb_MiniTableField_IsSubMessage( - const upb_MiniTableField* f); + const struct upb_MiniTable* UPB_PRIVATE(extendee); + union upb_MiniTableSub UPB_PRIVATE(sub); // NULL unless submsg or proto2 enum +}; -UPB_API_INLINE uint32_t upb_MiniTableField_Number(const upb_MiniTableField* f); +#ifdef __cplusplus +extern "C" { +#endif -UPB_API_INLINE upb_FieldType -upb_MiniTableField_Type(const upb_MiniTableField* f); +UPB_API_INLINE upb_CType +upb_MiniTableExtension_CType(const struct upb_MiniTableExtension* e) { + return upb_MiniTableField_CType(&e->UPB_PRIVATE(field)); +} + +UPB_API_INLINE uint32_t +upb_MiniTableExtension_Number(const struct upb_MiniTableExtension* e) { + return e->UPB_PRIVATE(field).UPB_ONLYBITS(number); +} + +UPB_API_INLINE const struct upb_MiniTable* upb_MiniTableExtension_GetSubMessage( + const struct upb_MiniTableExtension* e) { + if (upb_MiniTableExtension_CType(e) != kUpb_CType_Message) { + return NULL; + } + return upb_MiniTableSub_Message(e->UPB_PRIVATE(sub)); +} + +UPB_API_INLINE void upb_MiniTableExtension_SetSubMessage( + struct upb_MiniTableExtension* e, const struct upb_MiniTable* m) { + e->UPB_PRIVATE(sub).UPB_PRIVATE(submsg) = m; +} + +UPB_INLINE upb_FieldRep UPB_PRIVATE(_upb_MiniTableExtension_GetRep)( + const struct upb_MiniTableExtension* e) { + return UPB_PRIVATE(_upb_MiniTableField_GetRep)(&e->UPB_PRIVATE(field)); +} #ifdef __cplusplus } /* extern "C" */ #endif -#endif /* UPB_MINI_TABLE_FIELD_H_ */ +#endif /* UPB_MINI_TABLE_INTERNAL_EXTENSION_H_ */ #ifndef UPB_MINI_TABLE_MESSAGE_H_ #define UPB_MINI_TABLE_MESSAGE_H_ @@ -1970,62 +1851,60 @@ UPB_API_INLINE bool upb_MiniTableEnum_CheckValue(const upb_MiniTableEnum* e, #endif /* UPB_MINI_TABLE_ENUM_H_ */ -#ifndef UPB_MINI_TABLE_INTERNAL_MESSAGE_H_ -#define UPB_MINI_TABLE_INTERNAL_MESSAGE_H_ +#ifndef UPB_MINI_TABLE_FIELD_H_ +#define UPB_MINI_TABLE_FIELD_H_ -#include #include -#ifndef UPB_MINI_TABLE_INTERNAL_SUB_H_ -#define UPB_MINI_TABLE_INTERNAL_SUB_H_ - // Must be last. -typedef union { - const struct upb_MiniTable* const* UPB_PRIVATE(submsg); - const struct upb_MiniTableEnum* UPB_PRIVATE(subenum); -} upb_MiniTableSubInternal; - -union upb_MiniTableSub { - const struct upb_MiniTable* UPB_PRIVATE(submsg); - const struct upb_MiniTableEnum* UPB_PRIVATE(subenum); -}; +typedef struct upb_MiniTableField upb_MiniTableField; #ifdef __cplusplus extern "C" { #endif -UPB_API_INLINE union upb_MiniTableSub upb_MiniTableSub_FromEnum( - const struct upb_MiniTableEnum* subenum) { - union upb_MiniTableSub out; - out.UPB_PRIVATE(subenum) = subenum; - return out; -} +UPB_API_INLINE upb_CType upb_MiniTableField_CType(const upb_MiniTableField* f); -UPB_API_INLINE union upb_MiniTableSub upb_MiniTableSub_FromMessage( - const struct upb_MiniTable* submsg) { - union upb_MiniTableSub out; - out.UPB_PRIVATE(submsg) = submsg; - return out; -} +UPB_API_INLINE bool upb_MiniTableField_HasPresence(const upb_MiniTableField* f); -UPB_API_INLINE const struct upb_MiniTableEnum* upb_MiniTableSub_Enum( - const union upb_MiniTableSub sub) { - return sub.UPB_PRIVATE(subenum); -} +UPB_API_INLINE bool upb_MiniTableField_IsArray(const upb_MiniTableField* f); -UPB_API_INLINE const struct upb_MiniTable* upb_MiniTableSub_Message( - const union upb_MiniTableSub sub) { - return sub.UPB_PRIVATE(submsg); -} +UPB_API_INLINE bool upb_MiniTableField_IsClosedEnum( + const upb_MiniTableField* f); + +UPB_API_INLINE bool upb_MiniTableField_IsExtension(const upb_MiniTableField* f); + +UPB_API_INLINE bool upb_MiniTableField_IsInOneof(const upb_MiniTableField* f); + +UPB_API_INLINE bool upb_MiniTableField_IsMap(const upb_MiniTableField* f); + +UPB_API_INLINE bool upb_MiniTableField_IsPacked(const upb_MiniTableField* f); + +UPB_API_INLINE bool upb_MiniTableField_IsScalar(const upb_MiniTableField* f); + +UPB_API_INLINE bool upb_MiniTableField_IsSubMessage( + const upb_MiniTableField* f); + +UPB_API_INLINE uint32_t upb_MiniTableField_Number(const upb_MiniTableField* f); + +UPB_API_INLINE upb_FieldType +upb_MiniTableField_Type(const upb_MiniTableField* f); #ifdef __cplusplus } /* extern "C" */ #endif -#endif /* UPB_MINI_TABLE_INTERNAL_SUB_H_ */ +#endif /* UPB_MINI_TABLE_FIELD_H_ */ + +#ifndef UPB_MINI_TABLE_INTERNAL_MESSAGE_H_ +#define UPB_MINI_TABLE_INTERNAL_MESSAGE_H_ + +#include +#include + // Must be last. @@ -2294,90 +2173,73 @@ bool upb_MiniTable_NextOneofField(const upb_MiniTable* m, // Must be last. -typedef struct upb_Array upb_Array; +typedef struct upb_MiniTableExtension upb_MiniTableExtension; #ifdef __cplusplus extern "C" { #endif -// Creates a new array on the given arena that holds elements of this type. -UPB_API upb_Array* upb_Array_New(upb_Arena* a, upb_CType type); - -// Returns the number of elements in the array. -UPB_API_INLINE size_t upb_Array_Size(const upb_Array* arr); - -// Returns the given element, which must be within the array's current size. -UPB_API upb_MessageValue upb_Array_Get(const upb_Array* arr, size_t i); +UPB_API_INLINE upb_CType +upb_MiniTableExtension_CType(const upb_MiniTableExtension* e); -// Returns a mutating pointer to the given element, which must be within the -// array's current size. -UPB_API upb_MutableMessageValue upb_Array_GetMutable(upb_Array* arr, size_t i); +UPB_API_INLINE uint32_t +upb_MiniTableExtension_Number(const upb_MiniTableExtension* e); -// Sets the given element, which must be within the array's current size. -UPB_API void upb_Array_Set(upb_Array* arr, size_t i, upb_MessageValue val); +UPB_API_INLINE const upb_MiniTable* upb_MiniTableExtension_GetSubMessage( + const upb_MiniTableExtension* e); -// Appends an element to the array. Returns false on allocation failure. -UPB_API bool upb_Array_Append(upb_Array* array, upb_MessageValue val, - upb_Arena* arena); +UPB_API_INLINE void upb_MiniTableExtension_SetSubMessage( + upb_MiniTableExtension* e, const upb_MiniTable* m); -// Moves elements within the array using memmove(). -// Like memmove(), the source and destination elements may be overlapping. -UPB_API void upb_Array_Move(upb_Array* array, size_t dst_idx, size_t src_idx, - size_t count); +#ifdef __cplusplus +} /* extern "C" */ +#endif -// Inserts one or more empty elements into the array. -// Existing elements are shifted right. -// The new elements have undefined state and must be set with `upb_Array_Set()`. -// REQUIRES: `i <= upb_Array_Size(arr)` -UPB_API bool upb_Array_Insert(upb_Array* array, size_t i, size_t count, - upb_Arena* arena); -// Deletes one or more elements from the array. -// Existing elements are shifted left. -// REQUIRES: `i + count <= upb_Array_Size(arr)` -UPB_API void upb_Array_Delete(upb_Array* array, size_t i, size_t count); +#endif /* UPB_MINI_TABLE_EXTENSION_H_ */ -// Reserves |size| elements of storage for the array. -UPB_API_INLINE bool upb_Array_Reserve(struct upb_Array* array, size_t size, - upb_Arena* arena); +// Must be last. -// Changes the size of a vector. New elements are initialized to NULL/0. -// Returns false on allocation failure. -UPB_API bool upb_Array_Resize(upb_Array* array, size_t size, upb_Arena* arena); +// The internal representation of an extension is self-describing: it contains +// enough information that we can serialize it to binary format without needing +// to look it up in a upb_ExtensionRegistry. +// +// This representation allocates 16 bytes to data on 64-bit platforms. +// This is rather wasteful for scalars (in the extreme case of bool, +// it wastes 15 bytes). We accept this because we expect messages to be +// the most common extension type. +typedef struct { + const upb_MiniTableExtension* ext; + upb_MessageValue data; +} upb_Extension; -// Returns pointer to array data. -UPB_API_INLINE const void* upb_Array_DataPtr(const upb_Array* arr); +#ifdef __cplusplus +extern "C" { +#endif -// Returns mutable pointer to array data. -UPB_API_INLINE void* upb_Array_MutableDataPtr(upb_Array* arr); +// Adds the given extension data to the given message. +// |ext| is copied into the message instance. +// This logically replaces any previously-added extension with this number. +upb_Extension* UPB_PRIVATE(_upb_Message_GetOrCreateExtension)( + struct upb_Message* msg, const upb_MiniTableExtension* ext, + upb_Arena* arena); -// Mark an array and all of its descendents as frozen/immutable. -// If the array elements are messages then |m| must point to the minitable for -// those messages. Otherwise |m| must be NULL. -UPB_API void upb_Array_Freeze(upb_Array* arr, const upb_MiniTable* m); +// Returns an array of extensions for this message. +// Note: the array is ordered in reverse relative to the order of creation. +const upb_Extension* UPB_PRIVATE(_upb_Message_Getexts)( + const struct upb_Message* msg, size_t* count); -// Returns whether an array has been frozen. -UPB_API_INLINE bool upb_Array_IsFrozen(const upb_Array* arr); +// Returns an extension for a message with a given mini table, +// or NULL if no extension exists with this mini table. +const upb_Extension* UPB_PRIVATE(_upb_Message_Getext)( + const struct upb_Message* msg, const upb_MiniTableExtension* ext); #ifdef __cplusplus } /* extern "C" */ #endif -#endif /* UPB_MESSAGE_ARRAY_H_ */ - -#ifndef UPB_MESSAGE_INTERNAL_ACCESSORS_H_ -#define UPB_MESSAGE_INTERNAL_ACCESSORS_H_ - -#include -#include -#include - - -#ifndef UPB_BASE_INTERNAL_ENDIAN_H_ -#define UPB_BASE_INTERNAL_ENDIAN_H_ - -#include +#endif /* UPB_MESSAGE_INTERNAL_EXTENSION_H_ */ // Must be last. @@ -2385,170 +2247,501 @@ UPB_API_INLINE bool upb_Array_IsFrozen(const upb_Array* arr); extern "C" { #endif -UPB_INLINE bool upb_IsLittleEndian(void) { - const int x = 1; - return *(char*)&x == 1; -} - -UPB_INLINE uint32_t upb_BigEndian32(uint32_t val) { - if (upb_IsLittleEndian()) return val; - - return ((val & 0xff) << 24) | ((val & 0xff00) << 8) | - ((val & 0xff0000) >> 8) | ((val & 0xff000000) >> 24); -} +extern const float kUpb_FltInfinity; +extern const double kUpb_Infinity; +extern const double kUpb_NaN; -UPB_INLINE uint64_t upb_BigEndian64(uint64_t val) { - if (upb_IsLittleEndian()) return val; +// Internal members of a upb_Message that track unknown fields and/or +// extensions. We can change this without breaking binary compatibility. - const uint64_t hi = ((uint64_t)upb_BigEndian32((uint32_t)val)) << 32; - const uint64_t lo = upb_BigEndian32((uint32_t)(val >> 32)); - return hi | lo; -} +typedef struct upb_Message_Internal { + // Total size of this structure, including the data that follows. + // Must be aligned to 8, which is alignof(upb_Extension) + uint32_t size; -#ifdef __cplusplus -} /* extern "C" */ + /* Offsets relative to the beginning of this structure. + * + * Unknown data grows forward from the beginning to unknown_end. + * Extension data grows backward from size to ext_begin. + * When the two meet, we're out of data and have to realloc. + * + * If we imagine that the final member of this struct is: + * char data[size - overhead]; // overhead = sizeof(upb_Message_Internal) + * + * Then we have: + * unknown data: data[0 .. (unknown_end - overhead)] + * extensions data: data[(ext_begin - overhead) .. (size - overhead)] */ + uint32_t unknown_end; + uint32_t ext_begin; + // Data follows, as if there were an array: + // char data[size - sizeof(upb_Message_Internal)]; +} upb_Message_Internal; + +#ifdef UPB_TRACING_ENABLED +UPB_API void upb_Message_LogNewMessage(const upb_MiniTable* m, + const upb_Arena* arena); +UPB_API void upb_Message_SetNewMessageTraceHandler( + void (*handler)(const upb_MiniTable*, const upb_Arena*)); +#endif // UPB_TRACING_ENABLED + +// Inline version upb_Message_New(), for internal use. +UPB_INLINE struct upb_Message* _upb_Message_New(const upb_MiniTable* m, + upb_Arena* a) { +#ifdef UPB_TRACING_ENABLED + upb_Message_LogNewMessage(m, a); +#endif // UPB_TRACING_ENABLED + + const int size = m->UPB_PRIVATE(size); + struct upb_Message* msg = (struct upb_Message*)upb_Arena_Malloc(a, size); + if (UPB_UNLIKELY(!msg)) return NULL; + memset(msg, 0, size); + return msg; +} + +// Discards the unknown fields for this message only. +void _upb_Message_DiscardUnknown_shallow(struct upb_Message* msg); + +// Adds unknown data (serialized protobuf data) to the given message. +// The data is copied into the message instance. +bool UPB_PRIVATE(_upb_Message_AddUnknown)(struct upb_Message* msg, + const char* data, size_t len, + upb_Arena* arena); + +bool UPB_PRIVATE(_upb_Message_Realloc)(struct upb_Message* msg, size_t need, + upb_Arena* arena); + +#ifdef __cplusplus +} /* extern "C" */ #endif -#endif /* UPB_BASE_INTERNAL_ENDIAN_H_ */ +#endif /* UPB_MESSAGE_INTERNAL_MESSAGE_H_ */ -#ifndef UPB_MESSAGE_INTERNAL_EXTENSION_H_ -#define UPB_MESSAGE_INTERNAL_EXTENSION_H_ +#ifndef UPB_MESSAGE_INTERNAL_TYPES_H_ +#define UPB_MESSAGE_INTERNAL_TYPES_H_ -#include +#include + +// Must be last. +#define UPB_OPAQUE(x) x##_opaque -#ifndef UPB_MINI_TABLE_EXTENSION_H_ -#define UPB_MINI_TABLE_EXTENSION_H_ +struct upb_Message { + union { + uintptr_t UPB_OPAQUE(internal); // tagged pointer, low bit == frozen + double d; // Forces same size for 32-bit/64-bit builds + }; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +UPB_INLINE void UPB_PRIVATE(_upb_Message_ShallowFreeze)( + struct upb_Message* msg) { + msg->UPB_OPAQUE(internal) |= 1ULL; +} + +UPB_API_INLINE bool upb_Message_IsFrozen(const struct upb_Message* msg) { + return (msg->UPB_OPAQUE(internal) & 1ULL) != 0; +} + +UPB_INLINE struct upb_Message_Internal* UPB_PRIVATE(_upb_Message_GetInternal)( + const struct upb_Message* msg) { + const uintptr_t tmp = msg->UPB_OPAQUE(internal) & ~1ULL; + return (struct upb_Message_Internal*)tmp; +} + +UPB_INLINE void UPB_PRIVATE(_upb_Message_SetInternal)( + struct upb_Message* msg, struct upb_Message_Internal* internal) { + UPB_ASSERT(!upb_Message_IsFrozen(msg)); + msg->UPB_OPAQUE(internal) = (uintptr_t)internal; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#undef UPB_OPAQUE + + +#endif /* UPB_MESSAGE_INTERNAL_TYPES_H_ */ + +// Must be last. + +typedef struct upb_Message upb_Message; + +#ifdef __cplusplus +extern "C" { +#endif + +// Creates a new message with the given mini_table on the given arena. +UPB_API upb_Message* upb_Message_New(const upb_MiniTable* m, upb_Arena* arena); + +// Returns a reference to the message's unknown data. +const char* upb_Message_GetUnknown(const upb_Message* msg, size_t* len); + +// Removes partial unknown data from message. +void upb_Message_DeleteUnknown(upb_Message* msg, const char* data, size_t len); + +// Returns the number of extensions present in this message. +size_t upb_Message_ExtensionCount(const upb_Message* msg); + +// Mark a message and all of its descendents as frozen/immutable. +UPB_API void upb_Message_Freeze(upb_Message* msg, const upb_MiniTable* m); + +// Returns whether a message has been frozen. +UPB_API_INLINE bool upb_Message_IsFrozen(const upb_Message* msg); + +#ifdef UPB_TRACING_ENABLED +UPB_API void upb_Message_LogNewMessage(const upb_MiniTable* m, + const upb_Arena* arena); + +UPB_API void upb_Message_SetNewMessageTraceHandler( + void (*handler)(const upb_MiniTable* m, const upb_Arena* arena)); +#endif // UPB_TRACING_ENABLED + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* UPB_MESSAGE_MESSAGE_H_ */ + +#ifndef UPB_REFLECTION_DEF_H_ +#define UPB_REFLECTION_DEF_H_ + +// IWYU pragma: begin_exports + +// IWYU pragma: private, include "upb/reflection/def.h" + +#ifndef UPB_REFLECTION_DEF_POOL_H_ +#define UPB_REFLECTION_DEF_POOL_H_ + + +// IWYU pragma: private, include "upb/reflection/def.h" + +// Declarations common to all public def types. + +#ifndef UPB_REFLECTION_COMMON_H_ +#define UPB_REFLECTION_COMMON_H_ + +#ifndef THIRD_PARTY_UPB_UPB_REFLECTION_DESCRIPTOR_BOOTSTRAP_H_ +#define THIRD_PARTY_UPB_UPB_REFLECTION_DESCRIPTOR_BOOTSTRAP_H_ + +// IWYU pragma: begin_exports + +#if defined(UPB_BOOTSTRAP_STAGE) && UPB_BOOTSTRAP_STAGE == 0 +// This header is checked in. +#elif UPB_BOOTSTRAP_STAGE == 1 +// This header is generated at build time by the bootstrapping process. +#else +// This is the normal header, generated by upb_c_proto_library(). +/* This file was generated by upb_generator from the input file: + * + * google/protobuf/descriptor.proto + * + * Do not edit -- your changes will be discarded when the file is + * regenerated. + * NO CHECKED-IN PROTOBUF GENCODE */ + +#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPB_H__UPB_H_ +#define GOOGLE_PROTOBUF_DESCRIPTOR_PROTO_UPB_H__UPB_H_ + + +#ifndef UPB_GENERATED_CODE_SUPPORT_H_ +#define UPB_GENERATED_CODE_SUPPORT_H_ + +// IWYU pragma: begin_exports + +#ifndef UPB_BASE_UPCAST_H_ +#define UPB_BASE_UPCAST_H_ + +// Must be last. + +// This macro provides a way to upcast message pointers in a way that is +// somewhat more bulletproof than blindly casting a pointer. Example: +// +// typedef struct { +// upb_Message UPB_PRIVATE(base); +// } pkg_FooMessage; +// +// void f(pkg_FooMessage* msg) { +// upb_Decode(UPB_UPCAST(msg), ...); +// } + +#define UPB_UPCAST(x) (&(x)->base##_dont_copy_me__upb_internal_use_only) + + +#endif /* UPB_BASE_UPCAST_H_ */ + +#ifndef UPB_MESSAGE_ACCESSORS_H_ +#define UPB_MESSAGE_ACCESSORS_H_ #include -#ifndef UPB_MINI_TABLE_INTERNAL_EXTENSION_H_ -#define UPB_MINI_TABLE_INTERNAL_EXTENSION_H_ +#ifndef UPB_MESSAGE_ARRAY_H_ +#define UPB_MESSAGE_ARRAY_H_ #include + + +#ifndef UPB_MESSAGE_INTERNAL_ARRAY_H_ +#define UPB_MESSAGE_INTERNAL_ARRAY_H_ + #include +#include // Must be last. -struct upb_MiniTableExtension { - // Do not move this field. We need to be able to alias pointers. - struct upb_MiniTableField UPB_PRIVATE(field); - - const struct upb_MiniTable* UPB_PRIVATE(extendee); - union upb_MiniTableSub UPB_PRIVATE(sub); // NULL unless submsg or proto2 enum -}; +#define _UPB_ARRAY_MASK_IMM 0x4 // Frozen/immutable bit. +#define _UPB_ARRAY_MASK_LG2 0x3 // Encoded elem size. +#define _UPB_ARRAY_MASK_ALL (_UPB_ARRAY_MASK_IMM | _UPB_ARRAY_MASK_LG2) #ifdef __cplusplus extern "C" { #endif -UPB_API_INLINE upb_CType -upb_MiniTableExtension_CType(const struct upb_MiniTableExtension* e) { - return upb_MiniTableField_CType(&e->UPB_PRIVATE(field)); +// LINT.IfChange(upb_Array) + +// Our internal representation for repeated fields. +struct upb_Array { + // This is a tagged pointer. Bits #0 and #1 encode the elem size as follows: + // 0 maps to elem size 1 + // 1 maps to elem size 4 + // 2 maps to elem size 8 + // 3 maps to elem size 16 + // + // Bit #2 contains the frozen/immutable flag. + uintptr_t UPB_ONLYBITS(data); + + size_t UPB_ONLYBITS(size); // The number of elements in the array. + size_t UPB_PRIVATE(capacity); // Allocated storage. Measured in elements. +}; + +UPB_INLINE void UPB_PRIVATE(_upb_Array_ShallowFreeze)(struct upb_Array* arr) { + arr->UPB_ONLYBITS(data) |= _UPB_ARRAY_MASK_IMM; } -UPB_API_INLINE uint32_t -upb_MiniTableExtension_Number(const struct upb_MiniTableExtension* e) { - return e->UPB_PRIVATE(field).UPB_ONLYBITS(number); +UPB_API_INLINE bool upb_Array_IsFrozen(const struct upb_Array* arr) { + return (arr->UPB_ONLYBITS(data) & _UPB_ARRAY_MASK_IMM) != 0; } -UPB_API_INLINE const struct upb_MiniTable* upb_MiniTableExtension_GetSubMessage( - const struct upb_MiniTableExtension* e) { - if (upb_MiniTableExtension_CType(e) != kUpb_CType_Message) { - return NULL; - } - return upb_MiniTableSub_Message(e->UPB_PRIVATE(sub)); +UPB_INLINE void UPB_PRIVATE(_upb_Array_SetTaggedPtr)(struct upb_Array* array, + void* data, size_t lg2) { + UPB_ASSERT(lg2 != 1); + UPB_ASSERT(lg2 <= 4); + const size_t bits = lg2 - (lg2 != 0); + array->UPB_ONLYBITS(data) = (uintptr_t)data | bits; } -UPB_API_INLINE void upb_MiniTableExtension_SetSubMessage( - struct upb_MiniTableExtension* e, const struct upb_MiniTable* m) { - e->UPB_PRIVATE(sub).UPB_PRIVATE(submsg) = m; +UPB_INLINE size_t +UPB_PRIVATE(_upb_Array_ElemSizeLg2)(const struct upb_Array* array) { + const size_t bits = array->UPB_ONLYBITS(data) & _UPB_ARRAY_MASK_LG2; + const size_t lg2 = bits + (bits != 0); + return lg2; } -UPB_INLINE upb_FieldRep UPB_PRIVATE(_upb_MiniTableExtension_GetRep)( - const struct upb_MiniTableExtension* e) { - return UPB_PRIVATE(_upb_MiniTableField_GetRep)(&e->UPB_PRIVATE(field)); +UPB_API_INLINE const void* upb_Array_DataPtr(const struct upb_Array* array) { + UPB_PRIVATE(_upb_Array_ElemSizeLg2)(array); // Check assertions. + return (void*)(array->UPB_ONLYBITS(data) & ~(uintptr_t)_UPB_ARRAY_MASK_ALL); +} + +UPB_API_INLINE void* upb_Array_MutableDataPtr(struct upb_Array* array) { + return (void*)upb_Array_DataPtr(array); +} + +UPB_INLINE struct upb_Array* UPB_PRIVATE(_upb_Array_New)(upb_Arena* arena, + size_t init_capacity, + int elem_size_lg2) { + UPB_ASSERT(elem_size_lg2 != 1); + UPB_ASSERT(elem_size_lg2 <= 4); + const size_t array_size = + UPB_ALIGN_UP(sizeof(struct upb_Array), UPB_MALLOC_ALIGN); + const size_t bytes = array_size + (init_capacity << elem_size_lg2); + struct upb_Array* array = (struct upb_Array*)upb_Arena_Malloc(arena, bytes); + if (!array) return NULL; + UPB_PRIVATE(_upb_Array_SetTaggedPtr) + (array, UPB_PTR_AT(array, array_size, void), elem_size_lg2); + array->UPB_ONLYBITS(size) = 0; + array->UPB_PRIVATE(capacity) = init_capacity; + return array; +} + +// Resizes the capacity of the array to be at least min_size. +bool UPB_PRIVATE(_upb_Array_Realloc)(struct upb_Array* array, size_t min_size, + upb_Arena* arena); + +UPB_API_INLINE bool upb_Array_Reserve(struct upb_Array* array, size_t size, + upb_Arena* arena) { + UPB_ASSERT(!upb_Array_IsFrozen(array)); + if (array->UPB_PRIVATE(capacity) < size) + return UPB_PRIVATE(_upb_Array_Realloc)(array, size, arena); + return true; +} + +// Resize without initializing new elements. +UPB_INLINE bool UPB_PRIVATE(_upb_Array_ResizeUninitialized)( + struct upb_Array* array, size_t size, upb_Arena* arena) { + UPB_ASSERT(!upb_Array_IsFrozen(array)); + UPB_ASSERT(size <= array->UPB_ONLYBITS(size) || + arena); // Allow NULL arena when shrinking. + if (!upb_Array_Reserve(array, size, arena)) return false; + array->UPB_ONLYBITS(size) = size; + return true; } +// This function is intended for situations where elem_size is compile-time +// constant or a known expression of the form (1 << lg2), so that the expression +// i*elem_size does not result in an actual multiplication. +UPB_INLINE void UPB_PRIVATE(_upb_Array_Set)(struct upb_Array* array, size_t i, + const void* data, + size_t elem_size) { + UPB_ASSERT(!upb_Array_IsFrozen(array)); + UPB_ASSERT(i < array->UPB_ONLYBITS(size)); + UPB_ASSERT(elem_size == 1U << UPB_PRIVATE(_upb_Array_ElemSizeLg2)(array)); + char* arr_data = (char*)upb_Array_MutableDataPtr(array); + memcpy(arr_data + (i * elem_size), data, elem_size); +} + +UPB_API_INLINE size_t upb_Array_Size(const struct upb_Array* arr) { + return arr->UPB_ONLYBITS(size); +} + +// LINT.ThenChange(GoogleInternalName0) + #ifdef __cplusplus } /* extern "C" */ #endif +#undef _UPB_ARRAY_MASK_IMM +#undef _UPB_ARRAY_MASK_LG2 +#undef _UPB_ARRAY_MASK_ALL -#endif /* UPB_MINI_TABLE_INTERNAL_EXTENSION_H_ */ + +#endif /* UPB_MESSAGE_INTERNAL_ARRAY_H_ */ // Must be last. -typedef struct upb_MiniTableExtension upb_MiniTableExtension; +typedef struct upb_Array upb_Array; #ifdef __cplusplus extern "C" { #endif -UPB_API_INLINE upb_CType -upb_MiniTableExtension_CType(const upb_MiniTableExtension* e); +// Creates a new array on the given arena that holds elements of this type. +UPB_API upb_Array* upb_Array_New(upb_Arena* a, upb_CType type); -UPB_API_INLINE uint32_t -upb_MiniTableExtension_Number(const upb_MiniTableExtension* e); +// Returns the number of elements in the array. +UPB_API_INLINE size_t upb_Array_Size(const upb_Array* arr); -UPB_API_INLINE const upb_MiniTable* upb_MiniTableExtension_GetSubMessage( - const upb_MiniTableExtension* e); +// Returns the given element, which must be within the array's current size. +UPB_API upb_MessageValue upb_Array_Get(const upb_Array* arr, size_t i); + +// Returns a mutating pointer to the given element, which must be within the +// array's current size. +UPB_API upb_MutableMessageValue upb_Array_GetMutable(upb_Array* arr, size_t i); + +// Sets the given element, which must be within the array's current size. +UPB_API void upb_Array_Set(upb_Array* arr, size_t i, upb_MessageValue val); + +// Appends an element to the array. Returns false on allocation failure. +UPB_API bool upb_Array_Append(upb_Array* array, upb_MessageValue val, + upb_Arena* arena); + +// Moves elements within the array using memmove(). +// Like memmove(), the source and destination elements may be overlapping. +UPB_API void upb_Array_Move(upb_Array* array, size_t dst_idx, size_t src_idx, + size_t count); + +// Inserts one or more empty elements into the array. +// Existing elements are shifted right. +// The new elements have undefined state and must be set with `upb_Array_Set()`. +// REQUIRES: `i <= upb_Array_Size(arr)` +UPB_API bool upb_Array_Insert(upb_Array* array, size_t i, size_t count, + upb_Arena* arena); + +// Deletes one or more elements from the array. +// Existing elements are shifted left. +// REQUIRES: `i + count <= upb_Array_Size(arr)` +UPB_API void upb_Array_Delete(upb_Array* array, size_t i, size_t count); + +// Reserves |size| elements of storage for the array. +UPB_API_INLINE bool upb_Array_Reserve(struct upb_Array* array, size_t size, + upb_Arena* arena); + +// Changes the size of a vector. New elements are initialized to NULL/0. +// Returns false on allocation failure. +UPB_API bool upb_Array_Resize(upb_Array* array, size_t size, upb_Arena* arena); + +// Returns pointer to array data. +UPB_API_INLINE const void* upb_Array_DataPtr(const upb_Array* arr); + +// Returns mutable pointer to array data. +UPB_API_INLINE void* upb_Array_MutableDataPtr(upb_Array* arr); + +// Mark an array and all of its descendents as frozen/immutable. +// If the array elements are messages then |m| must point to the minitable for +// those messages. Otherwise |m| must be NULL. +UPB_API void upb_Array_Freeze(upb_Array* arr, const upb_MiniTable* m); + +// Returns whether an array has been frozen. +UPB_API_INLINE bool upb_Array_IsFrozen(const upb_Array* arr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* UPB_MESSAGE_ARRAY_H_ */ + +#ifndef UPB_MESSAGE_INTERNAL_ACCESSORS_H_ +#define UPB_MESSAGE_INTERNAL_ACCESSORS_H_ -UPB_API_INLINE void upb_MiniTableExtension_SetSubMessage( - upb_MiniTableExtension* e, const upb_MiniTable* m); +#include +#include +#include -#ifdef __cplusplus -} /* extern "C" */ -#endif +#ifndef UPB_BASE_INTERNAL_ENDIAN_H_ +#define UPB_BASE_INTERNAL_ENDIAN_H_ -#endif /* UPB_MINI_TABLE_EXTENSION_H_ */ +#include // Must be last. -// The internal representation of an extension is self-describing: it contains -// enough information that we can serialize it to binary format without needing -// to look it up in a upb_ExtensionRegistry. -// -// This representation allocates 16 bytes to data on 64-bit platforms. -// This is rather wasteful for scalars (in the extreme case of bool, -// it wastes 15 bytes). We accept this because we expect messages to be -// the most common extension type. -typedef struct { - const upb_MiniTableExtension* ext; - upb_MessageValue data; -} upb_Extension; - #ifdef __cplusplus extern "C" { #endif -// Adds the given extension data to the given message. -// |ext| is copied into the message instance. -// This logically replaces any previously-added extension with this number. -upb_Extension* UPB_PRIVATE(_upb_Message_GetOrCreateExtension)( - struct upb_Message* msg, const upb_MiniTableExtension* ext, - upb_Arena* arena); +UPB_INLINE bool upb_IsLittleEndian(void) { + const int x = 1; + return *(char*)&x == 1; +} -// Returns an array of extensions for this message. -// Note: the array is ordered in reverse relative to the order of creation. -const upb_Extension* UPB_PRIVATE(_upb_Message_Getexts)( - const struct upb_Message* msg, size_t* count); +UPB_INLINE uint32_t upb_BigEndian32(uint32_t val) { + if (upb_IsLittleEndian()) return val; -// Returns an extension for a message with a given mini table, -// or NULL if no extension exists with this mini table. -const upb_Extension* UPB_PRIVATE(_upb_Message_Getext)( - const struct upb_Message* msg, const upb_MiniTableExtension* ext); + return ((val & 0xff) << 24) | ((val & 0xff00) << 8) | + ((val & 0xff0000) >> 8) | ((val & 0xff000000) >> 24); +} + +UPB_INLINE uint64_t upb_BigEndian64(uint64_t val) { + if (upb_IsLittleEndian()) return val; + + const uint64_t hi = ((uint64_t)upb_BigEndian32((uint32_t)val)) << 32; + const uint64_t lo = upb_BigEndian32((uint32_t)(val >> 32)); + return hi | lo; +} #ifdef __cplusplus } /* extern "C" */ #endif -#endif /* UPB_MESSAGE_INTERNAL_EXTENSION_H_ */ +#endif /* UPB_BASE_INTERNAL_ENDIAN_H_ */ #ifndef UPB_MESSAGE_INTERNAL_MAP_H_ #define UPB_MESSAGE_INTERNAL_MAP_H_ @@ -3010,96 +3203,6 @@ struct upb_Map* _upb_Map_New(upb_Arena* a, size_t key_size, size_t value_size); #endif /* UPB_MESSAGE_INTERNAL_MAP_H_ */ -/* -** Our memory representation for parsing tables and messages themselves. -** Functions in this file are used by generated code and possibly reflection. -** -** The definitions in this file are internal to upb. -**/ - -#ifndef UPB_MESSAGE_INTERNAL_MESSAGE_H_ -#define UPB_MESSAGE_INTERNAL_MESSAGE_H_ - -#include -#include - - -// Must be last. - -#ifdef __cplusplus -extern "C" { -#endif - -extern const float kUpb_FltInfinity; -extern const double kUpb_Infinity; -extern const double kUpb_NaN; - -// Internal members of a upb_Message that track unknown fields and/or -// extensions. We can change this without breaking binary compatibility. - -typedef struct upb_Message_Internal { - // Total size of this structure, including the data that follows. - // Must be aligned to 8, which is alignof(upb_Extension) - uint32_t size; - - /* Offsets relative to the beginning of this structure. - * - * Unknown data grows forward from the beginning to unknown_end. - * Extension data grows backward from size to ext_begin. - * When the two meet, we're out of data and have to realloc. - * - * If we imagine that the final member of this struct is: - * char data[size - overhead]; // overhead = sizeof(upb_Message_Internal) - * - * Then we have: - * unknown data: data[0 .. (unknown_end - overhead)] - * extensions data: data[(ext_begin - overhead) .. (size - overhead)] */ - uint32_t unknown_end; - uint32_t ext_begin; - // Data follows, as if there were an array: - // char data[size - sizeof(upb_Message_Internal)]; -} upb_Message_Internal; - -#ifdef UPB_TRACING_ENABLED -UPB_API void upb_Message_LogNewMessage(const upb_MiniTable* m, - const upb_Arena* arena); -UPB_API void upb_Message_SetNewMessageTraceHandler( - void (*handler)(const upb_MiniTable*, const upb_Arena*)); -#endif // UPB_TRACING_ENABLED - -// Inline version upb_Message_New(), for internal use. -UPB_INLINE struct upb_Message* _upb_Message_New(const upb_MiniTable* m, - upb_Arena* a) { -#ifdef UPB_TRACING_ENABLED - upb_Message_LogNewMessage(m, a); -#endif // UPB_TRACING_ENABLED - - const int size = m->UPB_PRIVATE(size); - struct upb_Message* msg = (struct upb_Message*)upb_Arena_Malloc(a, size); - if (UPB_UNLIKELY(!msg)) return NULL; - memset(msg, 0, size); - return msg; -} - -// Discards the unknown fields for this message only. -void _upb_Message_DiscardUnknown_shallow(struct upb_Message* msg); - -// Adds unknown data (serialized protobuf data) to the given message. -// The data is copied into the message instance. -bool UPB_PRIVATE(_upb_Message_AddUnknown)(struct upb_Message* msg, - const char* data, size_t len, - upb_Arena* arena); - -bool UPB_PRIVATE(_upb_Message_Realloc)(struct upb_Message* msg, size_t need, - upb_Arena* arena); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - - -#endif /* UPB_MESSAGE_INTERNAL_MESSAGE_H_ */ - #ifndef UPB_MINI_TABLE_INTERNAL_TAGGED_PTR_H_ #define UPB_MINI_TABLE_INTERNAL_TAGGED_PTR_H_ @@ -3147,56 +3250,6 @@ UPB_INLINE struct upb_Message* UPB_PRIVATE( #endif /* UPB_MINI_TABLE_INTERNAL_TAGGED_PTR_H_ */ -#ifndef UPB_MESSAGE_INTERNAL_TYPES_H_ -#define UPB_MESSAGE_INTERNAL_TYPES_H_ - -#include - -// Must be last. - -#define UPB_OPAQUE(x) x##_opaque - -struct upb_Message { - union { - uintptr_t UPB_OPAQUE(internal); // tagged pointer, low bit == frozen - double d; // Forces same size for 32-bit/64-bit builds - }; -}; - -#ifdef __cplusplus -extern "C" { -#endif - -UPB_INLINE void UPB_PRIVATE(_upb_Message_ShallowFreeze)( - struct upb_Message* msg) { - msg->UPB_OPAQUE(internal) |= 1ULL; -} - -UPB_API_INLINE bool upb_Message_IsFrozen(const struct upb_Message* msg) { - return (msg->UPB_OPAQUE(internal) & 1ULL) != 0; -} - -UPB_INLINE struct upb_Message_Internal* UPB_PRIVATE(_upb_Message_GetInternal)( - const struct upb_Message* msg) { - const uintptr_t tmp = msg->UPB_OPAQUE(internal) & ~1ULL; - return (struct upb_Message_Internal*)tmp; -} - -UPB_INLINE void UPB_PRIVATE(_upb_Message_SetInternal)( - struct upb_Message* msg, struct upb_Message_Internal* internal) { - UPB_ASSERT(!upb_Message_IsFrozen(msg)); - msg->UPB_OPAQUE(internal) = (uintptr_t)internal; -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#undef UPB_OPAQUE - - -#endif /* UPB_MESSAGE_INTERNAL_TYPES_H_ */ - // Must be last. #if defined(__GNUC__) && !defined(__clang__) @@ -4203,57 +4256,6 @@ UPB_API_INLINE bool upb_Map_IsFrozen(const upb_Map* map); #endif /* UPB_MESSAGE_MAP_H_ */ -// Public APIs for message operations that do not depend on the schema. -// -// MiniTable-based accessors live in accessors.h. - -#ifndef UPB_MESSAGE_MESSAGE_H_ -#define UPB_MESSAGE_MESSAGE_H_ - -#include - - -// Must be last. - -typedef struct upb_Message upb_Message; - -#ifdef __cplusplus -extern "C" { -#endif - -// Creates a new message with the given mini_table on the given arena. -UPB_API upb_Message* upb_Message_New(const upb_MiniTable* m, upb_Arena* arena); - -// Returns a reference to the message's unknown data. -const char* upb_Message_GetUnknown(const upb_Message* msg, size_t* len); - -// Removes partial unknown data from message. -void upb_Message_DeleteUnknown(upb_Message* msg, const char* data, size_t len); - -// Returns the number of extensions present in this message. -size_t upb_Message_ExtensionCount(const upb_Message* msg); - -// Mark a message and all of its descendents as frozen/immutable. -UPB_API void upb_Message_Freeze(upb_Message* msg, const upb_MiniTable* m); - -// Returns whether a message has been frozen. -UPB_API_INLINE bool upb_Message_IsFrozen(const upb_Message* msg); - -#ifdef UPB_TRACING_ENABLED -UPB_API void upb_Message_LogNewMessage(const upb_MiniTable* m, - const upb_Arena* arena); - -UPB_API void upb_Message_SetNewMessageTraceHandler( - void (*handler)(const upb_MiniTable* m, const upb_Arena* arena)); -#endif // UPB_TRACING_ENABLED - -#ifdef __cplusplus -} /* extern "C" */ -#endif - - -#endif /* UPB_MESSAGE_MESSAGE_H_ */ - #ifndef UPB_MINI_TABLE_TAGGED_PTR_H_ #define UPB_MINI_TABLE_TAGGED_PTR_H_ @@ -13019,9 +13021,27 @@ extern "C" { enum { upb_JsonDecode_IgnoreUnknown = 1 }; -UPB_API bool upb_JsonDecode(const char* buf, size_t size, upb_Message* msg, - const upb_MessageDef* m, const upb_DefPool* symtab, - int options, upb_Arena* arena, upb_Status* status); +enum { + kUpb_JsonDecodeResult_Ok = 0, + kUpb_JsonDecodeResult_OkWithEmptyStringNumerics = 1, + kUpb_JsonDecodeResult_Error = 2, +}; + +UPB_API int upb_JsonDecodeDetectingNonconformance(const char* buf, size_t size, + upb_Message* msg, + const upb_MessageDef* m, + const upb_DefPool* symtab, + int options, upb_Arena* arena, + upb_Status* status); + +UPB_API_INLINE bool upb_JsonDecode(const char* buf, size_t size, + upb_Message* msg, const upb_MessageDef* m, + const upb_DefPool* symtab, int options, + upb_Arena* arena, upb_Status* status) { + return upb_JsonDecodeDetectingNonconformance(buf, size, msg, m, symtab, + options, arena, status) == + kUpb_JsonDecodeResult_Ok; +} #ifdef __cplusplus } /* extern "C" */ diff --git a/ruby/lib/google/protobuf/ffi/ffi.rb b/ruby/lib/google/protobuf/ffi/ffi.rb index 2549fcb0a625..0270f9778c3f 100644 --- a/ruby/lib/google/protobuf/ffi/ffi.rb +++ b/ruby/lib/google/protobuf/ffi/ffi.rb @@ -36,6 +36,11 @@ class FFI ## 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 diff --git a/ruby/lib/google/protobuf/ffi/message.rb b/ruby/lib/google/protobuf/ffi/message.rb index 66763c15a2e8..4b02ae52c9f2 100644 --- a/ruby/lib/google/protobuf/ffi/message.rb +++ b/ruby/lib/google/protobuf/ffi/message.rb @@ -17,7 +17,7 @@ class FFI 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, :upb_JsonDecode, [:binary_string, :size_t, :Message, Descriptor, :DefPool, :int, Internal::Arena, Status.by_ref], :bool + 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 @@ -270,7 +270,11 @@ def self.decode_json(data, options = {}) message = new pool_def = message.class.descriptor.instance_variable_get(:@descriptor_pool).descriptor_pool status = Google::Protobuf::FFI::Status.new - unless Google::Protobuf::FFI.json_decode_message(data, data.bytesize, message.instance_variable_get(:@msg), message.class.descriptor, pool_def, decoding_options, message.instance_variable_get(:@arena), status) + 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 diff --git a/ruby/tests/encode_decode_test.rb b/ruby/tests/encode_decode_test.rb index 0a2e9d4ddca1..41355fd7b612 100755 --- a/ruby/tests/encode_decode_test.rb +++ b/ruby/tests/encode_decode_test.rb @@ -3,14 +3,75 @@ # generated_code.rb is in the same directory as this test. $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) +require 'basic_test_proto2_pb' require 'generated_code_pb' require 'google/protobuf/well_known_types' require 'test/unit' +module CaptureWarnings + @@warnings = nil + + module_function + + def warn(message, category: nil, **kwargs) + if @@warnings + @@warnings << message + else + super + end + end + + def capture + @@warnings = [] + yield + @@warnings + ensure + @@warnings = nil + end +end + +Warning.extend CaptureWarnings + def hex2bin(s) s.scan(/../).map { |x| x.hex.chr }.join end +class NonConformantNumericsTest < Test::Unit::TestCase + def test_empty_json_numerics + if defined? JRUBY_VERSION and Google::Protobuf::IMPLEMENTATION != :FFI + # In a future version, CRuby and JRuby FFI will also have this behavior. + assert_raises Google::Protobuf::ParseError do + msg = ::BasicTestProto2::TestMessage.decode_json('{"optionalInt32":""}') + end + else + warnings = CaptureWarnings.capture { + msg = ::BasicTestProto2::TestMessage.decode_json('{"optionalInt32":""}') + assert_equal 0, msg.optional_int32 + assert msg.has_optional_int32? + } + assert_equal 1, warnings.size + assert_match "Empty string is not a valid number (field: basic_test_proto2.TestMessage.optional_int32)", warnings[0] + end + end + + def test_trailing_non_numeric_characters + if defined? JRUBY_VERSION and Google::Protobuf::IMPLEMENTATION != :FFI + # In a future version, CRuby and JRuby FFI will also have this behavior. + assert_raises Google::Protobuf::ParseError do + msg = ::BasicTestProto2::TestMessage.decode_json('{"optionalDouble":"123abc"}') + end + else + warnings = CaptureWarnings.capture { + msg = ::BasicTestProto2::TestMessage.decode_json('{"optionalDouble":"123abc"}') + assert_equal 123, msg.optional_double + assert msg.has_optional_double? + } + assert_equal 1, warnings.size + assert_match "Non-number characters in quoted number (field: basic_test_proto2.TestMessage.optional_double)", warnings[0] + end + end +end + class EncodeDecodeTest < Test::Unit::TestCase def test_discard_unknown # Test discard unknown in message. diff --git a/upb/json/decode.c b/upb/json/decode.c index 9275bcb1af50..c14374550bda 100644 --- a/upb/json/decode.c +++ b/upb/json/decode.c @@ -41,6 +41,7 @@ typedef struct { upb_Arena* arena; /* TODO: should we have a tmp arena for tmp data? */ const upb_DefPool* symtab; int depth; + int result; upb_Status* status; jmp_buf err; int line; @@ -670,6 +671,16 @@ static int64_t jsondec_strtoint64(jsondec* d, upb_StringView str) { return ret; } +static void jsondec_checkempty(jsondec* d, upb_StringView str, + const upb_FieldDef* f) { + if (str.size != 0) return; + d->result = kUpb_JsonDecodeResult_OkWithEmptyStringNumerics; + upb_Status_SetErrorFormat(d->status, + "Empty string is not a valid number (field: %s). " + "This will be an error in a future version.", + upb_FieldDef_FullName(f)); +} + /* Primitive value types ******************************************************/ /* Parse INT32 or INT64 value. */ @@ -691,6 +702,7 @@ static upb_MessageValue jsondec_int(jsondec* d, const upb_FieldDef* f) { } case JD_STRING: { upb_StringView str = jsondec_string(d); + jsondec_checkempty(d, str, f); val.int64_val = jsondec_strtoint64(d, str); break; } @@ -728,6 +740,7 @@ static upb_MessageValue jsondec_uint(jsondec* d, const upb_FieldDef* f) { } case JD_STRING: { upb_StringView str = jsondec_string(d); + jsondec_checkempty(d, str, f); val.uint64_val = jsondec_strtouint64(d, str); break; } @@ -756,14 +769,26 @@ static upb_MessageValue jsondec_double(jsondec* d, const upb_FieldDef* f) { break; case JD_STRING: str = jsondec_string(d); - if (jsondec_streql(str, "NaN")) { + if (str.size == 0) { + jsondec_checkempty(d, str, f); + val.double_val = 0.0; + } else if (jsondec_streql(str, "NaN")) { val.double_val = NAN; } else if (jsondec_streql(str, "Infinity")) { val.double_val = INFINITY; } else if (jsondec_streql(str, "-Infinity")) { val.double_val = -INFINITY; } else { - val.double_val = strtod(str.data, NULL); + char* end; + val.double_val = strtod(str.data, &end); + if (end != str.data + str.size) { + d->result = kUpb_JsonDecodeResult_OkWithEmptyStringNumerics; + upb_Status_SetErrorFormat( + d->status, + "Non-number characters in quoted number (field: %s). " + "This will be an error in a future version.", + upb_FieldDef_FullName(f)); + } } break; default: @@ -1505,10 +1530,10 @@ static void jsondec_wellknown(jsondec* d, upb_Message* msg, } } -static bool upb_JsonDecoder_Decode(jsondec* const d, upb_Message* const msg, - const upb_MessageDef* const m) { +static int upb_JsonDecoder_Decode(jsondec* const d, upb_Message* const msg, + const upb_MessageDef* const m) { UPB_ASSERT(!upb_Message_IsFrozen(msg)); - if (UPB_SETJMP(d->err)) return false; + if (UPB_SETJMP(d->err)) return kUpb_JsonDecodeResult_Error; jsondec_tomsg(d, msg, m); @@ -1517,16 +1542,19 @@ static bool upb_JsonDecoder_Decode(jsondec* const d, upb_Message* const msg, jsondec_consumews(d); if (d->ptr == d->end) { - return true; + return d->result; } else { jsondec_seterrmsg(d, "unexpected trailing characters"); - return false; + return kUpb_JsonDecodeResult_Error; } } -bool upb_JsonDecode(const char* buf, size_t size, upb_Message* msg, - const upb_MessageDef* m, const upb_DefPool* symtab, - int options, upb_Arena* arena, upb_Status* status) { +int upb_JsonDecodeDetectingNonconformance(const char* buf, size_t size, + upb_Message* msg, + const upb_MessageDef* m, + const upb_DefPool* symtab, + int options, upb_Arena* arena, + upb_Status* status) { UPB_ASSERT(!upb_Message_IsFrozen(msg)); jsondec d; @@ -1539,6 +1567,7 @@ bool upb_JsonDecode(const char* buf, size_t size, upb_Message* msg, d.status = status; d.options = options; d.depth = 64; + d.result = kUpb_JsonDecodeResult_Ok; d.line = 1; d.line_begin = d.ptr; d.debug_field = NULL; diff --git a/upb/json/decode.h b/upb/json/decode.h index cc17e84d21f8..ffb05dc80b75 100644 --- a/upb/json/decode.h +++ b/upb/json/decode.h @@ -8,6 +8,11 @@ #ifndef UPB_JSON_DECODE_H_ #define UPB_JSON_DECODE_H_ +#include + +#include "upb/base/status.h" +#include "upb/mem/arena.h" +#include "upb/message/message.h" #include "upb/reflection/def.h" // Must be last. @@ -19,9 +24,27 @@ extern "C" { enum { upb_JsonDecode_IgnoreUnknown = 1 }; -UPB_API bool upb_JsonDecode(const char* buf, size_t size, upb_Message* msg, - const upb_MessageDef* m, const upb_DefPool* symtab, - int options, upb_Arena* arena, upb_Status* status); +enum { + kUpb_JsonDecodeResult_Ok = 0, + kUpb_JsonDecodeResult_OkWithEmptyStringNumerics = 1, + kUpb_JsonDecodeResult_Error = 2, +}; + +UPB_API int upb_JsonDecodeDetectingNonconformance(const char* buf, size_t size, + upb_Message* msg, + const upb_MessageDef* m, + const upb_DefPool* symtab, + int options, upb_Arena* arena, + upb_Status* status); + +UPB_API_INLINE bool upb_JsonDecode(const char* buf, size_t size, + upb_Message* msg, const upb_MessageDef* m, + const upb_DefPool* symtab, int options, + upb_Arena* arena, upb_Status* status) { + return upb_JsonDecodeDetectingNonconformance(buf, size, msg, m, symtab, + options, arena, status) == + kUpb_JsonDecodeResult_Ok; +} #ifdef __cplusplus } /* extern "C" */