|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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_MSG_INT_H_
|
|
|
|
#define UPB_MSG_INT_H_
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "upb/collections/map_internal.h"
|
|
|
|
#include "upb/extension_registry.h"
|
|
|
|
#include "upb/internal/table.h"
|
|
|
|
#include "upb/msg.h"
|
|
|
|
#include "upb/upb.h"
|
|
|
|
|
|
|
|
// Must be last.
|
|
|
|
#include "upb/port/def.inc"
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
extern const float kUpb_FltInfinity;
|
|
|
|
extern const double kUpb_Infinity;
|
|
|
|
|
|
|
|
/** upb_MiniTable *************************************************************/
|
|
|
|
|
|
|
|
/* upb_MiniTable represents the memory layout of a given upb_MessageDef. The
|
|
|
|
* members are public so generated code can initialize them, but users MUST NOT
|
|
|
|
* read or write any of its members. */
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint32_t number;
|
|
|
|
uint16_t offset;
|
|
|
|
int16_t presence; // If >0, hasbit_index. If <0, ~oneof_index
|
|
|
|
uint16_t submsg_index; // kUpb_NoSub if descriptortype != MESSAGE/GROUP/ENUM
|
|
|
|
uint8_t descriptortype;
|
|
|
|
uint8_t mode; /* upb_FieldMode | upb_LabelFlags |
|
|
|
|
(upb_FieldRep << kUpb_FieldRep_Shift) */
|
|
|
|
} upb_MiniTableField;
|
|
|
|
|
|
|
|
#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,
|
Added function for getting the type of a MiniTable field
Prior to this CL, users were relying on `field->descriptortype` to get the field type. This almost works, as `field->descriptortype` is almost, but not quite, the field type of the field. In two special cases we deviate from the true field type, for ease of parsing and serialization:
- For open enums, we use `kUpb_FieldType_Int32` instead of `kUpb_FieldType_Enum`, because from the perspective of the wire format, an open enum field is equivalent to int32.
- For proto2 strings, we use `kUpb_FieldType_Bytes` instead of `kUpb_FieldType_String`, because proto2 strings do not perform UTF-8 validation, which makes them equivalent to bytes.
In this CL we add a public API function:
```
// Returns the true field type for this field.
upb_FieldType upb_MiniTableField_Type(const upb_MiniTable_Field* f);
```
This will provide the actual field type for this field.
Note that this CL changes the MiniDescriptor format. Previously MiniDescriptors did not contain enough information to distinguish between Enum/Int32. To remedy this we added a new encoded field type, `kUpb_EncodedType_ClosedEnum`.
PiperOrigin-RevId: 479387672
2 years ago
|
|
|
// 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_Shift = 6, // Bit offset of the rep in upb_MiniTableField.mode
|
|
|
|
kUpb_FieldRep_Max = kUpb_FieldRep_8Byte,
|
|
|
|
} upb_FieldRep;
|
|
|
|
|
|
|
|
UPB_INLINE upb_FieldMode upb_FieldMode_Get(const upb_MiniTableField* field) {
|
|
|
|
return (upb_FieldMode)(field->mode & 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool upb_IsRepeatedOrMap(const upb_MiniTableField* field) {
|
|
|
|
/* This works because upb_FieldMode has no value 3. */
|
|
|
|
return !(field->mode & kUpb_FieldMode_Scalar);
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool upb_IsSubMessage(const upb_MiniTableField* field) {
|
|
|
|
return field->descriptortype == kUpb_FieldType_Message ||
|
|
|
|
field->descriptortype == kUpb_FieldType_Group;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct upb_Decoder;
|
|
|
|
struct upb_MiniTable;
|
|
|
|
|
|
|
|
typedef const char* _upb_FieldParser(struct upb_Decoder* d, const char* ptr,
|
|
|
|
upb_Message* msg, intptr_t table,
|
|
|
|
uint64_t hasbits, uint64_t data);
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint64_t field_data;
|
|
|
|
_upb_FieldParser* field_parser;
|
|
|
|
} _upb_FastTable_Entry;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint32_t mask_limit; // Limit enum value that can be tested with mask.
|
|
|
|
uint32_t value_count; // Number of values after the bitfield.
|
|
|
|
uint32_t data[]; // Bitmask + enumerated values follow.
|
|
|
|
} upb_MiniTableEnum;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
_kUpb_FastEnumCheck_ValueIsInEnum = 0,
|
|
|
|
_kUpb_FastEnumCheck_ValueIsNotInEnum = 1,
|
|
|
|
_kUpb_FastEnumCheck_CannotCheckFast = 2,
|
|
|
|
} _kUpb_FastEnumCheck_Status;
|
|
|
|
|
|
|
|
UPB_INLINE _kUpb_FastEnumCheck_Status
|
|
|
|
_upb_MiniTable_CheckEnumValueFast(const upb_MiniTableEnum* e, uint32_t val) {
|
|
|
|
if (UPB_UNLIKELY(val >= 64)) return _kUpb_FastEnumCheck_CannotCheckFast;
|
|
|
|
uint64_t mask = e->data[0] | ((uint64_t)e->data[1] << 32);
|
|
|
|
return (mask & (1ULL << val)) ? _kUpb_FastEnumCheck_ValueIsInEnum
|
|
|
|
: _kUpb_FastEnumCheck_ValueIsNotInEnum;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool _upb_MiniTable_CheckEnumValueSlow(const upb_MiniTableEnum* e,
|
|
|
|
uint32_t val) {
|
|
|
|
if (val < e->mask_limit) return e->data[val / 32] & (1ULL << (val % 32));
|
|
|
|
// OPT: binary search long lists?
|
|
|
|
const uint32_t* start = &e->data[e->mask_limit / 32];
|
|
|
|
const uint32_t* limit = &e->data[(e->mask_limit / 32) + e->value_count];
|
|
|
|
for (const uint32_t* p = start; p < limit; p++) {
|
|
|
|
if (*p == val) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validates enum value against range defined by enum mini table.
|
|
|
|
UPB_INLINE bool upb_MiniTableEnum_CheckValue(const upb_MiniTableEnum* e,
|
|
|
|
uint32_t val) {
|
|
|
|
_kUpb_FastEnumCheck_Status status = _upb_MiniTable_CheckEnumValueFast(e, val);
|
|
|
|
if (UPB_UNLIKELY(status == _kUpb_FastEnumCheck_CannotCheckFast)) {
|
|
|
|
return _upb_MiniTable_CheckEnumValueSlow(e, val);
|
|
|
|
}
|
|
|
|
return status == _kUpb_FastEnumCheck_ValueIsInEnum ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef union {
|
|
|
|
const struct upb_MiniTable* submsg;
|
|
|
|
const upb_MiniTableEnum* subenum;
|
|
|
|
} upb_MiniTableSub;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
kUpb_ExtMode_NonExtendable = 0, // Non-extendable message.
|
|
|
|
kUpb_ExtMode_Extendable = 1, // Normal extendable message.
|
|
|
|
kUpb_ExtMode_IsMessageSet = 2, // MessageSet message.
|
|
|
|
kUpb_ExtMode_IsMessageSet_ITEM =
|
|
|
|
3, // MessageSet item (temporary only, see decode.c)
|
|
|
|
|
|
|
|
// During table building we steal a bit to indicate that the message is a map
|
|
|
|
// entry. *Only* used during table building!
|
|
|
|
kUpb_ExtMode_IsMapEntry = 4,
|
|
|
|
} upb_ExtMode;
|
|
|
|
|
|
|
|
struct upb_MiniTable {
|
|
|
|
const upb_MiniTableSub* subs;
|
|
|
|
const upb_MiniTableField* fields;
|
|
|
|
/* Must be aligned to sizeof(void*). Doesn't include internal members like
|
|
|
|
* unknown fields, extension dict, pointer to msglayout, etc. */
|
|
|
|
uint16_t size;
|
|
|
|
uint16_t field_count;
|
|
|
|
uint8_t ext; // upb_ExtMode, declared as uint8_t so sizeof(ext) == 1
|
Optimized decoder and paved the way for parsing extensions.
The primary motivation for this change is to avoid referring to the
`upb_msglayout` object when we are trying to fetch the `upb_msglayout`
object for a sub-message. This will help pave the way for parsing
extensions. We also implement several optimizations so that we can
make this change without regressing performance.
Normally we compute the layout for a sub-message field like so:
```
const upb_msglayout *get_submsg_layout(
const upb_msglayout *layout,
const upb_msglayout_field *field) {
return layout->submsgs[field->submsg_index]
}
```
The reason for this indirection is to avoid storing a pointer directly
in `upb_msglayout_field`, as this would double its size (from 12 to 24
bytes on 64-bit architectures) which is wasteful as this pointer is
only needed for message typed fields.
However `get_submsg_layout` as written above does not work for
extensions, as they will not have entries in the message's
`layout->submsgs` array by nature, and we want to avoid creating
an entire fake `upb_msglayout` for each such extension since that
would also be wasteful.
This change removes the dependency on `upb_msglayout` by passing down
the `submsgs` array instead:
```
const upb_msglayout *get_submsg_layout(
const upb_msglayout *const *submsgs,
const upb_msglayout_field *field) {
return submsgs[field->submsg_index]
}
```
This will pave the way for parsing extensions, as we can more easily
create an alternative `submsgs` array for extension fields without
extra overhead or waste.
Along the way several optimizations presented themselves that allow
a nice increase in performance:
1. Passing the parsed `wireval` by address instead of by value ended
up avoiding an expensive and useless stack copy (this is on Clang,
which was used for all measurements).
2. When field numbers are densely packed, we can find a field by number
with a single indexed lookup instead of linear search. At codegen
time we can compute the maximum field number that will allow such
an indexed lookup.
3. For fields that do require linear search, we can start the linear
search at the location where we found the previous field, taking
advantage of the fact that field numbers are generally increasing.
4. When the hasbit index is less than 32 (the common case) we can use
a less expensive code sequence to set it.
5. We check for the hasbit case before the oneof case, as optional
fields are more common than oneof fields.
Benchmark results indicate a 20% improvement in parse speed with a
small code size increase:
```
name old time/op new time/op delta
ArenaOneAlloc 21.3ns ± 0% 21.5ns ± 0% +0.96% (p=0.000 n=12+12)
ArenaInitialBlockOneAlloc 6.32ns ± 0% 6.32ns ± 0% +0.03% (p=0.000 n=12+10)
LoadDescriptor_Upb 53.5µs ± 1% 51.5µs ± 2% -3.70% (p=0.000 n=12+12)
LoadAdsDescriptor_Upb 2.78ms ± 2% 2.68ms ± 0% -3.57% (p=0.000 n=12+12)
LoadDescriptor_Proto2 240µs ± 0% 240µs ± 0% +0.12% (p=0.001 n=12+12)
LoadAdsDescriptor_Proto2 12.8ms ± 0% 12.7ms ± 0% -1.15% (p=0.000 n=12+10)
Parse_Upb_FileDesc<UseArena,Copy> 13.2µs ± 2% 10.7µs ± 0% -18.49% (p=0.000 n=10+12)
Parse_Upb_FileDesc<UseArena,Alias> 11.3µs ± 0% 9.6µs ± 0% -15.11% (p=0.000 n=12+11)
Parse_Upb_FileDesc<InitBlock,Copy> 12.7µs ± 0% 10.3µs ± 0% -19.00% (p=0.000 n=10+12)
Parse_Upb_FileDesc<InitBlock,Alias> 10.9µs ± 0% 9.2µs ± 0% -15.82% (p=0.000 n=12+12)
Parse_Proto2<FileDesc,NoArena,Copy> 29.4µs ± 0% 29.5µs ± 0% +0.61% (p=0.000 n=12+12)
Parse_Proto2<FileDesc,UseArena,Copy> 20.7µs ± 2% 20.6µs ± 2% ~ (p=0.260 n=12+11)
Parse_Proto2<FileDesc,InitBlock,Copy> 16.7µs ± 1% 16.7µs ± 0% -0.25% (p=0.036 n=12+10)
Parse_Proto2<FileDescSV,InitBlock,Alias> 16.5µs ± 0% 16.5µs ± 0% +0.20% (p=0.016 n=12+11)
SerializeDescriptor_Proto2 5.30µs ± 1% 5.36µs ± 1% +1.09% (p=0.000 n=12+11)
SerializeDescriptor_Upb 12.9µs ± 0% 13.0µs ± 0% +0.90% (p=0.000 n=12+11)
FILE SIZE VM SIZE
-------------- --------------
+1.5% +176 +1.6% +176 upb/decode.c
+1.8% +176 +1.9% +176 decode_msg
+0.4% +64 +0.4% +64 upb/def.c
+1.4% +64 +1.4% +64 _upb_symtab_addfile
+1.2% +48 +1.4% +48 upb/reflection.c
+15% +32 +18% +32 upb_msg_set
+2.9% +16 +3.1% +16 upb_msg_mutable
-9.3% -288 [ = ] 0 [Unmapped]
[ = ] 0 +0.2% +288 TOTAL
```
4 years ago
|
|
|
uint8_t dense_below;
|
|
|
|
uint8_t table_mask;
|
|
|
|
uint8_t required_count; // Required fields have the lowest hasbits.
|
|
|
|
/* To statically initialize the tables of variable length, we need a flexible
|
|
|
|
* array member, and we need to compile in gnu99 mode (constant initialization
|
|
|
|
* of flexible array members is a GNU extension, not in C99 unfortunately. */
|
|
|
|
_upb_FastTable_Entry fasttable[];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct upb_MiniTableExtension {
|
|
|
|
upb_MiniTableField field;
|
|
|
|
const upb_MiniTable* extendee;
|
|
|
|
upb_MiniTableSub sub; /* NULL unless submessage or proto2 enum */
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
const upb_MiniTable** msgs;
|
|
|
|
const upb_MiniTableEnum** enums;
|
|
|
|
const upb_MiniTableExtension** exts;
|
|
|
|
int msg_count;
|
|
|
|
int enum_count;
|
|
|
|
int ext_count;
|
|
|
|
} upb_MiniTableFile;
|
|
|
|
|
|
|
|
// Computes a bitmask in which the |l->required_count| lowest bits are set,
|
|
|
|
// except that we skip the lowest bit (because upb never uses hasbit 0).
|
|
|
|
//
|
|
|
|
// Sample output:
|
|
|
|
// requiredmask(1) => 0b10 (0x2)
|
|
|
|
// requiredmask(5) => 0b111110 (0x3e)
|
|
|
|
UPB_INLINE uint64_t upb_MiniTable_requiredmask(const upb_MiniTable* l) {
|
|
|
|
int n = l->required_count;
|
|
|
|
assert(0 < n && n <= 63);
|
|
|
|
return ((1ULL << n) - 1) << 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** upb_Message ***************************************************************/
|
|
|
|
|
|
|
|
/* Internal members of a upb_Message that track unknown fields and/or
|
|
|
|
* extensions. We can change this without breaking binary compatibility. We put
|
|
|
|
* these before the user's data. The user's upb_Message* points after the
|
|
|
|
* upb_Message_Internal. */
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
/* Total size of this structure, including the data that follows.
|
|
|
|
* Must be aligned to 8, which is alignof(upb_Message_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_InternalData)
|
|
|
|
*
|
|
|
|
* 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_InternalData)]; */
|
|
|
|
} upb_Message_InternalData;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
upb_Message_InternalData* internal;
|
|
|
|
/* Message data follows. */
|
|
|
|
} upb_Message_Internal;
|
|
|
|
|
|
|
|
/* Maps upb_CType -> memory size. */
|
|
|
|
extern char _upb_CTypeo_size[12];
|
|
|
|
|
|
|
|
UPB_INLINE size_t upb_msg_sizeof(const upb_MiniTable* l) {
|
|
|
|
return l->size + sizeof(upb_Message_Internal);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Inline version upb_Message_New(), for internal use */
|
|
|
|
UPB_INLINE upb_Message* _upb_Message_New(const upb_MiniTable* mini_table,
|
|
|
|
upb_Arena* arena) {
|
|
|
|
size_t size = upb_msg_sizeof(mini_table);
|
|
|
|
void* mem = upb_Arena_Malloc(arena, size + sizeof(upb_Message_Internal));
|
|
|
|
if (UPB_UNLIKELY(!mem)) return NULL;
|
|
|
|
upb_Message* msg = UPB_PTR_AT(mem, sizeof(upb_Message_Internal), upb_Message);
|
|
|
|
memset(mem, 0, size);
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE upb_Message_Internal* upb_Message_Getinternal(upb_Message* msg) {
|
|
|
|
ptrdiff_t size = sizeof(upb_Message_Internal);
|
|
|
|
return (upb_Message_Internal*)((char*)msg - size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clears the given message. */
|
|
|
|
void _upb_Message_Clear(upb_Message* msg, const upb_MiniTable* l);
|
|
|
|
|
|
|
|
/* Discards the unknown fields for this message only. */
|
|
|
|
void _upb_Message_DiscardUnknown_shallow(upb_Message* msg);
|
|
|
|
|
|
|
|
/* Adds unknown data (serialized protobuf data) to the given message. The data
|
|
|
|
* is copied into the message instance. */
|
|
|
|
bool _upb_Message_AddUnknown(upb_Message* msg, const char* data, size_t len,
|
|
|
|
upb_Arena* arena);
|
|
|
|
|
|
|
|
/** upb_Message_Extension *****************************************************/
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
union {
|
|
|
|
upb_StringView str;
|
|
|
|
void* ptr;
|
|
|
|
char scalar_data[8];
|
|
|
|
} data;
|
|
|
|
} upb_Message_Extension;
|
|
|
|
|
|
|
|
/* 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_Message_Extension* _upb_Message_GetOrCreateExtension(
|
|
|
|
upb_Message* msg, const upb_MiniTableExtension* ext, upb_Arena* arena);
|
|
|
|
|
|
|
|
/* Returns an array of extensions for this message. Note: the array is
|
|
|
|
* ordered in reverse relative to the order of creation. */
|
|
|
|
const upb_Message_Extension* _upb_Message_Getexts(const upb_Message* msg,
|
|
|
|
size_t* count);
|
|
|
|
|
|
|
|
/* Returns an extension for the given field number, or NULL if no extension
|
|
|
|
* exists for this field number. */
|
|
|
|
const upb_Message_Extension* _upb_Message_Getext(
|
|
|
|
const upb_Message* msg, const upb_MiniTableExtension* ext);
|
|
|
|
|
|
|
|
void _upb_Message_Clearext(upb_Message* msg, const upb_MiniTableExtension* ext);
|
|
|
|
|
|
|
|
// LINT.IfChange(presence_logic)
|
|
|
|
|
|
|
|
/** Hasbit access *************************************************************/
|
|
|
|
|
|
|
|
UPB_INLINE size_t _upb_hasbit_ofs(size_t idx) { return idx / 8; }
|
|
|
|
|
|
|
|
UPB_INLINE char _upb_hasbit_mask(size_t idx) { return 1 << (idx % 8); }
|
|
|
|
|
|
|
|
UPB_INLINE bool _upb_hasbit(const upb_Message* msg, size_t idx) {
|
|
|
|
return (*UPB_PTR_AT(msg, _upb_hasbit_ofs(idx), const char) &
|
|
|
|
_upb_hasbit_mask(idx)) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE void _upb_sethas(const upb_Message* msg, size_t idx) {
|
|
|
|
(*UPB_PTR_AT(msg, _upb_hasbit_ofs(idx), char)) |= _upb_hasbit_mask(idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE void _upb_clearhas(const upb_Message* msg, size_t idx) {
|
|
|
|
(*UPB_PTR_AT(msg, _upb_hasbit_ofs(idx), char)) &= ~_upb_hasbit_mask(idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE size_t _upb_Message_Hasidx(const upb_MiniTableField* f) {
|
|
|
|
UPB_ASSERT(f->presence > 0);
|
|
|
|
return f->presence;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool _upb_hasbit_field(const upb_Message* msg,
|
|
|
|
const upb_MiniTableField* f) {
|
|
|
|
return _upb_hasbit(msg, _upb_Message_Hasidx(f));
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE void _upb_sethas_field(const upb_Message* msg,
|
|
|
|
const upb_MiniTableField* f) {
|
|
|
|
_upb_sethas(msg, _upb_Message_Hasidx(f));
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE void _upb_clearhas_field(const upb_Message* msg,
|
|
|
|
const upb_MiniTableField* f) {
|
|
|
|
_upb_clearhas(msg, _upb_Message_Hasidx(f));
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Oneof case access *********************************************************/
|
|
|
|
|
|
|
|
UPB_INLINE uint32_t* _upb_oneofcase(upb_Message* msg, size_t case_ofs) {
|
|
|
|
return UPB_PTR_AT(msg, case_ofs, uint32_t);
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE uint32_t _upb_getoneofcase(const void* msg, size_t case_ofs) {
|
|
|
|
return *UPB_PTR_AT(msg, case_ofs, uint32_t);
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE size_t _upb_oneofcase_ofs(const upb_MiniTableField* f) {
|
|
|
|
UPB_ASSERT(f->presence < 0);
|
|
|
|
return ~(ptrdiff_t)f->presence;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE uint32_t* _upb_oneofcase_field(upb_Message* msg,
|
|
|
|
const upb_MiniTableField* f) {
|
|
|
|
return _upb_oneofcase(msg, _upb_oneofcase_ofs(f));
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE uint32_t _upb_getoneofcase_field(const upb_Message* msg,
|
|
|
|
const upb_MiniTableField* f) {
|
|
|
|
return _upb_getoneofcase(msg, _upb_oneofcase_ofs(f));
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE bool _upb_has_submsg_nohasbit(const upb_Message* msg, size_t ofs) {
|
|
|
|
return *UPB_PTR_AT(msg, ofs, const upb_Message*) != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// LINT.ThenChange(GoogleInternalName2)
|
|
|
|
|
|
|
|
/* Map entries aren't actually stored, they are only used during parsing. For
|
|
|
|
* parsing, it helps a lot if all map entry messages have the same layout.
|
|
|
|
* The compiler and def.c must ensure that all map entries have this layout. */
|
|
|
|
typedef struct {
|
|
|
|
upb_Message_Internal internal;
|
|
|
|
union {
|
|
|
|
upb_StringView str; /* For str/bytes. */
|
|
|
|
upb_value val; /* For all other types. */
|
|
|
|
} k;
|
|
|
|
union {
|
|
|
|
upb_StringView str; /* For str/bytes. */
|
|
|
|
upb_value val; /* For all other types. */
|
|
|
|
} v;
|
|
|
|
} upb_MapEntry;
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
} /* extern "C" */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "upb/port/undef.inc"
|
|
|
|
|
|
|
|
#endif /* UPB_MSG_INT_H_ */
|