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

931 lines
30 KiB

/*
* Copyright (c) 2009-2021, Google LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Google LLC nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <ctype.h>
#include <errno.h>
#include "upb/mini_table/decode.h"
Refactored message accessors to share a common set of functions instead of duplicating logic. Prior to this CL, there were several different code paths for reading/writing message data. Generated code, MiniTable accessors, and reflection all performed direct manipulation of the bits and bytes in a message, but they all had distinct implementations that did not share much of any code. This divergence meant that they could easily have different behavior, bugs could creep into one but not another, and we would need three different sets of tests to get full test coverage. This also made it very difficult to change the internal representation in any way, since it would require updating many places in the code. With this CL, the three different APIs for accessing message data now all share a common set of functions. The common functions all take a `upb_MiniTableField` as the canonical description of a field's type and layout. The lowest-level functions are very branchy, as they must test for every possible variation in the field type (field vs oneof, hasbit vs no-hasbit, different field sizes, whether a nonzero default value exists, extension vs. regular field), however these functions are declared inline and designed to be very optimizable when values are known at compile time. In generated accessors, for example, we can declare constant `upb_MiniTableField` instances so that all values can constant-propagate, and we can get fully specialized code even though we are calling a generic function. On the other hand, when we use the generic functions from reflection, we get runtime branches since values are not known at compile time. But even the function is written to still be as efficient as possible even when used from reflection. For example, we use memcpy() calls with constant length so that the compiler can optimize these into inline loads/stores without having to make an out-of-line call to memcpy(). In this way, this CL should be a benefit to both correctness and performance. It will also make it easier to change the message representation, for example to optimize the encoder by giving hasbits to all fields. Note that we have not completely consolidated all access in this CL: 1. Some functions outside of get/set such as clear and hazzers are not yet unified. 2. The encoder and decoder still touch the message without going through the common functions. The encoder and decoder require a bit more specialized code to get good performance when reading/writing fields en masse. PiperOrigin-RevId: 490016095
2 years ago
#include "upb/reflection/def.h"
#include "upb/reflection/def_builder_internal.h"
#include "upb/reflection/def_pool.h"
#include "upb/reflection/def_type.h"
#include "upb/reflection/desc_state_internal.h"
#include "upb/reflection/enum_def_internal.h"
#include "upb/reflection/enum_value_def_internal.h"
#include "upb/reflection/field_def_internal.h"
#include "upb/reflection/file_def_internal.h"
#include "upb/reflection/message_def_internal.h"
#include "upb/reflection/oneof_def_internal.h"
// Must be last.
#include "upb/port/def.inc"
#define UPB_FIELD_TYPE_UNSPECIFIED 0
typedef struct {
size_t len;
char str[1]; // Null-terminated string data follows.
} str_t;
struct upb_FieldDef {
const google_protobuf_FieldOptions* opts;
const upb_FileDef* file;
const upb_MessageDef* msgdef;
const char* full_name;
const char* json_name;
union {
int64_t sint;
uint64_t uint;
double dbl;
float flt;
bool boolean;
str_t* str;
Refactored message accessors to share a common set of functions instead of duplicating logic. Prior to this CL, there were several different code paths for reading/writing message data. Generated code, MiniTable accessors, and reflection all performed direct manipulation of the bits and bytes in a message, but they all had distinct implementations that did not share much of any code. This divergence meant that they could easily have different behavior, bugs could creep into one but not another, and we would need three different sets of tests to get full test coverage. This also made it very difficult to change the internal representation in any way, since it would require updating many places in the code. With this CL, the three different APIs for accessing message data now all share a common set of functions. The common functions all take a `upb_MiniTableField` as the canonical description of a field's type and layout. The lowest-level functions are very branchy, as they must test for every possible variation in the field type (field vs oneof, hasbit vs no-hasbit, different field sizes, whether a nonzero default value exists, extension vs. regular field), however these functions are declared inline and designed to be very optimizable when values are known at compile time. In generated accessors, for example, we can declare constant `upb_MiniTableField` instances so that all values can constant-propagate, and we can get fully specialized code even though we are calling a generic function. On the other hand, when we use the generic functions from reflection, we get runtime branches since values are not known at compile time. But even the function is written to still be as efficient as possible even when used from reflection. For example, we use memcpy() calls with constant length so that the compiler can optimize these into inline loads/stores without having to make an out-of-line call to memcpy(). In this way, this CL should be a benefit to both correctness and performance. It will also make it easier to change the message representation, for example to optimize the encoder by giving hasbits to all fields. Note that we have not completely consolidated all access in this CL: 1. Some functions outside of get/set such as clear and hazzers are not yet unified. 2. The encoder and decoder still touch the message without going through the common functions. The encoder and decoder require a bit more specialized code to get good performance when reading/writing fields en masse. PiperOrigin-RevId: 490016095
2 years ago
void* msg; // Always NULL.
} defaultval;
union {
const upb_OneofDef* oneof;
const upb_MessageDef* extension_scope;
} scope;
union {
const upb_MessageDef* msgdef;
const upb_EnumDef* enumdef;
const google_protobuf_FieldDescriptorProto* unresolved;
} sub;
uint32_t number_;
uint16_t index_;
uint16_t layout_index; // Index into msgdef->layout->fields or file->exts
bool has_default;
bool is_extension_;
bool is_packed_;
bool proto3_optional_;
bool has_json_name_;
upb_FieldType type_;
upb_Label label_;
#if UINTPTR_MAX == 0xffffffff
uint32_t padding; // Increase size to a multiple of 8.
#endif
};
upb_FieldDef* _upb_FieldDef_At(const upb_FieldDef* f, int i) {
return (upb_FieldDef*)&f[i];
}
const google_protobuf_FieldOptions* upb_FieldDef_Options(const upb_FieldDef* f) {
return f->opts;
}
bool upb_FieldDef_HasOptions(const upb_FieldDef* f) {
return f->opts != (void*)kUpbDefOptDefault;
}
const char* upb_FieldDef_FullName(const upb_FieldDef* f) {
return f->full_name;
}
upb_CType upb_FieldDef_CType(const upb_FieldDef* f) {
switch (f->type_) {
case kUpb_FieldType_Double:
return kUpb_CType_Double;
case kUpb_FieldType_Float:
return kUpb_CType_Float;
case kUpb_FieldType_Int64:
case kUpb_FieldType_SInt64:
case kUpb_FieldType_SFixed64:
return kUpb_CType_Int64;
case kUpb_FieldType_Int32:
case kUpb_FieldType_SFixed32:
case kUpb_FieldType_SInt32:
return kUpb_CType_Int32;
case kUpb_FieldType_UInt64:
case kUpb_FieldType_Fixed64:
return kUpb_CType_UInt64;
case kUpb_FieldType_UInt32:
case kUpb_FieldType_Fixed32:
return kUpb_CType_UInt32;
case kUpb_FieldType_Enum:
return kUpb_CType_Enum;
case kUpb_FieldType_Bool:
return kUpb_CType_Bool;
case kUpb_FieldType_String:
return kUpb_CType_String;
case kUpb_FieldType_Bytes:
return kUpb_CType_Bytes;
case kUpb_FieldType_Group:
case kUpb_FieldType_Message:
return kUpb_CType_Message;
}
UPB_UNREACHABLE();
}
upb_FieldType upb_FieldDef_Type(const upb_FieldDef* f) { return f->type_; }
uint32_t upb_FieldDef_Index(const upb_FieldDef* f) { return f->index_; }
upb_Label upb_FieldDef_Label(const upb_FieldDef* f) { return f->label_; }
uint32_t upb_FieldDef_Number(const upb_FieldDef* f) { return f->number_; }
bool upb_FieldDef_IsExtension(const upb_FieldDef* f) {
return f->is_extension_;
}
bool upb_FieldDef_IsPacked(const upb_FieldDef* f) { return f->is_packed_; }
const char* upb_FieldDef_Name(const upb_FieldDef* f) {
return _upb_DefBuilder_FullToShort(f->full_name);
}
const char* upb_FieldDef_JsonName(const upb_FieldDef* f) {
return f->json_name;
}
bool upb_FieldDef_HasJsonName(const upb_FieldDef* f) {
return f->has_json_name_;
}
const upb_FileDef* upb_FieldDef_File(const upb_FieldDef* f) { return f->file; }
const upb_MessageDef* upb_FieldDef_ContainingType(const upb_FieldDef* f) {
return f->msgdef;
}
const upb_MessageDef* upb_FieldDef_ExtensionScope(const upb_FieldDef* f) {
return f->is_extension_ ? f->scope.extension_scope : NULL;
}
const upb_OneofDef* upb_FieldDef_ContainingOneof(const upb_FieldDef* f) {
return f->is_extension_ ? NULL : f->scope.oneof;
}
const upb_OneofDef* upb_FieldDef_RealContainingOneof(const upb_FieldDef* f) {
const upb_OneofDef* oneof = upb_FieldDef_ContainingOneof(f);
if (!oneof || upb_OneofDef_IsSynthetic(oneof)) return NULL;
return oneof;
}
upb_MessageValue upb_FieldDef_Default(const upb_FieldDef* f) {
upb_MessageValue ret;
Refactored message accessors to share a common set of functions instead of duplicating logic. Prior to this CL, there were several different code paths for reading/writing message data. Generated code, MiniTable accessors, and reflection all performed direct manipulation of the bits and bytes in a message, but they all had distinct implementations that did not share much of any code. This divergence meant that they could easily have different behavior, bugs could creep into one but not another, and we would need three different sets of tests to get full test coverage. This also made it very difficult to change the internal representation in any way, since it would require updating many places in the code. With this CL, the three different APIs for accessing message data now all share a common set of functions. The common functions all take a `upb_MiniTableField` as the canonical description of a field's type and layout. The lowest-level functions are very branchy, as they must test for every possible variation in the field type (field vs oneof, hasbit vs no-hasbit, different field sizes, whether a nonzero default value exists, extension vs. regular field), however these functions are declared inline and designed to be very optimizable when values are known at compile time. In generated accessors, for example, we can declare constant `upb_MiniTableField` instances so that all values can constant-propagate, and we can get fully specialized code even though we are calling a generic function. On the other hand, when we use the generic functions from reflection, we get runtime branches since values are not known at compile time. But even the function is written to still be as efficient as possible even when used from reflection. For example, we use memcpy() calls with constant length so that the compiler can optimize these into inline loads/stores without having to make an out-of-line call to memcpy(). In this way, this CL should be a benefit to both correctness and performance. It will also make it easier to change the message representation, for example to optimize the encoder by giving hasbits to all fields. Note that we have not completely consolidated all access in this CL: 1. Some functions outside of get/set such as clear and hazzers are not yet unified. 2. The encoder and decoder still touch the message without going through the common functions. The encoder and decoder require a bit more specialized code to get good performance when reading/writing fields en masse. PiperOrigin-RevId: 490016095
2 years ago
if (upb_FieldDef_IsRepeated(f) || upb_FieldDef_IsSubMessage(f)) {
return (upb_MessageValue){.msg_val = NULL};
}
switch (upb_FieldDef_CType(f)) {
case kUpb_CType_Bool:
return (upb_MessageValue){.bool_val = f->defaultval.boolean};
case kUpb_CType_Int64:
return (upb_MessageValue){.int64_val = f->defaultval.sint};
case kUpb_CType_UInt64:
return (upb_MessageValue){.uint64_val = f->defaultval.uint};
case kUpb_CType_Enum:
case kUpb_CType_Int32:
return (upb_MessageValue){.int32_val = (int32_t)f->defaultval.sint};
case kUpb_CType_UInt32:
return (upb_MessageValue){.uint32_val = (uint32_t)f->defaultval.uint};
case kUpb_CType_Float:
return (upb_MessageValue){.float_val = f->defaultval.flt};
case kUpb_CType_Double:
return (upb_MessageValue){.double_val = f->defaultval.dbl};
case kUpb_CType_String:
case kUpb_CType_Bytes: {
str_t* str = f->defaultval.str;
if (str) {
return (upb_MessageValue){
.str_val = (upb_StringView){.data = str->str, .size = str->len}};
} else {
return (upb_MessageValue){
.str_val = (upb_StringView){.data = NULL, .size = 0}};
}
}
default:
UPB_UNREACHABLE();
}
return ret;
}
const upb_MessageDef* upb_FieldDef_MessageSubDef(const upb_FieldDef* f) {
return upb_FieldDef_CType(f) == kUpb_CType_Message ? f->sub.msgdef : NULL;
}
const upb_EnumDef* upb_FieldDef_EnumSubDef(const upb_FieldDef* f) {
return upb_FieldDef_CType(f) == kUpb_CType_Enum ? f->sub.enumdef : NULL;
}
const upb_MiniTableField* upb_FieldDef_MiniTable(const upb_FieldDef* f) {
Refactored message accessors to share a common set of functions instead of duplicating logic. Prior to this CL, there were several different code paths for reading/writing message data. Generated code, MiniTable accessors, and reflection all performed direct manipulation of the bits and bytes in a message, but they all had distinct implementations that did not share much of any code. This divergence meant that they could easily have different behavior, bugs could creep into one but not another, and we would need three different sets of tests to get full test coverage. This also made it very difficult to change the internal representation in any way, since it would require updating many places in the code. With this CL, the three different APIs for accessing message data now all share a common set of functions. The common functions all take a `upb_MiniTableField` as the canonical description of a field's type and layout. The lowest-level functions are very branchy, as they must test for every possible variation in the field type (field vs oneof, hasbit vs no-hasbit, different field sizes, whether a nonzero default value exists, extension vs. regular field), however these functions are declared inline and designed to be very optimizable when values are known at compile time. In generated accessors, for example, we can declare constant `upb_MiniTableField` instances so that all values can constant-propagate, and we can get fully specialized code even though we are calling a generic function. On the other hand, when we use the generic functions from reflection, we get runtime branches since values are not known at compile time. But even the function is written to still be as efficient as possible even when used from reflection. For example, we use memcpy() calls with constant length so that the compiler can optimize these into inline loads/stores without having to make an out-of-line call to memcpy(). In this way, this CL should be a benefit to both correctness and performance. It will also make it easier to change the message representation, for example to optimize the encoder by giving hasbits to all fields. Note that we have not completely consolidated all access in this CL: 1. Some functions outside of get/set such as clear and hazzers are not yet unified. 2. The encoder and decoder still touch the message without going through the common functions. The encoder and decoder require a bit more specialized code to get good performance when reading/writing fields en masse. PiperOrigin-RevId: 490016095
2 years ago
if (upb_FieldDef_IsExtension(f)) {
const upb_FileDef* file = upb_FieldDef_File(f);
return (upb_MiniTableField*)_upb_FileDef_ExtensionMiniTable(
file, f->layout_index);
} else {
const upb_MiniTable* layout = upb_MessageDef_MiniTable(f->msgdef);
return &layout->fields[f->layout_index];
}
}
const upb_MiniTableExtension* _upb_FieldDef_ExtensionMiniTable(
const upb_FieldDef* f) {
UPB_ASSERT(upb_FieldDef_IsExtension(f));
const upb_FileDef* file = upb_FieldDef_File(f);
return _upb_FileDef_ExtensionMiniTable(file, f->layout_index);
}
bool _upb_FieldDef_IsClosedEnum(const upb_FieldDef* f) {
if (UPB_TREAT_PROTO2_ENUMS_LIKE_PROTO3) return false;
if (f->type_ != kUpb_FieldType_Enum) return false;
// TODO: Maybe make is_proto2 a bool at creation?
const upb_FileDef* file = upb_EnumDef_File(f->sub.enumdef);
return upb_FileDef_Syntax(file) == kUpb_Syntax_Proto2;
}
bool _upb_FieldDef_IsProto3Optional(const upb_FieldDef* f) {
return f->proto3_optional_;
}
int _upb_FieldDef_LayoutIndex(const upb_FieldDef* f) { return f->layout_index; }
uint64_t _upb_FieldDef_Modifiers(const upb_FieldDef* f) {
uint64_t out = f->is_packed_ ? kUpb_FieldModifier_IsPacked : 0;
switch (f->label_) {
case kUpb_Label_Optional:
if (!upb_FieldDef_HasPresence(f)) {
out |= kUpb_FieldModifier_IsProto3Singular;
}
break;
case kUpb_Label_Repeated:
out |= kUpb_FieldModifier_IsRepeated;
break;
case kUpb_Label_Required:
out |= kUpb_FieldModifier_IsRequired;
break;
}
if (_upb_FieldDef_IsClosedEnum(f)) {
out |= kUpb_FieldModifier_IsClosedEnum;
}
return out;
}
bool upb_FieldDef_HasDefault(const upb_FieldDef* f) { return f->has_default; }
bool upb_FieldDef_HasPresence(const upb_FieldDef* f) {
if (upb_FieldDef_IsRepeated(f)) return false;
const upb_FileDef* file = upb_FieldDef_File(f);
return upb_FieldDef_IsSubMessage(f) || upb_FieldDef_ContainingOneof(f) ||
upb_FileDef_Syntax(file) == kUpb_Syntax_Proto2;
}
bool upb_FieldDef_HasSubDef(const upb_FieldDef* f) {
return upb_FieldDef_IsSubMessage(f) ||
upb_FieldDef_CType(f) == kUpb_CType_Enum;
}
bool upb_FieldDef_IsMap(const upb_FieldDef* f) {
return upb_FieldDef_IsRepeated(f) && upb_FieldDef_IsSubMessage(f) &&
upb_MessageDef_IsMapEntry(upb_FieldDef_MessageSubDef(f));
}
bool upb_FieldDef_IsOptional(const upb_FieldDef* f) {
return upb_FieldDef_Label(f) == kUpb_Label_Optional;
}
bool upb_FieldDef_IsPrimitive(const upb_FieldDef* f) {
return !upb_FieldDef_IsString(f) && !upb_FieldDef_IsSubMessage(f);
}
bool upb_FieldDef_IsRepeated(const upb_FieldDef* f) {
return upb_FieldDef_Label(f) == kUpb_Label_Repeated;
}
bool upb_FieldDef_IsRequired(const upb_FieldDef* f) {
return upb_FieldDef_Label(f) == kUpb_Label_Required;
}
bool upb_FieldDef_IsString(const upb_FieldDef* f) {
return upb_FieldDef_CType(f) == kUpb_CType_String ||
upb_FieldDef_CType(f) == kUpb_CType_Bytes;
}
bool upb_FieldDef_IsSubMessage(const upb_FieldDef* f) {
return upb_FieldDef_CType(f) == kUpb_CType_Message;
}
static bool between(int32_t x, int32_t low, int32_t high) {
return x >= low && x <= high;
}
bool upb_FieldDef_checklabel(int32_t label) { return between(label, 1, 3); }
bool upb_FieldDef_checktype(int32_t type) { return between(type, 1, 11); }
bool upb_FieldDef_checkintfmt(int32_t fmt) { return between(fmt, 1, 3); }
bool upb_FieldDef_checkdescriptortype(int32_t type) {
return between(type, 1, 18);
}
static bool streql2(const char* a, size_t n, const char* b) {
return n == strlen(b) && memcmp(a, b, n) == 0;
}
// Implement the transformation as described in the spec:
// 1. upper case all letters after an underscore.
// 2. remove all underscores.
static char* make_json_name(const char* name, size_t size, upb_Arena* a) {
char* out = upb_Arena_Malloc(a, size + 1); // +1 is to add a trailing '\0'
if (out == NULL) return NULL;
bool ucase_next = false;
char* des = out;
for (size_t i = 0; i < size; i++) {
if (name[i] == '_') {
ucase_next = true;
} else {
*des++ = ucase_next ? toupper(name[i]) : name[i];
ucase_next = false;
}
}
*des++ = '\0';
return out;
}
static str_t* newstr(upb_DefBuilder* ctx, const char* data, size_t len) {
str_t* ret = _upb_DefBuilder_Alloc(ctx, sizeof(*ret) + len);
if (!ret) _upb_DefBuilder_OomErr(ctx);
ret->len = len;
if (len) memcpy(ret->str, data, len);
ret->str[len] = '\0';
return ret;
}
static str_t* unescape(upb_DefBuilder* ctx, const upb_FieldDef* f,
const char* data, size_t len) {
// Size here is an upper bound; escape sequences could ultimately shrink it.
str_t* ret = _upb_DefBuilder_Alloc(ctx, sizeof(*ret) + len);
char* dst = &ret->str[0];
const char* src = data;
const char* end = data + len;
while (src < end) {
if (*src == '\\') {
src++;
*dst++ = _upb_DefBuilder_ParseEscape(ctx, f, &src, end);
} else {
*dst++ = *src++;
}
}
ret->len = dst - &ret->str[0];
return ret;
}
static void parse_default(upb_DefBuilder* ctx, const char* str, size_t len,
upb_FieldDef* f) {
char* end;
char nullz[64];
errno = 0;
switch (upb_FieldDef_CType(f)) {
case kUpb_CType_Int32:
case kUpb_CType_Int64:
case kUpb_CType_UInt32:
case kUpb_CType_UInt64:
case kUpb_CType_Double:
case kUpb_CType_Float:
// Standard C number parsing functions expect null-terminated strings.
if (len >= sizeof(nullz) - 1) {
_upb_DefBuilder_Errf(ctx, "Default too long: %.*s", (int)len, str);
}
memcpy(nullz, str, len);
nullz[len] = '\0';
str = nullz;
break;
default:
break;
}
switch (upb_FieldDef_CType(f)) {
case kUpb_CType_Int32: {
long val = strtol(str, &end, 0);
if (val > INT32_MAX || val < INT32_MIN || errno == ERANGE || *end) {
goto invalid;
}
f->defaultval.sint = val;
break;
}
case kUpb_CType_Enum: {
const upb_EnumDef* e = f->sub.enumdef;
const upb_EnumValueDef* ev =
upb_EnumDef_FindValueByNameWithSize(e, str, len);
if (!ev) {
goto invalid;
}
f->defaultval.sint = upb_EnumValueDef_Number(ev);
break;
}
case kUpb_CType_Int64: {
long long val = strtoll(str, &end, 0);
if (val > INT64_MAX || val < INT64_MIN || errno == ERANGE || *end) {
goto invalid;
}
f->defaultval.sint = val;
break;
}
case kUpb_CType_UInt32: {
unsigned long val = strtoul(str, &end, 0);
if (val > UINT32_MAX || errno == ERANGE || *end) {
goto invalid;
}
f->defaultval.uint = val;
break;
}
case kUpb_CType_UInt64: {
unsigned long long val = strtoull(str, &end, 0);
if (val > UINT64_MAX || errno == ERANGE || *end) {
goto invalid;
}
f->defaultval.uint = val;
break;
}
case kUpb_CType_Double: {
double val = strtod(str, &end);
if (errno == ERANGE || *end) {
goto invalid;
}
f->defaultval.dbl = val;
break;
}
case kUpb_CType_Float: {
float val = strtof(str, &end);
if (errno == ERANGE || *end) {
goto invalid;
}
f->defaultval.flt = val;
break;
}
case kUpb_CType_Bool: {
if (streql2(str, len, "false")) {
f->defaultval.boolean = false;
} else if (streql2(str, len, "true")) {
f->defaultval.boolean = true;
} else {
goto invalid;
}
break;
}
case kUpb_CType_String:
f->defaultval.str = newstr(ctx, str, len);
break;
case kUpb_CType_Bytes:
f->defaultval.str = unescape(ctx, f, str, len);
break;
case kUpb_CType_Message:
/* Should not have a default value. */
_upb_DefBuilder_Errf(ctx, "Message should not have a default (%s)",
upb_FieldDef_FullName(f));
}
return;
invalid:
_upb_DefBuilder_Errf(ctx, "Invalid default '%.*s' for field %s of type %d",
(int)len, str, upb_FieldDef_FullName(f),
(int)upb_FieldDef_Type(f));
}
static void set_default_default(upb_DefBuilder* ctx, upb_FieldDef* f) {
switch (upb_FieldDef_CType(f)) {
case kUpb_CType_Int32:
case kUpb_CType_Int64:
f->defaultval.sint = 0;
break;
case kUpb_CType_UInt64:
case kUpb_CType_UInt32:
f->defaultval.uint = 0;
break;
case kUpb_CType_Double:
case kUpb_CType_Float:
f->defaultval.dbl = 0;
break;
case kUpb_CType_String:
case kUpb_CType_Bytes:
f->defaultval.str = newstr(ctx, NULL, 0);
break;
case kUpb_CType_Bool:
f->defaultval.boolean = false;
break;
case kUpb_CType_Enum: {
const upb_EnumValueDef* v = upb_EnumDef_Value(f->sub.enumdef, 0);
f->defaultval.sint = upb_EnumValueDef_Number(v);
Refactored message accessors to share a common set of functions instead of duplicating logic. Prior to this CL, there were several different code paths for reading/writing message data. Generated code, MiniTable accessors, and reflection all performed direct manipulation of the bits and bytes in a message, but they all had distinct implementations that did not share much of any code. This divergence meant that they could easily have different behavior, bugs could creep into one but not another, and we would need three different sets of tests to get full test coverage. This also made it very difficult to change the internal representation in any way, since it would require updating many places in the code. With this CL, the three different APIs for accessing message data now all share a common set of functions. The common functions all take a `upb_MiniTableField` as the canonical description of a field's type and layout. The lowest-level functions are very branchy, as they must test for every possible variation in the field type (field vs oneof, hasbit vs no-hasbit, different field sizes, whether a nonzero default value exists, extension vs. regular field), however these functions are declared inline and designed to be very optimizable when values are known at compile time. In generated accessors, for example, we can declare constant `upb_MiniTableField` instances so that all values can constant-propagate, and we can get fully specialized code even though we are calling a generic function. On the other hand, when we use the generic functions from reflection, we get runtime branches since values are not known at compile time. But even the function is written to still be as efficient as possible even when used from reflection. For example, we use memcpy() calls with constant length so that the compiler can optimize these into inline loads/stores without having to make an out-of-line call to memcpy(). In this way, this CL should be a benefit to both correctness and performance. It will also make it easier to change the message representation, for example to optimize the encoder by giving hasbits to all fields. Note that we have not completely consolidated all access in this CL: 1. Some functions outside of get/set such as clear and hazzers are not yet unified. 2. The encoder and decoder still touch the message without going through the common functions. The encoder and decoder require a bit more specialized code to get good performance when reading/writing fields en masse. PiperOrigin-RevId: 490016095
2 years ago
break;
}
case kUpb_CType_Message:
break;
}
}
static void _upb_FieldDef_Create(upb_DefBuilder* ctx, const char* prefix,
const google_protobuf_FieldDescriptorProto* field_proto,
upb_MessageDef* m, upb_FieldDef* f) {
// Must happen before _upb_DefBuilder_Add()
f->file = _upb_DefBuilder_File(ctx);
if (!google_protobuf_FieldDescriptorProto_has_name(field_proto)) {
_upb_DefBuilder_Errf(ctx, "field has no name");
}
const upb_StringView name = google_protobuf_FieldDescriptorProto_name(field_proto);
_upb_DefBuilder_CheckIdentNotFull(ctx, name);
f->has_json_name_ = google_protobuf_FieldDescriptorProto_has_json_name(field_proto);
if (f->has_json_name_) {
const upb_StringView sv =
google_protobuf_FieldDescriptorProto_json_name(field_proto);
f->json_name = upb_strdup2(sv.data, sv.size, ctx->arena);
} else {
f->json_name = make_json_name(name.data, name.size, ctx->arena);
}
if (!f->json_name) _upb_DefBuilder_OomErr(ctx);
f->full_name = _upb_DefBuilder_MakeFullName(ctx, prefix, name);
f->label_ = (int)google_protobuf_FieldDescriptorProto_label(field_proto);
f->number_ = google_protobuf_FieldDescriptorProto_number(field_proto);
f->proto3_optional_ =
google_protobuf_FieldDescriptorProto_proto3_optional(field_proto);
f->msgdef = m;
f->scope.oneof = NULL;
const bool has_type = google_protobuf_FieldDescriptorProto_has_type(field_proto);
const bool has_type_name =
google_protobuf_FieldDescriptorProto_has_type_name(field_proto);
f->type_ = (int)google_protobuf_FieldDescriptorProto_type(field_proto);
if (has_type) {
switch (f->type_) {
case kUpb_FieldType_Message:
case kUpb_FieldType_Group:
case kUpb_FieldType_Enum:
if (!has_type_name) {
_upb_DefBuilder_Errf(ctx, "field of type %d requires type name (%s)",
(int)f->type_, f->full_name);
}
break;
default:
if (has_type_name) {
_upb_DefBuilder_Errf(
ctx, "invalid type for field with type_name set (%s, %d)",
f->full_name, (int)f->type_);
}
}
} else if (has_type_name) {
f->type_ =
UPB_FIELD_TYPE_UNSPECIFIED; // We'll fill this in in resolve_fielddef()
}
if (f->type_ < kUpb_FieldType_Double || f->type_ > kUpb_FieldType_SInt64) {
_upb_DefBuilder_Errf(ctx, "invalid type for field %s (%d)", f->full_name,
f->type_);
}
if (f->label_ < kUpb_Label_Optional || f->label_ > kUpb_Label_Repeated) {
_upb_DefBuilder_Errf(ctx, "invalid label for field %s (%d)", f->full_name,
f->label_);
}
/* We can't resolve the subdef or (in the case of extensions) the containing
* message yet, because it may not have been defined yet. We stash a pointer
* to the field_proto until later when we can properly resolve it. */
f->sub.unresolved = field_proto;
if (f->label_ == kUpb_Label_Required &&
upb_FileDef_Syntax(f->file) == kUpb_Syntax_Proto3) {
_upb_DefBuilder_Errf(ctx, "proto3 fields cannot be required (%s)",
f->full_name);
}
if (google_protobuf_FieldDescriptorProto_has_oneof_index(field_proto)) {
uint32_t oneof_index = google_protobuf_FieldDescriptorProto_oneof_index(field_proto);
if (upb_FieldDef_Label(f) != kUpb_Label_Optional) {
_upb_DefBuilder_Errf(ctx, "fields in oneof must have OPTIONAL label (%s)",
f->full_name);
}
if (!m) {
_upb_DefBuilder_Errf(ctx, "oneof field (%s) has no containing msg",
f->full_name);
}
if (oneof_index >= upb_MessageDef_OneofCount(m)) {
_upb_DefBuilder_Errf(ctx, "oneof_index out of range (%s)", f->full_name);
}
upb_OneofDef* oneof = (upb_OneofDef*)upb_MessageDef_Oneof(m, oneof_index);
f->scope.oneof = oneof;
bool ok = _upb_OneofDef_Insert(oneof, f, name.data, name.size, ctx->arena);
if (!ok) _upb_DefBuilder_OomErr(ctx);
}
UPB_DEF_SET_OPTIONS(f->opts, FieldDescriptorProto, FieldOptions, field_proto);
if (google_protobuf_FieldOptions_has_packed(f->opts)) {
f->is_packed_ = google_protobuf_FieldOptions_packed(f->opts);
} else {
// Repeated fields default to packed for proto3 only.
f->is_packed_ = upb_FieldDef_IsPrimitive(f) &&
f->label_ == kUpb_Label_Repeated &&
upb_FileDef_Syntax(f->file) == kUpb_Syntax_Proto3;
}
}
static void _upb_FieldDef_CreateExt(
upb_DefBuilder* ctx, const char* prefix,
const google_protobuf_FieldDescriptorProto* field_proto, upb_MessageDef* m,
upb_FieldDef* f) {
_upb_FieldDef_Create(ctx, prefix, field_proto, m, f);
f->is_extension_ = true;
if (google_protobuf_FieldDescriptorProto_has_oneof_index(field_proto)) {
_upb_DefBuilder_Errf(ctx, "oneof_index provided for extension field (%s)",
f->full_name);
}
f->scope.extension_scope = m;
_upb_DefBuilder_Add(ctx, f->full_name, _upb_DefType_Pack(f, UPB_DEFTYPE_EXT));
f->layout_index = ctx->ext_count++;
if (ctx->layout) {
UPB_ASSERT(_upb_FieldDef_ExtensionMiniTable(f)->field.number == f->number_);
}
}
static void _upb_FieldDef_CreateNotExt(
upb_DefBuilder* ctx, const char* prefix,
const google_protobuf_FieldDescriptorProto* field_proto, upb_MessageDef* m,
upb_FieldDef* f) {
_upb_FieldDef_Create(ctx, prefix, field_proto, m, f);
f->is_extension_ = false;
if (!google_protobuf_FieldDescriptorProto_has_oneof_index(field_proto)) {
if (f->proto3_optional_) {
_upb_DefBuilder_Errf(
ctx,
"non-extension field (%s) with proto3_optional was not in a oneof",
f->full_name);
}
}
_upb_MessageDef_InsertField(ctx, m, f);
if (!ctx->layout) return;
const upb_MiniTable* mt = upb_MessageDef_MiniTable(m);
const upb_MiniTableField* fields = mt->fields;
for (int i = 0; i < mt->field_count; i++) {
if (fields[i].number == f->number_) {
f->layout_index = i;
return;
}
}
UPB_ASSERT(false); // It should be impossible to reach this point.
}
upb_FieldDef* _upb_FieldDefs_New(
upb_DefBuilder* ctx, int n,
const google_protobuf_FieldDescriptorProto* const* protos, const char* prefix,
upb_MessageDef* m, bool* is_sorted) {
_upb_DefType_CheckPadding(sizeof(upb_FieldDef));
upb_FieldDef* defs =
(upb_FieldDef*)_upb_DefBuilder_Alloc(ctx, sizeof(upb_FieldDef) * n);
// If we are creating extensions then is_sorted will be NULL.
// If we are not creating extensions then is_sorted will be non-NULL.
if (is_sorted) {
uint32_t previous = 0;
for (int i = 0; i < n; i++) {
upb_FieldDef* f = &defs[i];
_upb_FieldDef_CreateNotExt(ctx, prefix, protos[i], m, f);
f->index_ = i;
if (!ctx->layout) f->layout_index = i;
const uint32_t current = f->number_;
if (previous > current) *is_sorted = false;
previous = current;
}
} else {
for (int i = 0; i < n; i++) {
upb_FieldDef* f = &defs[i];
_upb_FieldDef_CreateExt(ctx, prefix, protos[i], m, f);
f->index_ = i;
}
}
return defs;
}
static void resolve_subdef(upb_DefBuilder* ctx, const char* prefix,
upb_FieldDef* f) {
const google_protobuf_FieldDescriptorProto* field_proto = f->sub.unresolved;
upb_StringView name =
google_protobuf_FieldDescriptorProto_type_name(field_proto);
bool has_name =
google_protobuf_FieldDescriptorProto_has_type_name(field_proto);
switch ((int)f->type_) {
case UPB_FIELD_TYPE_UNSPECIFIED: {
// Type was not specified and must be inferred.
UPB_ASSERT(has_name);
upb_deftype_t type;
const void* def =
_upb_DefBuilder_ResolveAny(ctx, f->full_name, prefix, name, &type);
switch (type) {
case UPB_DEFTYPE_ENUM:
f->sub.enumdef = def;
f->type_ = kUpb_FieldType_Enum;
break;
case UPB_DEFTYPE_MSG:
f->sub.msgdef = def;
f->type_ = kUpb_FieldType_Message; // It appears there is no way of
// this being a group.
break;
default:
_upb_DefBuilder_Errf(ctx, "Couldn't resolve type name for field %s",
f->full_name);
}
}
case kUpb_FieldType_Message:
case kUpb_FieldType_Group:
UPB_ASSERT(has_name);
f->sub.msgdef = _upb_DefBuilder_Resolve(ctx, f->full_name, prefix, name,
UPB_DEFTYPE_MSG);
break;
case kUpb_FieldType_Enum:
UPB_ASSERT(has_name);
f->sub.enumdef = _upb_DefBuilder_Resolve(ctx, f->full_name, prefix, name,
UPB_DEFTYPE_ENUM);
break;
default:
// No resolution necessary.
break;
}
}
static int _upb_FieldDef_Compare(const void* p1, const void* p2) {
const uint32_t v1 = (*(upb_FieldDef**)p1)->number_;
const uint32_t v2 = (*(upb_FieldDef**)p2)->number_;
return (v1 < v2) ? -1 : (v1 > v2);
}
const upb_FieldDef** _upb_FieldDefs_Sorted(const upb_FieldDef* f, int n,
upb_Arena* a) {
// TODO: Try to replace this arena alloc with a persistent scratch buffer.
upb_FieldDef** out = (upb_FieldDef**)upb_Arena_Malloc(a, n * sizeof(void*));
if (!out) return NULL;
for (int i = 0; i < n; i++) {
out[i] = (upb_FieldDef*)&f[i];
}
qsort(out, n, sizeof(void*), _upb_FieldDef_Compare);
for (int i = 0; i < n; i++) {
out[i]->layout_index = i;
}
return (const upb_FieldDef**)out;
}
bool upb_FieldDef_MiniDescriptorEncode(const upb_FieldDef* f, upb_Arena* a,
upb_StringView* out) {
UPB_ASSERT(f->is_extension_);
upb_DescState s;
_upb_DescState_Init(&s);
const int number = upb_FieldDef_Number(f);
const uint64_t modifiers = _upb_FieldDef_Modifiers(f);
if (!_upb_DescState_Grow(&s, a)) return false;
s.ptr = upb_MtDataEncoder_EncodeExtension(&s.e, s.ptr, f->type_, number,
modifiers);
*s.ptr = '\0';
out->data = s.buf;
out->size = s.ptr - s.buf;
return true;
}
static void resolve_extension(upb_DefBuilder* ctx, const char* prefix,
upb_FieldDef* f,
const google_protobuf_FieldDescriptorProto* field_proto) {
if (!google_protobuf_FieldDescriptorProto_has_extendee(field_proto)) {
_upb_DefBuilder_Errf(ctx, "extension for field '%s' had no extendee",
f->full_name);
}
upb_StringView name = google_protobuf_FieldDescriptorProto_extendee(field_proto);
const upb_MessageDef* m =
_upb_DefBuilder_Resolve(ctx, f->full_name, prefix, name, UPB_DEFTYPE_MSG);
f->msgdef = m;
if (!_upb_MessageDef_IsValidExtensionNumber(m, f->number_)) {
_upb_DefBuilder_Errf(
ctx,
"field number %u in extension %s has no extension range in message %s",
(unsigned)f->number_, f->full_name, upb_MessageDef_FullName(m));
}
const upb_MiniTableExtension* ext = _upb_FieldDef_ExtensionMiniTable(f);
if (ctx->layout) {
UPB_ASSERT(upb_FieldDef_Number(f) == ext->field.number);
} else {
upb_StringView desc;
if (!upb_FieldDef_MiniDescriptorEncode(f, ctx->tmp_arena, &desc)) {
_upb_DefBuilder_OomErr(ctx);
}
upb_MiniTableExtension* mut_ext = (upb_MiniTableExtension*)ext;
upb_MiniTableSub sub = {NULL};
if (upb_FieldDef_IsSubMessage(f)) {
sub.submsg = upb_MessageDef_MiniTable(f->sub.msgdef);
} else if (_upb_FieldDef_IsClosedEnum(f)) {
sub.subenum = _upb_EnumDef_MiniTable(f->sub.enumdef);
}
bool ok2 = upb_MiniTableExtension_Build(desc.data, desc.size, mut_ext,
upb_MessageDef_MiniTable(m), sub,
ctx->status);
if (!ok2) _upb_DefBuilder_Errf(ctx, "Could not build extension mini table");
}
bool ok = _upb_DefPool_InsertExt(ctx->symtab, ext, f);
if (!ok) _upb_DefBuilder_OomErr(ctx);
}
static void resolve_default(upb_DefBuilder* ctx, upb_FieldDef* f,
const google_protobuf_FieldDescriptorProto* field_proto) {
// Have to delay resolving of the default value until now because of the enum
// case, since enum defaults are specified with a label.
if (google_protobuf_FieldDescriptorProto_has_default_value(field_proto)) {
upb_StringView defaultval =
google_protobuf_FieldDescriptorProto_default_value(field_proto);
if (upb_FileDef_Syntax(f->file) == kUpb_Syntax_Proto3) {
_upb_DefBuilder_Errf(ctx,
"proto3 fields cannot have explicit defaults (%s)",
f->full_name);
}
if (upb_FieldDef_IsSubMessage(f)) {
_upb_DefBuilder_Errf(ctx,
"message fields cannot have explicit defaults (%s)",
f->full_name);
}
parse_default(ctx, defaultval.data, defaultval.size, f);
f->has_default = true;
} else {
set_default_default(ctx, f);
f->has_default = false;
}
}
void _upb_FieldDef_Resolve(upb_DefBuilder* ctx, const char* prefix,
upb_FieldDef* f) {
// We have to stash this away since resolve_subdef() may overwrite it.
const google_protobuf_FieldDescriptorProto* field_proto = f->sub.unresolved;
resolve_subdef(ctx, prefix, f);
resolve_default(ctx, f, field_proto);
if (f->is_extension_) {
resolve_extension(ctx, prefix, f, field_proto);
}
}