dep-protobuf/upb_generator/common.cc
Joshua Haberman 2ad03aa1d0 Pack the field array and the subs array into one block of contiguous memory.
Instead of giving every MiniTable two parallel arrays (fields and subs), we now have one block of memory that contains both lists.

For sub-message or enum fields, the `upb_MiniTableField` now contains a byte offset to where the `upb_MiniTable*` or `upb_MiniTableEnum*` can be found.

The new design offers several benefits:

1. The sub-table requires fewer pointer-chasing indirections to find.
2. We no longer need the `upb_MiniTable*` to find the sub-MiniTable -- the `upb_MiniTableField*` is enough now.  So APIs like `upb_MiniTable_GetSubMessageTable()` now require fewer parameters.
3. This unifies fields and extensions -- both can now find a sub-table using exactly the same code, with no need to distinguish between them.

We still benefit from having two lists.  If we tried to put the sub-table pointer directly into `upb_MiniTableField`, it would double the size of `upb_MiniTableField` from 12 to 24 bytes, due to alignment.  Using the current design, sub-message fields only take 20 bytes, while others take 12, so this design is an improvement even if 100% of fields are sub-message fields.

PiperOrigin-RevId: 833329706
2025-11-17 07:29:38 -08:00

118 lines
3.6 KiB
C++

// 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
#include "upb_generator/common.h"
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string>
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "upb/mini_table/field.h"
#include "upb/mini_table/internal/field.h"
#include "upb/reflection/def.hpp"
// Must be last
#include "upb/port/def.inc"
namespace upb {
namespace generator {
std::string FieldInitializer(upb::FieldDefPtr field,
const upb_MiniTableField* field64,
const upb_MiniTableField* field32) {
return absl::Substitute(
"{$0, $1, $2, $3, $4, $5}", upb_MiniTableField_Number(field64),
ArchDependentSize(field32->UPB_PRIVATE(offset),
field64->UPB_PRIVATE(offset)),
ArchDependentSize(field32->presence, field64->presence),
field64->UPB_PRIVATE(submsg_ofs) == kUpb_NoSub
? "kUpb_NoSub"
: ArchDependentSize(field32->UPB_PRIVATE(submsg_ofs),
field64->UPB_PRIVATE(submsg_ofs)),
field64->UPB_PRIVATE(descriptortype), GetModeInit(field32, field64));
}
std::string ArchDependentSize(int64_t size32, int64_t size64) {
if (size32 == size64) return absl::StrCat(size32);
return absl::Substitute("UPB_SIZE($0, $1)", size32, size64);
}
// Returns the field mode as a string initializer.
//
// We could just emit this as a number (and we may yet go in that direction) but
// for now emitting symbolic constants gives this better readability and
// debuggability.
std::string GetModeInit(const upb_MiniTableField* field32,
const upb_MiniTableField* field64) {
std::string ret;
uint8_t mode32 = UPB_SIZE(field32, field64)->UPB_PRIVATE(mode);
switch (mode32 & kUpb_FieldMode_Mask) {
case kUpb_FieldMode_Map:
ret = "(int)kUpb_FieldMode_Map";
break;
case kUpb_FieldMode_Array:
ret = "(int)kUpb_FieldMode_Array";
break;
case kUpb_FieldMode_Scalar:
ret = "(int)kUpb_FieldMode_Scalar";
break;
default:
break;
}
if (mode32 & kUpb_LabelFlags_IsPacked) {
absl::StrAppend(&ret, " | (int)kUpb_LabelFlags_IsPacked");
}
if (mode32 & kUpb_LabelFlags_IsExtension) {
absl::StrAppend(&ret, " | (int)kUpb_LabelFlags_IsExtension");
}
if (mode32 & kUpb_LabelFlags_IsAlternate) {
absl::StrAppend(&ret, " | (int)kUpb_LabelFlags_IsAlternate");
}
absl::StrAppend(&ret, " | ((int)", GetFieldRep(field32, field64),
" << kUpb_FieldRep_Shift)");
return ret;
}
std::string GetFieldRep(const upb_MiniTableField* field32,
const upb_MiniTableField* field64) {
const auto rep32 = UPB_PRIVATE(_upb_MiniTableField_GetRep)(field32);
const auto rep64 = UPB_PRIVATE(_upb_MiniTableField_GetRep)(field64);
switch (rep32) {
case kUpb_FieldRep_1Byte:
return "kUpb_FieldRep_1Byte";
break;
case kUpb_FieldRep_4Byte: {
if (rep64 == kUpb_FieldRep_4Byte) {
return "kUpb_FieldRep_4Byte";
} else {
assert(rep64 == kUpb_FieldRep_8Byte);
return "UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte)";
}
break;
}
case kUpb_FieldRep_StringView:
return "kUpb_FieldRep_StringView";
break;
case kUpb_FieldRep_8Byte:
return "kUpb_FieldRep_8Byte";
break;
}
UPB_UNREACHABLE();
}
} // namespace generator
} // namespace upb