|
|
|
// 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
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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 <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "upb/mem/arena.h"
|
|
|
|
#include "upb/message/internal/extension.h"
|
|
|
|
#include "upb/mini_table/message.h"
|
|
|
|
|
|
|
|
// Must be last.
|
|
|
|
#include "upb/port/def.inc"
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
extern const float kUpb_FltInfinity;
|
|
|
|
extern const double kUpb_Infinity;
|
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
|
|
|
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
|
|
|
|
// must represent one or more complete and well formed proto fields.
|
|
|
|
// 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 alias);
|
|
|
|
|
|
|
|
// Adds unknown data (serialized protobuf data) to the given message.
|
|
|
|
// The data is copied into the message instance. Data when concatenated together
|
|
|
|
// must represent one or more complete and well formed proto fields, but the
|
|
|
|
// individual spans may point only to partial fields.
|
|
|
|
bool UPB_PRIVATE(_upb_Message_AddUnknownV)(struct upb_Message* msg,
|
|
|
|
upb_Arena* arena,
|
|
|
|
upb_StringView data[], size_t count);
|
|
|
|
|
|
|
|
bool UPB_PRIVATE(_upb_Message_Realloc)(struct upb_Message* msg, size_t need,
|
|
|
|
upb_Arena* arena);
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
} /* extern "C" */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "upb/port/undef.inc"
|
|
|
|
|
|
|
|
#endif /* UPB_MESSAGE_INTERNAL_MESSAGE_H_ */
|