blob: 684a21ae35b1fd79ed1d9099a64504d584e80918 [file]
// Protocol Buffers - Google's data interchange format
// Copyright 2026 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
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "upb/base/string_view.h"
#include "upb/hash/common.h"
#include "upb/message/internal/map.h"
#include "upb/message/internal/message.h"
#include "upb/message/map.h"
#include "upb/message/message.h"
#include "upb/mini_table/field.h"
#include "upb/mini_table/internal/message.h"
#include "upb/mini_table/internal/sub.h"
#include "upb/mini_table/message.h"
#include "upb/wire/decode.h"
#include "upb/wire/decode_fast/cardinality.h"
#include "upb/wire/decode_fast/combinations.h"
#include "upb/wire/decode_fast/data.h"
#include "upb/wire/decode_fast/dispatch.h"
#include "upb/wire/decode_fast/field_parsers.h"
#include "upb/wire/eps_copy_input_stream.h"
#include "upb/wire/internal/decoder.h"
#include "upb/wire/internal/eps_copy_input_stream.h"
#include "upb/wire/reader.h"
#include "upb/wire/types.h"
#include "utf8_range.h"
// Must be last.
#include "upb/port/def.inc"
typedef struct {
upb_Map* map;
const upb_MiniTable* sub_table;
} upb_DecodeFastMap;
// Resolves and initializes the map field on the target message.
// Traverses the MiniTable metadata only when the map value is a submessage,
// creates the upb_Map if not yet allocated, and synchronizes hasbits.
UPB_FORCEINLINE
bool upb_DecodeFast_GetMap(upb_Decoder* d, upb_Message* msg,
const upb_MiniTable* table, uint64_t data,
bool is_str_map, uint64_t* hasbits,
upb_DecodeFastMap* map_ctx,
upb_DecodeFastNext* next) {
// Sync hasbits so we don't have to preserve them across the map entries.
upb_DecodeFast_SetHasbits(msg, *hasbits);
*hasbits = 0;
const upb_MiniTable* sub_table = NULL;
if (upb_DecodeFastData_ValIsMessage(data)) {
uint32_t submsg_ofs = upb_DecodeFastData_GetSubofs(data) * 8;
const upb_MiniTableSubInternal* sub = UPB_PTR_AT(
table->UPB_ONLYBITS(fields), submsg_ofs, upb_MiniTableSubInternal);
const upb_MiniTable* entry_table = sub->UPB_PRIVATE(submsg);
// Map entries are synthetic messages co-generated with the parent message
// in the same compilation unit, so they are never tree-shaken independently
// of the parent message, and are strictly validated to have exactly 2
// fields (field 1 = key, field 2 = value). Unlinked dynamic map tables are
// filtered out prior to fast decode dispatch.
UPB_ASSERT(entry_table);
UPB_ASSERT(entry_table->UPB_PRIVATE(field_count) == 2);
const upb_MiniTableField* val_field = &entry_table->UPB_PRIVATE(fields)[1];
sub_table = upb_MiniTable_GetSubMessageTable(val_field);
if (UPB_UNLIKELY(!sub_table)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable, next);
}
}
uint16_t offset = upb_DecodeFastData_GetOffset(data);
upb_Map** map_p = UPB_PTR_AT(msg, offset, upb_Map*);
upb_Map* map = *map_p;
if (UPB_UNLIKELY(!map)) {
size_t key_size = is_str_map ? UPB_MAPTYPE_STRING
: (upb_DecodeFastData_KeyIs32(data) ? 4 : 8);
size_t val_size = upb_DecodeFastData_GetValSize(data);
map = _upb_Map_New(&d->arena, key_size, val_size);
if (UPB_UNLIKELY(!map)) {
return UPB_DECODEFAST_ERROR(d, kUpb_DecodeStatus_OutOfMemory, next);
}
*map_p = map;
}
map_ctx->map = map;
map_ctx->sub_table = sub_table;
return true;
}
// Parses a single map entry (key, value) and inserts it directly into upb_Map.
UPB_FORCEINLINE
bool upb_DecodeFast_ParseMapEntry(upb_Decoder* d, const char** ptr,
const upb_DecodeFastMap* map_ctx,
uint64_t data, bool is_str_map,
upb_DecodeFastNext* next) {
UPB_ASSERT(map_ctx->map->UPB_PRIVATE(is_strtable) == is_str_map);
const char* p = *ptr;
int size;
if (UPB_UNLIKELY(!upb_DecodeFast_DecodeSize(d, &p, &size, next))) {
return false;
}
if (UPB_UNLIKELY(
!upb_EpsCopyInputStream_SizeFitsWithoutSwap(EPS(d), p, size))) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable, next);
}
const char* entry_end = p + size;
if (UPB_UNLIKELY(p >= entry_end)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable, next);
}
// Parse Key (Field 1)
UPB_PRIVATE(upb_EpsCopyInputStream_ConsumeBytes)(EPS(d), 1);
uint8_t key_tag = *p++;
uint8_t expected_key_tag = upb_DecodeFastData_GetKeyTag(data);
if (is_str_map) {
UPB_ASSUME(expected_key_tag == ((1 << 3) | kUpb_WireType_Delimited));
}
if (UPB_UNLIKELY(key_tag != expected_key_tag)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable, next);
}
UPB_PRIVATE(upb_EpsCopyInputStream_BoundsCheckedToEnd)(EPS(d), p, entry_end);
upb_StringView strkey = {NULL, 0};
uintptr_t intkey = 0;
if (is_str_map) {
int klen;
if (UPB_UNLIKELY(!upb_DecodeFast_DecodeSize(d, &p, &klen, next))) {
return false;
}
if (UPB_UNLIKELY(entry_end - p < klen || klen < 0)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable, next);
}
// Note: We do not need to copy string keys here even when aliasing is
// disabled because upb_strtable_insert() always deep-copies keys into its
// own arena-allocated upb_SizePrefixString.
p = upb_EpsCopyInputStream_ReadStringAlwaysAlias(EPS(d), p, klen, &strkey);
if (UPB_UNLIKELY(p == NULL)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable, next);
}
// Validate the key before looking at the value so that an entry with both
// a bad key and a bad value reports the same error as the MiniTable
// decoder, which validates the key as soon as it reads it.
if (upb_DecodeFastData_KeyValidateUtf8(data) &&
!utf8_range_IsValid(strkey.data, strkey.size)) {
return UPB_DECODEFAST_ERROR(d, kUpb_DecodeStatus_BadUtf8, next);
}
} else {
uint64_t k = 0;
switch (expected_key_tag & 0x7) {
case kUpb_WireType_Varint:
p = upb_WireReader_ReadVarint(p, &k, EPS(d));
if (UPB_UNLIKELY(upb_DecodeFastData_KeyIsZigZag(data))) {
k = upb_DecodeFastData_KeyIs32(data) ? _upb_Decoder_ZigZagDecode32(k)
: _upb_Decoder_ZigZagDecode64(k);
}
break;
case kUpb_WireType_32Bit: {
if (UPB_UNLIKELY(entry_end - p < 4)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable,
next);
}
uint32_t k32;
p = upb_WireReader_ReadFixed32(p, &k32, EPS(d));
k = k32;
break;
}
case kUpb_WireType_64Bit:
if (UPB_UNLIKELY(entry_end - p < 8)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable,
next);
}
p = upb_WireReader_ReadFixed64(p, &k, EPS(d));
break;
default:
// upb_DecodeFast_TryFillMapEntry() only selects the int map parsers for
// key types that use one of the wire types above.
UPB_UNREACHABLE();
}
if (upb_DecodeFastData_KeyIs32(data)) {
intkey = (uint32_t)k;
} else {
UPB_ASSERT(upb_DecodeFastData_GetKeySize(data) == 8);
intkey = (uintptr_t)k;
}
}
if (UPB_UNLIKELY(p >= entry_end)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable, next);
}
// Parse Value (Field 2)
UPB_PRIVATE(upb_EpsCopyInputStream_BoundsCheckedToEnd)(EPS(d), p, entry_end);
UPB_PRIVATE(upb_EpsCopyInputStream_ConsumeBytes)(EPS(d), 1);
uint8_t val_tag = *p++;
upb_value tabval = {0};
uint8_t expected_val_tag = upb_DecodeFastData_GetValTag(data);
uint8_t expected_val_wire_type = expected_val_tag & 0x7;
// Fasttable won't contain entries for anything but these
UPB_ASSERT(expected_val_wire_type == kUpb_WireType_Varint ||
expected_val_wire_type == kUpb_WireType_32Bit ||
expected_val_wire_type == kUpb_WireType_64Bit ||
expected_val_wire_type == kUpb_WireType_Delimited);
if (UPB_UNLIKELY(val_tag != expected_val_tag)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable, next);
}
// The value must have at least one byte; an empty value is malformed.
if (UPB_UNLIKELY(p >= entry_end)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable, next);
}
UPB_PRIVATE(upb_EpsCopyInputStream_BoundsCheckedToEnd)(EPS(d), p, entry_end);
switch (expected_val_wire_type) {
case kUpb_WireType_Varint: {
uint64_t v = 0;
const char* p_next = upb_WireReader_ReadVarint(p, &v, EPS(d));
if (UPB_UNLIKELY(p_next != entry_end)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable,
next);
}
p = p_next;
if (upb_DecodeFastData_ValIsBool(data)) {
tabval.val = (v != 0);
} else {
if (UPB_UNLIKELY(upb_DecodeFastData_ValIsZigZag(data))) {
v = upb_DecodeFastData_ValIs32(data) ? _upb_Decoder_ZigZagDecode32(v)
: _upb_Decoder_ZigZagDecode64(v);
}
tabval.val = upb_DecodeFastData_ValIs32(data) ? (uint32_t)v : v;
}
break;
}
case kUpb_WireType_64Bit: {
if (UPB_UNLIKELY(entry_end - p != 8)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable,
next);
}
p = upb_WireReader_ReadFixed64(p, &tabval.val, EPS(d));
break;
}
case kUpb_WireType_32Bit: {
if (UPB_UNLIKELY(entry_end - p != 4)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable,
next);
}
uint32_t v32;
p = upb_WireReader_ReadFixed32(p, &v32, EPS(d));
tabval.val = v32;
break;
}
case kUpb_WireType_Delimited: {
int vlen;
if (UPB_UNLIKELY(!upb_DecodeFast_DecodeSize(d, &p, &vlen, next))) {
return false;
}
if (UPB_UNLIKELY(entry_end - p != vlen)) {
return UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable,
next);
}
if (map_ctx->sub_table) {
upb_Message* sub_msg = _upb_Message_New(map_ctx->sub_table, &d->arena);
if (UPB_UNLIKELY(!sub_msg)) {
return UPB_DECODEFAST_ERROR(d, kUpb_DecodeStatus_OutOfMemory, next);
}
upb_DecodeFast_MessageContext ctx = {map_ctx->sub_table, false,
sub_msg};
if (upb_EpsCopyInputStream_TryParseDelimitedFast(
EPS(d), &p, vlen, &upb_DecodeFast_MessageData, &ctx)) {
if (UPB_UNLIKELY(p == NULL)) {
return UPB_DECODEFAST_ERROR(d, kUpb_DecodeStatus_Malformed, next);
}
} else {
ptrdiff_t delta = upb_EpsCopyInputStream_PushLimit(EPS(d), p, vlen);
p = upb_DecodeFast_MessageData(EPS(d), p, vlen, &ctx);
if (UPB_UNLIKELY(p == NULL)) {
return UPB_DECODEFAST_ERROR(d, kUpb_DecodeStatus_Malformed, next);
}
upb_EpsCopyInputStream_PopLimit(EPS(d), p, delta);
}
tabval = upb_value_ptr(sub_msg);
} else {
upb_StringView str_val;
if (UPB_UNLIKELY(!_upb_Decoder_ReadString(
d, &p, vlen, &str_val,
upb_DecodeFastData_ValValidateUtf8(data)))) {
return UPB_DECODEFAST_ERROR(d, kUpb_DecodeStatus_OutOfMemory, next);
}
if (UPB_UNLIKELY(!_upb_map_tovalue(&str_val, UPB_MAPTYPE_STRING,
&tabval, &d->arena))) {
return UPB_DECODEFAST_ERROR(d, kUpb_DecodeStatus_OutOfMemory, next);
}
}
break;
}
default:
UPB_UNREACHABLE();
}
bool replaced;
bool ok =
is_str_map
? upb_strtable_insert_or_replace(&map_ctx->map->t.strtable,
strkey.data, strkey.size, tabval,
&replaced, &d->arena)
: upb_inttable_insert_or_replace(&map_ctx->map->t.inttable, intkey,
tabval, &replaced, &d->arena);
if (UPB_UNLIKELY(!ok)) {
return UPB_DECODEFAST_ERROR(d, kUpb_DecodeStatus_OutOfMemory, next);
}
*ptr = p;
return true;
}
// Estimates the number of contiguous map entries with single-byte lengths by
// skip-scanning ahead. If an entry has a length > 127 (multibyte varint),
// scanning stops early to avoid cache pollution on large entries.
UPB_FORCEINLINE
size_t upb_DecodeFast_EstimateMapSize(upb_Decoder* d, const char* ptr,
uint16_t expected,
upb_DecodeFast_TagSize tagsize) {
size_t count = 0;
const char* limit_ptr = d->input.limit_ptr;
int saved_guaranteed =
UPB_PRIVATE(upb_EpsCopyInputStream_GetBoundsCheckedBytes)(EPS(d));
if (tagsize == kUpb_DecodeFast_Tag1Byte) {
if (limit_ptr - ptr >= 2) {
UPB_PRIVATE(upb_EpsCopyInputStream_BoundsCheckedToEnd)(EPS(d), ptr,
limit_ptr);
while (limit_ptr - ptr >= 2) {
UPB_PRIVATE(upb_EpsCopyInputStream_ConsumeBytes)(EPS(d), 2);
uint16_t val;
memcpy(&val, ptr, 2);
if ((val & 0x80ff) != (uint8_t)expected) break;
uint32_t len = val >> 8;
uint32_t skip = 2 + len;
if (limit_ptr - ptr < skip) break;
UPB_PRIVATE(upb_EpsCopyInputStream_ConsumeBytes)(EPS(d), len);
count++;
ptr += skip;
}
}
} else {
UPB_ASSERT(tagsize == kUpb_DecodeFast_Tag2Byte);
if (limit_ptr - ptr >= 3) {
UPB_PRIVATE(upb_EpsCopyInputStream_BoundsCheckedToEnd)(EPS(d), ptr,
limit_ptr);
while (limit_ptr - ptr >= 3) {
UPB_PRIVATE(upb_EpsCopyInputStream_ConsumeBytes)(EPS(d), 4);
uint32_t val = 0;
memcpy(&val, ptr, 4);
if ((val & 0x80ffff) != expected) break;
uint32_t len = (val >> 16) & 0x7f;
uint32_t skip = 3 + len;
if (skip < 4 || limit_ptr - ptr < skip) break;
UPB_PRIVATE(upb_EpsCopyInputStream_ConsumeBytes)(EPS(d), skip - 4);
count++;
ptr += skip;
}
}
}
UPB_PRIVATE(upb_EpsCopyInputStream_BoundsChecked)(EPS(d), saved_guaranteed);
return count;
}
UPB_FORCEINLINE
void upb_DecodeFast_Map(upb_Decoder* d, const char** ptr, upb_Message* msg,
const upb_MiniTable* table, uint64_t* hasbits,
uint64_t data, uint64_t data2,
upb_DecodeFast_TagSize tagsize, bool is_str_map,
upb_DecodeFastNext* next) {
UPB_ASSERT(is_str_map == upb_DecodeFastData_IsStrMap(data));
UPB_ASSERT((tagsize == kUpb_DecodeFast_Tag2Byte) ==
upb_DecodeFastData_IsTag2(data));
uint16_t expected = upb_DecodeFastData_GetExpectedTag(data);
uint16_t actual = upb_DecodeFastData2_GetOriginalTag(data2);
if (UPB_UNLIKELY(!upb_DecodeFast_TagMatches(expected, actual, tagsize))) {
UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackMismatchedSlot, next);
return;
}
upb_DecodeFastMap map_ctx;
if (UPB_UNLIKELY(!upb_DecodeFast_GetMap(d, msg, table, data, is_str_map,
hasbits, &map_ctx, next))) {
return;
}
// The sizes packed into `data` by upb_DecodeFast_TryFillMapEntry() describe
// the same map that _upb_Decoder_CreateMap() sized from kSizeInMap[], so they
// must agree. Use UPB_ASSERT rather than UPB_ASSUME here so the compiler does
// not replace register bitfield extractions from `data` with memory loads
// from `map_ctx.map`.
UPB_ASSERT(map_ctx.map->UPB_PRIVATE(is_strtable) == is_str_map);
UPB_ASSERT(map_ctx.map->key_size == upb_DecodeFastData_GetKeySize(data));
UPB_ASSERT(map_ctx.map->val_size == upb_DecodeFastData_GetValSize(data));
UPB_ASSERT(is_str_map ==
(upb_DecodeFastData_GetKeySize(data) == UPB_MAPTYPE_STRING));
int depth_cost = map_ctx.sub_table ? 2 : 1;
if (d->depth < depth_cost) {
// This isn't yet an error, because if the map's entries are malformed
// (value field not actually present or wrong wire type) then it won't
// actually recurse and thus won't exceed the max depth. But we don't want
// to repeatedly check the depth limit on every entry, so let the fallback
// decoder handle this.
UPB_DECODEFAST_EXIT(kUpb_DecodeFastNext_FallbackToMiniTable, next);
return;
}
d->depth -= depth_cost;
size_t estimated_entries =
upb_DecodeFast_EstimateMapSize(d, *ptr, expected, tagsize);
if (estimated_entries > 0) {
// The map's table discriminator is derived from the same entry MiniTable
// that selected this parser, so it always agrees with our compile-time
// is_str_map. Place the assume immediately before _upb_Map_Size() with no
// intervening memory writes so EarlyCSE folds the strtable/inttable branch.
UPB_ASSUME(map_ctx.map->UPB_PRIVATE(is_strtable) == is_str_map);
if (UPB_UNLIKELY(!_upb_Map_Reserve(
map_ctx.map, _upb_Map_Size(map_ctx.map) + estimated_entries,
&d->arena))) {
d->depth += depth_cost;
UPB_DECODEFAST_ERROR(d, kUpb_DecodeStatus_OutOfMemory, next);
return;
}
}
const char* p = *ptr + upb_DecodeFast_TagSizeBytes(tagsize);
while (1) {
const char* entry_start = p - upb_DecodeFast_TagSizeBytes(tagsize);
if (UPB_UNLIKELY(!upb_DecodeFast_ParseMapEntry(d, &p, &map_ctx, data,
is_str_map, next))) {
*ptr = entry_start;
break;
}
*ptr = p;
_upb_Decoder_Trace(d, 'F');
if (!upb_DecodeFast_TryMatchTag(d, p, expected, next, tagsize)) {
break;
}
p += upb_DecodeFast_TagSizeBytes(tagsize);
}
d->depth += depth_cost;
}
#define F(key_type, is_str, tagsize) \
UPB_NOINLINE UPB_PRESERVE_NONE upb_FastDecoder_Return \
upb_DecodeFast_##key_type##Map_##tagsize(UPB_PARSE_PARAMS) { \
upb_DecodeFastNext next = kUpb_DecodeFastNext_Dispatch; \
upb_DecodeFast_Map(d, &ptr, msg, table, &hasbits, data, data2, \
kUpb_DecodeFast_##tagsize, is_str, &next); \
UPB_DECODEFAST_NEXT(next); \
}
F(Int, false, Tag1Byte)
F(Int, false, Tag2Byte)
F(Str, true, Tag1Byte)
F(Str, true, Tag2Byte)
#undef F