|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* upb_table
|
|
|
|
*
|
|
|
|
* This header is INTERNAL-ONLY! Its interfaces are not public or stable!
|
|
|
|
* This file defines very fast int->upb_value (inttable) and string->upb_value
|
|
|
|
* (strtable) hash tables.
|
|
|
|
*
|
|
|
|
* The table uses chained scatter with Brent's variation (inspired by the Lua
|
|
|
|
* implementation of hash tables). The hash function for strings is Austin
|
|
|
|
* Appleby's "MurmurHash."
|
|
|
|
*
|
|
|
|
* The inttable uses uintptr_t as its key, which guarantees it can be used to
|
|
|
|
* store pointers or integers of at least 32 bits (upb isn't really useful on
|
|
|
|
* systems where sizeof(void*) < 4).
|
|
|
|
*
|
|
|
|
* The table must be homogeneous (all values of the same type). In debug
|
|
|
|
* mode, we check this on insert and lookup.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef UPB_HASH_COMMON_H_
|
|
|
|
#define UPB_HASH_COMMON_H_
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "upb/base/string_view.h"
|
|
|
|
#include "upb/mem/arena.h"
|
|
|
|
|
|
|
|
// Must be last.
|
|
|
|
#include "upb/port/def.inc"
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* upb_value ******************************************************************/
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint64_t val;
|
|
|
|
} upb_value;
|
|
|
|
|
|
|
|
/* Variant that works with a length-delimited rather than NULL-delimited string,
|
|
|
|
* as supported by strtable. */
|
|
|
|
char* upb_strdup2(const char* s, size_t len, upb_Arena* a);
|
|
|
|
|
|
|
|
UPB_INLINE void _upb_value_setval(upb_value* v, uint64_t val) { v->val = val; }
|
|
|
|
|
|
|
|
/* For each value ctype, define the following set of functions:
|
|
|
|
*
|
|
|
|
* // Get/set an int32 from a upb_value.
|
|
|
|
* int32_t upb_value_getint32(upb_value val);
|
|
|
|
* void upb_value_setint32(upb_value *val, int32_t cval);
|
|
|
|
*
|
|
|
|
* // Construct a new upb_value from an int32.
|
|
|
|
* upb_value upb_value_int32(int32_t val); */
|
|
|
|
#define FUNCS(name, membername, type_t, converter, proto_type) \
|
|
|
|
UPB_INLINE void upb_value_set##name(upb_value* val, type_t cval) { \
|
|
|
|
val->val = (converter)cval; \
|
|
|
|
} \
|
|
|
|
UPB_INLINE upb_value upb_value_##name(type_t val) { \
|
|
|
|
upb_value ret; \
|
|
|
|
upb_value_set##name(&ret, val); \
|
|
|
|
return ret; \
|
|
|
|
} \
|
|
|
|
UPB_INLINE type_t upb_value_get##name(upb_value val) { \
|
|
|
|
return (type_t)(converter)val.val; \
|
|
|
|
}
|
|
|
|
|
|
|
|
FUNCS(int32, int32, int32_t, int32_t, UPB_CTYPE_INT32)
|
|
|
|
FUNCS(int64, int64, int64_t, int64_t, UPB_CTYPE_INT64)
|
|
|
|
FUNCS(uint32, uint32, uint32_t, uint32_t, UPB_CTYPE_UINT32)
|
|
|
|
FUNCS(uint64, uint64, uint64_t, uint64_t, UPB_CTYPE_UINT64)
|
|
|
|
FUNCS(bool, _bool, bool, bool, UPB_CTYPE_BOOL)
|
|
|
|
FUNCS(cstr, cstr, char*, uintptr_t, UPB_CTYPE_CSTR)
|
Added a new dynamic tree shaking model to upb, with the intention of removing the old model once YouTube has migrated.
The `kUpb_DecodeOption_ExperimentalAllowUnlinked` flag to the decoder will enable the new behavior. When that flag is not passed, tree shaking with the old model will still be possible.
"Dynamic tree shaking" in upb is a feature that allows messages to be parsed even if the MiniTables have not been fully linked. Unlinked sub-message fields can be parsed by preserving their data in the unknown fields. If the application later discovers that the message field is actually needed, the MiniTable can be patched to properly link that field, and existing message instances can "promote" the data from the unknown fields to an actual message of the correct type.
Before this change, dynamic tree shaking stored unparsed message data in the unknown fields of the *parent*. In effect, we were treating the field as if it did not exist at all. This meant that parsing an unlinked field did not affect the hasbits or oneof cases of the parent, nor did it create a `upb_Array` or `upb_Map` for array/map fields. Only when a message was linked and promoted did any of these things occur.
While this model had some amount of conceptual simplicity, it caused significant problems with oneofs. When multiple fields inside a single oneof are parsed from the wire, order matters, because later oneof fields must overwrite earlier ones. Dynamic tree shaking can mean that some fields in a oneof are linked while others are not. It is essential that we preserve this ordering semantic even when dynamic tree shaking is being used, but it is difficult to do if the oneof's data can be split between linked fields (which have been reified into parsed field data) and unlinked fields (whose data lives in the unknown fields of the parent).
To solve this problem, this CL changes the representation for unlinked fields. Instead of being placed in the parent's unknown fields, we create an actual message instance for each unlinked message we parse, but we use a placeholder "empty message" MiniTable as the message's type. All of the message's data will therefore be placed into the "empty message's" unknown fields. But unlike before, this "empty message" is actually present according to the hasbits, oneof case, and `upb_Array`/`upb_Map` of the parent. This means that all of the oneof presence logic works as normal.
Since the MiniTable can be patched at any time, we need a bit in the message instance itself to signal whether a pointer to a sub-message is an "empty message" or not. When dynamic tree shaking is in use, all users must be capable of recognizing an empty message and acting accordingly (promoting, etc) even if the MiniTable itself says that the field is linked.
Because dynamic tree shaking imposes this extra requirement on users, we require that users pass an extra option to the decoder to allow parsing of unlinked sub-messages. Many existing users of upb (Ruby, PHP, Python, etc) will always have fully-linked MiniTables, so there is no reason for them to add extra logic to handle empty messages. By omitting the `kUpb_DecodeOption_ExperimentalAllowUnlinked` option, they will be relieved of the duty to check the tagged pointer that would indicate an empty, unlinked message.
For existing users of dynamic tree shaking, there are three main changes:
1. The APIs in message/promote.h have changed, and users will need to update to the new interfaces.
2. The model for maps has changed slightly. Before, we required that map entries always had their values linked; for dynamic tree shaking to apply to maps, we required that the *entry* was left unlinked, not the entry's value. In the new model, that is reversed: map entries must always be linked, but a map entry's value can be unlinked.
3. The presence model for unlinked fields has changed. Unlinked fields will now register as "present" from the perspective of hasbits, oneof cases, and array/map entries. Users must test the tagged pointer to know if a message is of the correct, linked type or whether it is a placeholder "empty" message. There is a new function `upb_Message_GetTaggedMessagePtr()`, as well as a new accessor `upb_MessageValue.tagged_msg_val` that can be used to read and test the tagged pointer directly.
PiperOrigin-RevId: 535288031
2 years ago
|
|
|
FUNCS(uintptr, uptr, uintptr_t, uintptr_t, UPB_CTYPE_UPTR)
|
|
|
|
FUNCS(ptr, ptr, void*, uintptr_t, UPB_CTYPE_PTR)
|
|
|
|
FUNCS(constptr, constptr, const void*, uintptr_t, UPB_CTYPE_CONSTPTR)
|
|
|
|
|
|
|
|
#undef FUNCS
|
|
|
|
|
|
|
|
UPB_INLINE void upb_value_setfloat(upb_value* val, float cval) {
|
|
|
|
memcpy(&val->val, &cval, sizeof(cval));
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE void upb_value_setdouble(upb_value* val, double cval) {
|
|
|
|
memcpy(&val->val, &cval, sizeof(cval));
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE upb_value upb_value_float(float cval) {
|
|
|
|
upb_value ret;
|
|
|
|
upb_value_setfloat(&ret, cval);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE upb_value upb_value_double(double cval) {
|
|
|
|
upb_value ret;
|
|
|
|
upb_value_setdouble(&ret, cval);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef SET_TYPE
|
|
|
|
|
|
|
|
/* upb_tabkey *****************************************************************/
|
|
|
|
|
|
|
|
/* Either:
|
|
|
|
* 1. an actual integer key, or
|
|
|
|
* 2. a pointer to a string prefixed by its uint32_t length, owned by us.
|
|
|
|
*
|
|
|
|
* ...depending on whether this is a string table or an int table. We would
|
|
|
|
* make this a union of those two types, but C89 doesn't support statically
|
|
|
|
* initializing a non-first union member. */
|
|
|
|
typedef uintptr_t upb_tabkey;
|
|
|
|
|
|
|
|
UPB_INLINE char* upb_tabstr(upb_tabkey key, uint32_t* len) {
|
|
|
|
char* mem = (char*)key;
|
|
|
|
if (len) memcpy(len, mem, sizeof(*len));
|
|
|
|
return mem + sizeof(*len);
|
|
|
|
}
|
|
|
|
|
|
|
|
UPB_INLINE upb_StringView upb_tabstrview(upb_tabkey key) {
|
|
|
|
upb_StringView ret;
|
|
|
|
uint32_t len;
|
|
|
|
ret.data = upb_tabstr(key, &len);
|
|
|
|
ret.size = len;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* upb_tabval *****************************************************************/
|
|
|
|
|
|
|
|
typedef struct upb_tabval {
|
|
|
|
uint64_t val;
|
|
|
|
} upb_tabval;
|
|
|
|
|
|
|
|
#define UPB_TABVALUE_EMPTY_INIT \
|
|
|
|
{ -1 }
|
|
|
|
|
|
|
|
/* upb_table ******************************************************************/
|
|
|
|
|
|
|
|
typedef struct _upb_tabent {
|
|
|
|
upb_tabkey key;
|
|
|
|
upb_tabval val;
|
|
|
|
|
|
|
|
/* Internal chaining. This is const so we can create static initializers for
|
|
|
|
* tables. We cast away const sometimes, but *only* when the containing
|
|
|
|
* upb_table is known to be non-const. This requires a bit of care, but
|
|
|
|
* the subtlety is confined to table.c. */
|
|
|
|
const struct _upb_tabent* next;
|
|
|
|
} upb_tabent;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
size_t count; /* Number of entries in the hash part. */
|
|
|
|
uint32_t mask; /* Mask to turn hash value -> bucket. */
|
|
|
|
uint32_t max_count; /* Max count before we hit our load limit. */
|
|
|
|
uint8_t size_lg2; /* Size of the hashtable part is 2^size_lg2 entries. */
|
|
|
|
upb_tabent* entries;
|
|
|
|
} upb_table;
|
|
|
|
|
|
|
|
UPB_INLINE size_t upb_table_size(const upb_table* t) {
|
|
|
|
return t->size_lg2 ? 1 << t->size_lg2 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Internal-only functions, in .h file only out of necessity.
|
|
|
|
|
|
|
|
UPB_INLINE bool upb_tabent_isempty(const upb_tabent* e) { return e->key == 0; }
|
|
|
|
|
|
|
|
uint32_t _upb_Hash(const void* p, size_t n, uint64_t seed);
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
} /* extern "C" */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "upb/port/undef.inc"
|
|
|
|
|
|
|
|
#endif /* UPB_HASH_COMMON_H_ */
|