[api] Return the varint parse result as a struct again

The out parameter form regressed the host, where the 16 byte result already travels in
registers, by about 20 percent on the direct varint parse benchmarks. The void
decode_field and low word bool changes stay.
This commit is contained in:
J. Nick Koston
2026-09-07 15:24:33 +02:00
parent 68a45bad8e
commit c26c830f82
2 changed files with 16 additions and 25 deletions
+11 -15
View File
@@ -20,7 +20,7 @@ void ProtoWriteBuffer::encode_varint_raw_slow_(uint32_t value) {
*this->pos_++ = static_cast<uint8_t>(value);
}
uint32_t ProtoVarInt::parse_slow(const uint8_t *buffer, uint32_t len, proto_varint_value_t &value) {
ProtoVarIntResult ProtoVarInt::parse_slow(const uint8_t *buffer, uint32_t len) {
// Multi-byte varint: first byte already checked to have high bit set
uint32_t result32 = buffer[0] & 0x7F;
#ifdef USE_API_VARINT64
@@ -32,30 +32,28 @@ uint32_t ProtoVarInt::parse_slow(const uint8_t *buffer, uint32_t len, proto_vari
uint8_t val = buffer[i];
result32 |= uint32_t(val & 0x7F) << (i * 7);
if ((val & 0x80) == 0) {
value = result32;
return i + 1;
return {result32, i + 1};
}
}
#ifdef USE_API_VARINT64
return parse_wide(buffer, len, result32, value);
return parse_wide(buffer, len, result32);
#else
return PROTO_VARINT_PARSE_FAILED;
return {0, PROTO_VARINT_PARSE_FAILED};
#endif
}
#ifdef USE_API_VARINT64
uint32_t ProtoVarInt::parse_wide(const uint8_t *buffer, uint32_t len, uint32_t result32, uint64_t &value) {
ProtoVarIntResult ProtoVarInt::parse_wide(const uint8_t *buffer, uint32_t len, uint32_t result32) {
uint64_t result64 = result32;
uint32_t limit = std::min(len, uint32_t(10));
for (uint32_t i = 4; i < limit; i++) {
uint8_t val = buffer[i];
result64 |= uint64_t(val & 0x7F) << (i * 7);
if ((val & 0x80) == 0) {
value = result64;
return i + 1;
return {result64, i + 1};
}
}
return PROTO_VARINT_PARSE_FAILED;
return {0, PROTO_VARINT_PARSE_FAILED};
}
#endif
@@ -224,13 +222,11 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
value = *ptr++;
return true;
}
// Separate out variable so the fast path above keeps `value` in a register
proto_varint_value_t wide;
uint32_t consumed = ProtoVarInt::parse_slow(ptr, end - ptr, wide);
if (consumed == PROTO_VARINT_PARSE_FAILED)
auto res = ProtoVarInt::parse_non_empty(ptr, end - ptr);
if (!res.has_value())
return false;
value = wide;
ptr += consumed;
value = res.value;
ptr += res.consumed;
return true;
};
+5 -10
View File
@@ -145,9 +145,7 @@ class ProtoVarInt {
// (booleans, small enums, field tags, small message sizes/types).
if ((buffer[0] & 0x80) == 0) [[likely]]
return {buffer[0], 1};
ProtoVarIntResult res;
res.consumed = parse_slow(buffer, len, res.value);
return res;
return parse_slow(buffer, len);
}
/// Parse a varint from buffer (safe for empty buffers).
@@ -158,16 +156,13 @@ class ProtoVarInt {
return parse_non_empty(buffer, len);
}
/// Multi-byte varint (first byte has the high bit set), outlined to keep the fast path small.
/// Writes the value and returns the bytes consumed, PROTO_VARINT_PARSE_FAILED when truncated.
static uint32_t parse_slow(const uint8_t *buffer, uint32_t len, proto_varint_value_t &value)
__attribute__((noinline));
protected:
// Slow path for multi-byte varints (>= 128), outlined to keep fast path small
static ProtoVarIntResult parse_slow(const uint8_t *buffer, uint32_t len) __attribute__((noinline));
#ifdef USE_API_VARINT64
/// Continue parsing varint bytes 4-9 with 64-bit arithmetic.
static uint32_t parse_wide(const uint8_t *buffer, uint32_t len, uint32_t result32, uint64_t &value)
__attribute__((noinline));
static ProtoVarIntResult parse_wide(const uint8_t *buffer, uint32_t len, uint32_t result32) __attribute__((noinline));
#endif
};