|
|
|
// Protocol Buffers - Google's data interchange format
|
|
|
|
// Copyright 2023 Google LLC. All rights reserved.
|
|
|
|
//
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file or at
|
|
|
|
// https://developers.google.com/open-source/licenses/bsd
|
|
|
|
|
|
|
|
#ifndef UPB_MINI_TABLE_INTERNAL_FIELD_H_
|
|
|
|
#define UPB_MINI_TABLE_INTERNAL_FIELD_H_
|
|
|
|
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
#include "upb/base/descriptor_constants.h"
|
|
|
|
#include "upb/mini_table/internal/size_log2.h"
|
|
|
|
|
|
|
|
// Must be last.
|
|
|
|
#include "upb/port/def.inc"
|
|
|
|
|
|
|
|
struct upb_MiniTableField {
|
|
|
|
uint32_t number;
|
|
|
|
uint16_t 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
|
|
|
|
uint16_t UPB_PRIVATE(submsg_index);
|
|
|
|
|
|
|
|
uint8_t UPB_PRIVATE(descriptortype);
|
|
|
|
|
|
|
|
// upb_FieldMode | upb_LabelFlags | (upb_FieldRep << kUpb_FieldRep_Shift)
|
|
|
|
uint8_t mode;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define kUpb_NoSub ((uint16_t)-1)
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
kUpb_FieldMode_Map = 0,
|
|
|
|
kUpb_FieldMode_Array = 1,
|
|
|
|
kUpb_FieldMode_Scalar = 2,
|
|
|
|
} upb_FieldMode;
|
|
|
|
|
|
|
|
// Mask to isolate the upb_FieldMode from field.mode.
|
|
|
|
#define kUpb_FieldMode_Mask 3
|
|
|
|
|
|
|
|
// Extra flags on the mode field.
|
|
|
|
typedef enum {
|
|
|
|
kUpb_LabelFlags_IsPacked = 4,
|
|
|
|
kUpb_LabelFlags_IsExtension = 8,
|
|
|
|
// Indicates that this descriptor type is an "alternate type":
|
|
|
|
// - for Int32, this indicates that the actual type is Enum (but was
|
|
|
|
// rewritten to Int32 because it is an open enum that requires no check).
|
|
|
|
// - for Bytes, this indicates that the actual type is String (but does
|
|
|
|
// not require any UTF-8 check).
|
|
|
|
kUpb_LabelFlags_IsAlternate = 16,
|
|
|
|
} upb_LabelFlags;
|
|
|
|
|
|
|
|
// Note: we sort by this number when calculating layout order.
|
|
|
|
typedef enum {
|
|
|
|
kUpb_FieldRep_1Byte = 0,
|
|
|
|
kUpb_FieldRep_4Byte = 1,
|
|
|
|
kUpb_FieldRep_StringView = 2,
|
|
|
|
kUpb_FieldRep_8Byte = 3,
|
|
|
|
|
|
|
|
kUpb_FieldRep_NativePointer =
|
|
|
|
UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte),
|
|
|
|
kUpb_FieldRep_Max = kUpb_FieldRep_8Byte,
|
|
|
|
} upb_FieldRep;
|
|
|
|
|
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
|
|
|
#define kUpb_FieldRep_Shift 6
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
UPB_INLINE upb_FieldMode
|
|
|
|
UPB_PRIVATE(_upb_MiniTableField_Mode)(const struct upb_MiniTableField* f) {
|
|
|
|
return (upb_FieldMode)(f->mode & kUpb_FieldMode_Mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE upb_FieldRep
|
|
|
|
UPB_PRIVATE(_upb_MiniTableField_GetRep)(const struct upb_MiniTableField* f) {
|
|
|
|
return (upb_FieldRep)(f->mode >> kUpb_FieldRep_Shift);
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool UPB_PRIVATE(_upb_MiniTableField_IsArray)(
|
|
|
|
const struct upb_MiniTableField* f) {
|
|
|
|
return (f->mode & kUpb_FieldMode_Mask) == kUpb_FieldMode_Array;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool UPB_PRIVATE(_upb_MiniTableField_IsMap)(
|
|
|
|
const struct upb_MiniTableField* f) {
|
|
|
|
return (f->mode & kUpb_FieldMode_Mask) == kUpb_FieldMode_Map;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool UPB_PRIVATE(_upb_MiniTableField_IsScalar)(
|
|
|
|
const struct upb_MiniTableField* f) {
|
|
|
|
return (f->mode & kUpb_FieldMode_Mask) == kUpb_FieldMode_Scalar;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool UPB_PRIVATE(_upb_MiniTableField_IsAlternate)(
|
|
|
|
const struct upb_MiniTableField* f) {
|
|
|
|
return (f->mode & kUpb_LabelFlags_IsAlternate) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool UPB_PRIVATE(_upb_MiniTableField_IsExtension)(
|
|
|
|
const struct upb_MiniTableField* f) {
|
|
|
|
return (f->mode & kUpb_LabelFlags_IsExtension) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool UPB_PRIVATE(_upb_MiniTableField_IsPacked)(
|
|
|
|
const struct upb_MiniTableField* f) {
|
|
|
|
return (f->mode & kUpb_LabelFlags_IsPacked) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE upb_FieldType
|
|
|
|
UPB_PRIVATE(_upb_MiniTableField_Type)(const struct upb_MiniTableField* f) {
|
|
|
|
const upb_FieldType type = (upb_FieldType)f->UPB_PRIVATE(descriptortype);
|
|
|
|
if (UPB_PRIVATE(_upb_MiniTableField_IsAlternate)(f)) {
|
|
|
|
if (type == kUpb_FieldType_Int32) return kUpb_FieldType_Enum;
|
|
|
|
if (type == kUpb_FieldType_Bytes) return kUpb_FieldType_String;
|
|
|
|
UPB_ASSERT(false);
|
|
|
|
}
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE upb_CType
|
|
|
|
UPB_PRIVATE(_upb_MiniTableField_CType)(const struct upb_MiniTableField* f) {
|
|
|
|
return upb_FieldType_CType(UPB_PRIVATE(_upb_MiniTableField_Type)(f));
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool UPB_PRIVATE(_upb_MiniTableField_IsClosedEnum)(
|
|
|
|
const struct upb_MiniTableField* f) {
|
|
|
|
return f->UPB_PRIVATE(descriptortype) == kUpb_FieldType_Enum;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool UPB_PRIVATE(_upb_MiniTableField_IsInOneof)(
|
|
|
|
const struct upb_MiniTableField* f) {
|
|
|
|
return f->presence < 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool UPB_PRIVATE(_upb_MiniTableField_IsSubMessage)(
|
|
|
|
const struct upb_MiniTableField* f) {
|
|
|
|
return f->UPB_PRIVATE(descriptortype) == kUpb_FieldType_Message ||
|
|
|
|
f->UPB_PRIVATE(descriptortype) == kUpb_FieldType_Group;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool UPB_PRIVATE(_upb_MiniTableField_HasPresence)(
|
|
|
|
const struct upb_MiniTableField* f) {
|
|
|
|
if (UPB_PRIVATE(_upb_MiniTableField_IsExtension)(f)) {
|
|
|
|
return UPB_PRIVATE(_upb_MiniTableField_IsScalar)(f);
|
|
|
|
} else {
|
|
|
|
return f->presence != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE void UPB_PRIVATE(_upb_MiniTableField_CheckIsArray)(
|
|
|
|
const struct upb_MiniTableField* f) {
|
|
|
|
UPB_ASSUME(UPB_PRIVATE(_upb_MiniTableField_GetRep)(f) ==
|
|
|
|
kUpb_FieldRep_NativePointer);
|
|
|
|
UPB_ASSUME(UPB_PRIVATE(_upb_MiniTableField_IsArray)(f));
|
|
|
|
UPB_ASSUME(f->presence == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE void UPB_PRIVATE(_upb_MiniTableField_CheckIsMap)(
|
|
|
|
const struct upb_MiniTableField* f) {
|
|
|
|
UPB_ASSUME(UPB_PRIVATE(_upb_MiniTableField_GetRep)(f) ==
|
|
|
|
kUpb_FieldRep_NativePointer);
|
|
|
|
UPB_ASSUME(UPB_PRIVATE(_upb_MiniTableField_IsMap)(f));
|
|
|
|
UPB_ASSUME(f->presence == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE size_t UPB_PRIVATE(_upb_MiniTableField_ElemSizeLg2)(
|
|
|
|
const struct upb_MiniTableField* f) {
|
|
|
|
return upb_FieldType_SizeLg2((upb_FieldType)f->UPB_PRIVATE(descriptortype));
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
} /* extern "C" */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "upb/port/undef.inc"
|
|
|
|
|
|
|
|
#endif /* UPB_MINI_TABLE_INTERNAL_FIELD_H_ */
|