This commit is contained in:
J. Nick Koston 2025-07-10 22:45:00 -10:00
parent 00e39958b8
commit f2037aadc5
No known key found for this signature in database
2 changed files with 78 additions and 63 deletions

View File

@ -24,7 +24,7 @@ std::string ProtoMessage::dump() const {
// ============================================================================
// Helper to get vector size for any repeated field type
static size_t get_vector_size(ProtoFieldType type, const void *field_addr) {
static inline size_t get_vector_size(ProtoFieldType type, const void *field_addr) {
switch (type) {
case ProtoFieldType::TYPE_BOOL:
return static_cast<const std::vector<bool> *>(field_addr)->size();
@ -52,7 +52,7 @@ static size_t get_vector_size(ProtoFieldType type, const void *field_addr) {
}
// Helper to get a pointer to the nth element in a vector (const version)
static const void *get_vector_element(ProtoFieldType type, const void *field_addr, size_t index) {
static inline const void *get_vector_element(ProtoFieldType type, const void *field_addr, size_t index) {
switch (type) {
case ProtoFieldType::TYPE_BOOL: {
// std::vector<bool> is special - we need to handle it differently
@ -84,8 +84,8 @@ static const void *get_vector_element(ProtoFieldType type, const void *field_add
}
// Unified encode function that works for both single fields and repeated fields
static void encode_field(ProtoWriteBuffer &buffer, ProtoFieldType type, uint8_t field_num, const void *field_addr,
bool force) {
static inline void encode_field(ProtoWriteBuffer &buffer, ProtoFieldType type, uint8_t field_num,
const void *field_addr, bool force) {
switch (type) {
case ProtoFieldType::TYPE_BOOL:
buffer.encode_bool(field_num, *static_cast<const bool *>(field_addr), force);
@ -132,8 +132,8 @@ static void encode_field(ProtoWriteBuffer &buffer, ProtoFieldType type, uint8_t
}
// Unified size calculation
static void calculate_field_size(uint32_t &total_size, ProtoFieldType type, uint8_t precalc_size,
const void *field_addr, bool force) {
static inline void calculate_field_size(uint32_t &total_size, ProtoFieldType type, uint8_t precalc_size,
const void *field_addr, bool force) {
switch (type) {
case ProtoFieldType::TYPE_BOOL:
ProtoSize::add_bool_field(total_size, precalc_size, *static_cast<const bool *>(field_addr), force);
@ -176,7 +176,7 @@ static void calculate_field_size(uint32_t &total_size, ProtoFieldType type, uint
}
// Decode varint for single fields
static bool decode_varint_field(ProtoFieldType type, void *field_addr, const ProtoVarInt &value) {
static inline bool decode_varint_field(ProtoFieldType type, void *field_addr, const ProtoVarInt &value) {
switch (type) {
case ProtoFieldType::TYPE_BOOL:
*static_cast<bool *>(field_addr) = value.as_bool();
@ -206,7 +206,7 @@ static bool decode_varint_field(ProtoFieldType type, void *field_addr, const Pro
}
// Decode varint for repeated fields
static bool decode_repeated_varint_field(ProtoFieldType type, void *field_addr, const ProtoVarInt &value) {
static inline bool decode_repeated_varint_field(ProtoFieldType type, void *field_addr, const ProtoVarInt &value) {
switch (type) {
case ProtoFieldType::TYPE_BOOL:
static_cast<std::vector<bool> *>(field_addr)->push_back(value.as_bool());
@ -236,7 +236,7 @@ static bool decode_repeated_varint_field(ProtoFieldType type, void *field_addr,
}
// Decode 32-bit for single fields
static bool decode_32bit_field(ProtoFieldType type, void *field_addr, const Proto32Bit &value) {
static inline bool decode_32bit_field(ProtoFieldType type, void *field_addr, const Proto32Bit &value) {
switch (type) {
case ProtoFieldType::TYPE_FLOAT:
*static_cast<float *>(field_addr) = value.as_float();
@ -253,7 +253,7 @@ static bool decode_32bit_field(ProtoFieldType type, void *field_addr, const Prot
}
// Decode 32-bit for repeated fields
static bool decode_repeated_32bit_field(ProtoFieldType type, void *field_addr, const Proto32Bit &value) {
static inline bool decode_repeated_32bit_field(ProtoFieldType type, void *field_addr, const Proto32Bit &value) {
switch (type) {
case ProtoFieldType::TYPE_FLOAT:
static_cast<std::vector<float> *>(field_addr)->push_back(value.as_float());
@ -270,8 +270,8 @@ static bool decode_repeated_32bit_field(ProtoFieldType type, void *field_addr, c
}
// Decode length-delimited for single fields
static bool decode_length_field(ProtoFieldType type, void *field_addr, const ProtoLengthDelimited &value,
uint8_t message_type_id) {
static inline bool decode_length_field(ProtoFieldType type, void *field_addr, const ProtoLengthDelimited &value,
uint8_t message_type_id) {
switch (type) {
case ProtoFieldType::TYPE_STRING:
case ProtoFieldType::TYPE_BYTES:
@ -288,8 +288,8 @@ static bool decode_length_field(ProtoFieldType type, void *field_addr, const Pro
}
// Decode length-delimited for repeated fields
static bool decode_repeated_length_field(ProtoFieldType type, void *field_addr, const ProtoLengthDelimited &value,
uint8_t message_type_id) {
static inline bool decode_repeated_length_field(ProtoFieldType type, void *field_addr,
const ProtoLengthDelimited &value, uint8_t message_type_id) {
switch (type) {
case ProtoFieldType::TYPE_STRING:
case ProtoFieldType::TYPE_BYTES:
@ -486,8 +486,13 @@ void ProtoMessage::encode(ProtoWriteBuffer buffer) const {
REPEATED_MESSAGE_HANDLERS[handler_id].encode(buffer, field_addr, repeated_fields[i].field_num);
}
} else {
// Iterate through the vector and encode each element using the same function!
// Early exit for empty vectors
size_t count = get_vector_size(repeated_fields[i].get_type(), field_addr);
if (count == 0) {
continue;
}
// Iterate through the vector and encode each element using the same function!
for (size_t j = 0; j < count; j++) {
const void *element = get_vector_element(repeated_fields[i].get_type(), field_addr, j);
if (element != nullptr) {
@ -535,10 +540,18 @@ void ProtoMessage::calculate_size(uint32_t &total_size) const {
ProtoFieldType type = repeated_fields[i].get_type();
size_t count = get_vector_size(type, field_addr);
// Early exit for empty vectors
if (count == 0) {
continue;
}
// For fixed-size types, we can calculate size more efficiently
if (type == ProtoFieldType::TYPE_FIXED32 || type == ProtoFieldType::TYPE_SFIXED32 ||
type == ProtoFieldType::TYPE_FLOAT) {
total_size += count * (repeated_fields[i].get_precalced_size() + 4);
} else if (type == ProtoFieldType::TYPE_BOOL) {
// Booleans are always 1 byte when encoded
total_size += count * (repeated_fields[i].get_precalced_size() + 1);
} else {
// For variable-size types, calculate each element
for (size_t j = 0; j < count; j++) {

View File

@ -40,7 +40,7 @@ enum class ProtoFieldType : uint8_t {
};
// Helper to get wire type from field type
constexpr uint8_t get_wire_type(ProtoFieldType type) {
inline constexpr uint8_t get_wire_type(ProtoFieldType type) {
switch (type) {
case ProtoFieldType::TYPE_BOOL:
case ProtoFieldType::TYPE_INT32:
@ -113,20 +113,20 @@ class ProtoVarInt {
return {}; // Incomplete or invalid varint
}
uint16_t as_uint16() const { return this->value_; }
uint32_t as_uint32() const { return this->value_; }
uint64_t as_uint64() const { return this->value_; }
bool as_bool() const { return this->value_; }
template<typename T> T as_enum() const { return static_cast<T>(this->as_uint32()); }
int32_t as_int32() const {
inline uint16_t as_uint16() const { return this->value_; }
inline uint32_t as_uint32() const { return this->value_; }
inline uint64_t as_uint64() const { return this->value_; }
inline bool as_bool() const { return this->value_; }
template<typename T> inline T as_enum() const { return static_cast<T>(this->as_uint32()); }
inline int32_t as_int32() const {
// Not ZigZag encoded
return static_cast<int32_t>(this->as_int64());
}
int64_t as_int64() const {
inline int64_t as_int64() const {
// Not ZigZag encoded
return static_cast<int64_t>(this->value_);
}
int32_t as_sint32() const {
inline int32_t as_sint32() const {
// with ZigZag encoding
if (this->value_ & 1) {
return static_cast<int32_t>(~(this->value_ >> 1));
@ -134,7 +134,7 @@ class ProtoVarInt {
return static_cast<int32_t>(this->value_ >> 1);
}
}
int64_t as_sint64() const {
inline int64_t as_sint64() const {
// with ZigZag encoding
if (this->value_ & 1) {
return static_cast<int64_t>(~(this->value_ >> 1));
@ -194,8 +194,10 @@ class ProtoVarInt {
class ProtoLengthDelimited {
public:
explicit ProtoLengthDelimited(const uint8_t *value, size_t length) : value_(value), length_(length) {}
std::string as_string() const { return std::string(reinterpret_cast<const char *>(this->value_), this->length_); }
template<class C> C as_message() const {
inline std::string as_string() const {
return std::string(reinterpret_cast<const char *>(this->value_), this->length_);
}
template<class C> inline C as_message() const {
auto msg = C();
msg.decode(this->value_, this->length_);
return msg;
@ -209,9 +211,9 @@ class ProtoLengthDelimited {
class Proto32Bit {
public:
explicit Proto32Bit(uint32_t value) : value_(value) {}
uint32_t as_fixed32() const { return this->value_; }
int32_t as_sfixed32() const { return static_cast<int32_t>(this->value_); }
float as_float() const {
inline uint32_t as_fixed32() const { return this->value_; }
inline int32_t as_sfixed32() const { return static_cast<int32_t>(this->value_); }
inline float as_float() const {
union {
uint32_t raw;
float value;
@ -267,9 +269,9 @@ struct FieldMeta {
};
// Helper methods
ProtoFieldType get_type() const { return static_cast<ProtoFieldType>(type_and_size & 0x1F); }
uint8_t get_precalced_size() const { return ((type_and_size >> 5) & 0x03) + 1; }
uint8_t get_wire_type() const {
inline ProtoFieldType get_type() const { return static_cast<ProtoFieldType>(type_and_size & 0x1F); }
inline uint8_t get_precalced_size() const { return ((type_and_size >> 5) & 0x03) + 1; }
inline uint8_t get_wire_type() const {
// Wire type is encoded as: 0=varint, 2=length-delimited, 5=32-bit
// We only need 1 bit to distinguish between 0/2 and 5 (32-bit)
// If bit 7 is set, it's wire type 5, otherwise check the field type
@ -279,7 +281,7 @@ struct FieldMeta {
ProtoFieldType t = get_type();
return (t >= ProtoFieldType::TYPE_STRING) ? 2 : 0; // length-delimited : varint
}
uint16_t get_offset() const {
inline uint16_t get_offset() const {
if (get_type() == ProtoFieldType::TYPE_MESSAGE) {
// Reconstruct full offset from packed fields (10-bit offset)
// Bits 0-7 from offset_low, bits 8-9 from lower 2 bits of message_type_id
@ -287,7 +289,7 @@ struct FieldMeta {
}
return offset;
}
uint8_t get_message_type_id() const { return message_type_id >> 2; } // Upper 6 bits for type ID (0-63)
inline uint8_t get_message_type_id() const { return message_type_id >> 2; } // Upper 6 bits for type ID (0-63)
};
// Optimized repeated field metadata (4 bytes - no padding on 32-bit architectures)
@ -303,9 +305,9 @@ struct RepeatedFieldMeta {
};
// Helper methods
ProtoFieldType get_type() const { return static_cast<ProtoFieldType>(type_and_size & 0x1F); }
uint8_t get_precalced_size() const { return ((type_and_size >> 5) & 0x03) + 1; }
uint8_t get_wire_type() const {
inline ProtoFieldType get_type() const { return static_cast<ProtoFieldType>(type_and_size & 0x1F); }
inline uint8_t get_precalced_size() const { return ((type_and_size >> 5) & 0x03) + 1; }
inline uint8_t get_wire_type() const {
// Wire type is encoded as: 0=varint, 2=length-delimited, 5=32-bit
// We only need 1 bit to distinguish between 0/2 and 5 (32-bit)
// If bit 7 is set, it's wire type 5, otherwise check the field type
@ -315,7 +317,7 @@ struct RepeatedFieldMeta {
ProtoFieldType t = get_type();
return (t >= ProtoFieldType::TYPE_STRING) ? 2 : 0; // length-delimited : varint
}
uint16_t get_offset() const {
inline uint16_t get_offset() const {
if (get_type() == ProtoFieldType::TYPE_MESSAGE) {
// Reconstruct full offset from packed fields (10-bit offset)
// Bits 0-7 from offset_low, bits 8-9 from lower 2 bits of message_type_id
@ -323,7 +325,7 @@ struct RepeatedFieldMeta {
}
return offset;
}
uint8_t get_message_type_id() const { return message_type_id >> 2; } // Upper 6 bits for type ID (0-63)
inline uint8_t get_message_type_id() const { return message_type_id >> 2; } // Upper 6 bits for type ID (0-63)
};
// Binary search for field lookup - optimized for performance
@ -371,9 +373,9 @@ inline const FieldMeta *find_field_binary(const FieldMeta *fields, uint8_t count
class ProtoWriteBuffer {
public:
ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer) {}
void write(uint8_t value) { this->buffer_->push_back(value); }
void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); }
void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); }
inline void write(uint8_t value) { this->buffer_->push_back(value); }
inline void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); }
inline void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); }
/**
* Encode a field key (tag/wire type combination).
*
@ -386,11 +388,11 @@ class ProtoWriteBuffer {
*
* Following https://protobuf.dev/programming-guides/encoding/#structure
*/
void encode_field_raw(uint32_t field_id, uint32_t type) {
inline void encode_field_raw(uint32_t field_id, uint32_t type) {
uint32_t val = (field_id << 3) | (type & 0b111);
this->encode_varint_raw(val);
}
void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) {
inline void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) {
if (len == 0 && !force)
return;
@ -399,25 +401,25 @@ class ProtoWriteBuffer {
auto *data = reinterpret_cast<const uint8_t *>(string);
this->buffer_->insert(this->buffer_->end(), data, data + len);
}
void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
inline void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
this->encode_string(field_id, value.data(), value.size(), force);
}
void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
inline void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force);
}
void encode_uint32(uint32_t field_id, uint32_t value, bool force = false) {
inline void encode_uint32(uint32_t field_id, uint32_t value, bool force = false) {
if (value == 0 && !force)
return;
this->encode_field_raw(field_id, 0); // type 0: Varint - uint32
this->encode_varint_raw(value);
}
void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) {
inline void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) {
if (value == 0 && !force)
return;
this->encode_field_raw(field_id, 0); // type 0: Varint - uint64
this->encode_varint_raw(ProtoVarInt(value));
}
void encode_bool(uint32_t field_id, bool value, bool force = false) {
inline void encode_bool(uint32_t field_id, bool value, bool force = false) {
if (!value && !force)
return;
this->encode_field_raw(field_id, 0); // type 0: Varint - bool
@ -428,15 +430,15 @@ class ProtoWriteBuffer {
return;
this->encode_field_raw(field_id, 5); // type 5: 32-bit fixed32
this->write((value >> 0) & 0xFF);
this->write((value >> 8) & 0xFF);
this->write((value >> 16) & 0xFF);
this->write((value >> 24) & 0xFF);
// Reserve space and write all 4 bytes at once for better performance
size_t pos = this->buffer_->size();
this->buffer_->resize(pos + 4);
(*this->buffer_)[pos] = (value >> 0) & 0xFF;
(*this->buffer_)[pos + 1] = (value >> 8) & 0xFF;
(*this->buffer_)[pos + 2] = (value >> 16) & 0xFF;
(*this->buffer_)[pos + 3] = (value >> 24) & 0xFF;
}
template<typename T> void encode_enum(uint32_t field_id, T value, bool force = false) {
this->encode_uint32(field_id, static_cast<uint32_t>(value), force);
}
void encode_float(uint32_t field_id, float value, bool force = false) {
inline void encode_float(uint32_t field_id, float value, bool force = false) {
if (value == 0.0f && !force)
return;
@ -447,7 +449,7 @@ class ProtoWriteBuffer {
val.value = value;
this->encode_fixed32(field_id, val.raw);
}
void encode_int32(uint32_t field_id, int32_t value, bool force = false) {
inline void encode_int32(uint32_t field_id, int32_t value, bool force = false) {
if (value < 0) {
// negative int32 is always 10 byte long
this->encode_int64(field_id, value, force);
@ -455,10 +457,10 @@ class ProtoWriteBuffer {
}
this->encode_uint32(field_id, static_cast<uint32_t>(value), force);
}
void encode_int64(uint32_t field_id, int64_t value, bool force = false) {
inline void encode_int64(uint32_t field_id, int64_t value, bool force = false) {
this->encode_uint64(field_id, static_cast<uint64_t>(value), force);
}
void encode_sint32(uint32_t field_id, int32_t value, bool force = false) {
inline void encode_sint32(uint32_t field_id, int32_t value, bool force = false) {
uint32_t uvalue;
if (value < 0) {
uvalue = ~(value << 1);
@ -467,7 +469,7 @@ class ProtoWriteBuffer {
}
this->encode_uint32(field_id, uvalue, force);
}
void encode_sint64(uint32_t field_id, int64_t value, bool force = false) {
inline void encode_sint64(uint32_t field_id, int64_t value, bool force = false) {
uint64_t uvalue;
if (value < 0) {
uvalue = ~(value << 1);
@ -476,7 +478,7 @@ class ProtoWriteBuffer {
}
this->encode_uint64(field_id, uvalue, force);
}
void encode_sfixed32(uint32_t field_id, int32_t value, bool force = false) {
inline void encode_sfixed32(uint32_t field_id, int32_t value, bool force = false) {
if (!force && value == 0)
return;
this->encode_fixed32(field_id, static_cast<uint32_t>(value), force);