[ObjC] Adapt the varint size calculation from C++

The C++ has a version that is mostly branchless and also very short, so just use
that.

However, don't bother with also doing the Intel version as that won't likely
matter for Apple platforms for much longer.

We've seen cases where folks use clang flags to aggressively inline things and
so this could actual shrink things depending on how many times it gets inlined
within CodedOutputStream.

PiperOrigin-RevId: 831826052
This commit is contained in:
Thomas Van Lenten 2025-11-13 06:02:32 -08:00 committed by Copybara-Service
parent 7ebcd88e66
commit 1bf47797fb

View file

@ -1094,12 +1094,26 @@ size_t GPBComputeWireFormatTagSize(int field_number, GPBDataType dataType) {
}
size_t GPBComputeRawVarint32Size(int32_t value) {
#if __has_builtin(__builtin_clz) && !(defined(__x86__) || defined(__x86_64__) || defined(__i386__))
// This logic comes from the C++ for CodedOutputStream::VarintSize32(uint32_t), it provides a
// branchless calculation of the size. ObjC/C doesn't have some of the nicer C++ things do this so
// model it simply. Also don't bother with the Intel specific version since that likely won't be
// needed for Apple platforms for much longer.
// Ensure the correct `__builtin_clz*` be being used for the compiled architecture.
GPBInternalCompileAssert(sizeof(unsigned int) == sizeof(uint32_t), __builtin_clz_not_32_bits);
const int uint32_digits = sizeof(uint32_t) * 8; // std::numeric_limits<uint32_t>::digits
int clz = (value == 0) ? uint32_digits : __builtin_clz((uint32_t)value);
return (size_t)(((uint32_digits * 9 + 64) - (clz * 9)) / 64);
#else
// value is treated as unsigned, so it won't be sign-extended if negative.
if ((value & (0xffffffff << 7)) == 0) return 1;
if ((value & (0xffffffff << 14)) == 0) return 2;
if ((value & (0xffffffff << 21)) == 0) return 3;
if ((value & (0xffffffff << 28)) == 0) return 4;
return 5;
#endif
}
size_t GPBComputeRawVarint32SizeForInteger(NSInteger value) {
@ -1108,6 +1122,20 @@ size_t GPBComputeRawVarint32SizeForInteger(NSInteger value) {
}
size_t GPBComputeRawVarint64Size(int64_t value) {
#if __has_builtin(__builtin_clzll) && \
!(defined(__x86__) || defined(__x86_64__) || defined(__i386__))
// This logic comes from the C++ for CodedOutputStream::VarintSize64(uint64_t), it provides a
// branchless calculation of the size. ObjC/C doesn't have some of the nicer C++ things do this so
// model it simply. Also don't bother with the Intel specific version since that likely won't be
// needed for Apple platforms for much longer.
// Ensure the correct `__builtin_clz*` be being used for the compiled architecture.
GPBInternalCompileAssert(sizeof(unsigned long long) == sizeof(uint64_t),
__builtin_clzll_not_64_bits);
const int uint64_digits = sizeof(uint64_t) * 8; // std::numeric_limits<uint64_t>::digits
int clz = (value == 0) ? uint64_digits : __builtin_clzll((uint64_t)value);
return (size_t)(((uint64_digits * 9 + 64) - (clz * 9)) / 64);
#else
if ((value & (0xffffffffffffffffL << 7)) == 0) return 1;
if ((value & (0xffffffffffffffffL << 14)) == 0) return 2;
if ((value & (0xffffffffffffffffL << 21)) == 0) return 3;
@ -1118,4 +1146,5 @@ size_t GPBComputeRawVarint64Size(int64_t value) {
if ((value & (0xffffffffffffffffL << 56)) == 0) return 8;
if ((value & (0xffffffffffffffffL << 63)) == 0) return 9;
return 10;
#endif
}