Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ code_coverage_report/**

# Ignore the generated options file.
generator/EmbeddedProto/embedded_proto_options_pb2.py
generator/EmbeddedProto.egg-info
13 changes: 10 additions & 3 deletions generator/EmbeddedProto/Field.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def render(self, filename, jinja_environment):
class FieldBasic(Field):
# A dictionary to convert the wire type into a default value.
type_to_default_value = {FieldDescriptorProto.TYPE_DOUBLE: "0.0",
FieldDescriptorProto.TYPE_FLOAT: "0.0",
FieldDescriptorProto.TYPE_FLOAT: "0.0f",
FieldDescriptorProto.TYPE_INT64: "0",
FieldDescriptorProto.TYPE_UINT64: "0U",
FieldDescriptorProto.TYPE_INT32: "0",
Expand Down Expand Up @@ -379,11 +379,18 @@ def get_type_as_defined(self):

return type_name

def get_max_enum_value(self):
# Return the maximum value used by any of the enum items as a string. This is used to calculate the maximum serialized size.
max_number = self.definition.descriptor.value[0].number
for value in self.definition.descriptor.value[1:]:
if max_number < value.number:
max_number = value.number
return str(max_number)
def get_type(self):
return "EmbeddedProto::enumeration<" + self.get_type_as_defined() + ">"
return "EmbeddedProto::enumeration<" + self.get_type_as_defined() + ", EmbeddedProto::WireFormatter::VarintSize(" + self.get_max_enum_value() + ")>"

def get_short_type(self):
return "EmbeddedProto::enumeration<" + self.get_type_as_defined().split("::")[-1] + ">"
return "EmbeddedProto::enumeration<" + self.get_type_as_defined().split("::")[-1] + ", EmbeddedProto::WireFormatter::VarintSize(" + self.get_max_enum_value() + ")>"

def get_default_value(self):
return "static_cast<" + self.get_type_as_defined() + ">(0)"
Expand Down
34 changes: 34 additions & 0 deletions generator/EmbeddedProto/templates/TypeDefMsg.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,39 @@ class {{ typedef.get_name() }} final: public ::EmbeddedProto::MessageInterface

#endif

//! Calculate the maximum number of bytes required to serialize this type of message.
/*!
This function does not take into account a possible id of this message object is self.
\return The number of bytes required at most to serialize this message type.
*/
static constexpr uint32_t max_serialized_size()
{
using namespace ::EmbeddedProto;
return
{% for field in typedef.fields %}
{{"+ " if not loop.first }}{{field.get_type()}}::max_serialized_size(static_cast<uint32_t>(FieldNumber::{{field.variable_id_name}}))
{% endfor %}
{% for oneof in typedef.oneofs %}
{{"+ " if not loop.first or typedef.fields|length != 0}}max_serialized_size_{{oneof.get_name()}}()
{% endfor %}
{% if typedef.fields|length == 0 and typedef.oneofs|length == 0 %}
0
{% endif %}
;
}

//! Calculate the maximum number of bytes required to serialize this message including the field id number and tag.
/*!
\return The number of bytes required at most to serialize this message type.
*/
static constexpr uint32_t max_serialized_size(const uint32_t field_number)
{
using namespace ::EmbeddedProto;
return WireFormatter::VarintSize(WireFormatter::MakeTag(field_number, WireFormatter::WireType::LENGTH_DELIMITED)) // Size of the tag
+ WireFormatter::VarintSize(max_serialized_size()) // Length delimted value
+ max_serialized_size(); // Length of the serialized content
}

#ifdef MSG_TO_STRING

::EmbeddedProto::string_view to_string(::EmbeddedProto::string_view& str) const
Expand Down Expand Up @@ -421,6 +454,7 @@ class {{ typedef.get_name() }} final: public ::EmbeddedProto::MessageInterface
{{ TypeOneof.init(oneof)|indent(6) }}
{{ TypeOneof.clear(oneof)|indent(6) }}
{{ TypeOneof.deserialize(oneof, environment)|indent(6) }}
{{ TypeOneof.max_serialized_size(oneof)|indent(6) }}
#ifdef MSG_TO_STRING
{{ TypeOneof.to_string(oneof)|indent(6) }}
#endif // End of MSG_TO_STRING
Expand Down
23 changes: 23 additions & 0 deletions generator/EmbeddedProto/templates/TypeOneof.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,27 @@ ::EmbeddedProto::string_view to_string_{{_oneof.get_name()}}(::EmbeddedProto::st

return left_chars;
}
{% endmacro %}
{# #}
{# ------------------------------------------------------------------------------------------------------------------ #}
{# #}
{% macro max_serialized_size(_oneof) %}
static constexpr uint32_t max_serialized_size_{{_oneof.get_name()}}()
{
using namespace EmbeddedProto;
return
{% for field in _oneof.get_fields() %}
{% if not loop.last %}
{{ ' ' * (loop.index0 * 2) }}max({{field.get_type()}}::max_serialized_size(static_cast<uint32_t>(FieldNumber::{{field.variable_id_name}})),
{% else %}
{{ ' ' * (2 + (loop.index0 * 2))}}{{field.get_type()}}::max_serialized_size(static_cast<uint32_t>(FieldNumber::{{field.variable_id_name}}))
{% endif %}
{% endfor %}
{% if _oneof.get_fields() | length > 1 %}
{% for i in range(_oneof.get_fields() | length - 1) %}
{{ ' ' * (5 + (loop.revindex * 2)) }})
{% endfor %}
{% endif %}
;
}
{% endmacro %}
9 changes: 7 additions & 2 deletions src/Defines.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2024 Embedded AMS B.V. - All Rights Reserved
* Copyright (C) 2020-2025 Embedded AMS B.V. - All Rights Reserved
*
* This file is part of Embedded Proto.
*
Expand All @@ -16,7 +16,7 @@
* along with Embedded Proto. If not, see <https://www.gnu.org/licenses/>.
*
* For commercial and closed source application please visit:
* <https://EmbeddedProto.com/license/>.
* <https://embeddedproto.com/pricing/>.
*
* Embedded AMS B.V.
* Info:
Expand Down Expand Up @@ -83,6 +83,11 @@ namespace EmbeddedProto
using string_view = array_view<char>;
using bytes_view = array_view<uint8_t>;

//! Simple max function as constexpr
constexpr uint32_t max(const uint32_t a, const uint32_t b)
{
return (a > b) ? a : b;
}
}

#endif //_EMBEDDED_PROTO_DEFINES_H_
27 changes: 26 additions & 1 deletion src/FieldStringBytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,32 @@ namespace EmbeddedProto
void clear() override
{
data_.fill(0);
current_length_ = 0;
current_length_ = 0;
}

//! When serialized with the all elements set, how much bytes are then required.
/*!
This function takes into account the field number and tag combination.
\param[in] field_number We need to include the field number. This because large field numbers require more bytes.
\return The number of bytes required at most.
*/
static constexpr uint32_t max_serialized_size(const uint32_t field_number)
{
return MAX_LENGTH // The number of bytes of the data.
+ WireFormatter::VarintSize(MAX_LENGTH) // The varint indicating the actual number of bytes.
+ WireFormatter::VarintSize(WireFormatter::MakeTag(field_number,
WireFormatter::WireType::LENGTH_DELIMITED)); // The field and tag comby
}

//! When serialized with the all elements set, how much bytes are then required.
/*!
This function is used when the field bytes or string is serialized packed. Think in a repeated field.
\return The number of bytes required at most.
*/
static constexpr uint32_t max_serialized_size()
{
return MAX_LENGTH // The number of bytes of the data.
+ WireFormatter::VarintSize(MAX_LENGTH); // The varint indicating the actual number of bytes.
}

protected:
Expand Down
80 changes: 48 additions & 32 deletions src/Fields.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2024 Embedded AMS B.V. - All Rights Reserved
* Copyright (C) 2020-2025 Embedded AMS B.V. - All Rights Reserved
*
* This file is part of Embedded Proto.
*
Expand All @@ -16,7 +16,7 @@
* along with Embedded Proto. If not, see <https://www.gnu.org/licenses/>.
*
* For commercial and closed source application please visit:
* <https://EmbeddedProto.com/license/>.
* <https://embeddedproto.com/pricing/>.
*
* Embedded AMS B.V.
* Info:
Expand Down Expand Up @@ -119,12 +119,12 @@ namespace EmbeddedProto
#endif // End of MSG_TO_STRING
};

template<Field::FieldTypes FIELDTYPE, class VARIABLE_TYPE, WireFormatter::WireType WIRETYPE>
template<Field::FieldTypes FIELDTYPE, class VARIABLE_TYPE, WireFormatter::WireType WIRETYPE, uint32_t MAX_SER_SIZE>
class FieldTemplate
{
public:
using TYPE = VARIABLE_TYPE;
using CLASS_TYPE = FieldTemplate<FIELDTYPE, VARIABLE_TYPE, WIRETYPE>;
using CLASS_TYPE = FieldTemplate<FIELDTYPE, VARIABLE_TYPE, WIRETYPE, MAX_SER_SIZE>;

FieldTemplate() = default;
FieldTemplate(const VARIABLE_TYPE& v) : value_(v) { };
Expand Down Expand Up @@ -209,18 +209,18 @@ namespace EmbeddedProto
bool operator>=(const VARIABLE_TYPE& rhs) { return value_ >= rhs; }
bool operator<=(const VARIABLE_TYPE& rhs) { return value_ <= rhs; }

template<Field::FieldTypes FIELDTYPE_RHS, class TYPE_RHS, WireFormatter::WireType WIRETYPE_RHS>
bool operator==(const FieldTemplate<FIELDTYPE_RHS, TYPE_RHS, WIRETYPE_RHS>& rhs) { return value_ == rhs.get(); }
template<Field::FieldTypes FIELDTYPE_RHS, class TYPE_RHS, WireFormatter::WireType WIRETYPE_RHS>
bool operator!=(const FieldTemplate<FIELDTYPE_RHS, TYPE_RHS, WIRETYPE_RHS>& rhs) { return value_ != rhs.get(); }
template<Field::FieldTypes FIELDTYPE_RHS, class TYPE_RHS, WireFormatter::WireType WIRETYPE_RHS>
bool operator>(const FieldTemplate<FIELDTYPE_RHS, TYPE_RHS, WIRETYPE_RHS>& rhs) { return value_ > rhs.get(); }
template<Field::FieldTypes FIELDTYPE_RHS, class TYPE_RHS, WireFormatter::WireType WIRETYPE_RHS>
bool operator<(const FieldTemplate<FIELDTYPE_RHS, TYPE_RHS, WIRETYPE_RHS>& rhs) { return value_ < rhs.get(); }
template<Field::FieldTypes FIELDTYPE_RHS, class TYPE_RHS, WireFormatter::WireType WIRETYPE_RHS>
bool operator>=(const FieldTemplate<FIELDTYPE_RHS, TYPE_RHS, WIRETYPE_RHS>& rhs) { return value_ >= rhs.get(); }
template<Field::FieldTypes FIELDTYPE_RHS, class TYPE_RHS, WireFormatter::WireType WIRETYPE_RHS>
bool operator<=(const FieldTemplate<FIELDTYPE_RHS, TYPE_RHS, WIRETYPE_RHS>& rhs) { return value_ <= rhs.get(); }
template<Field::FieldTypes FIELDTYPE_RHS, class TYPE_RHS, WireFormatter::WireType WIRETYPE_RHS, uint32_t SIZE_RHS>
bool operator==(const FieldTemplate<FIELDTYPE_RHS, TYPE_RHS, WIRETYPE_RHS, SIZE_RHS>& rhs) { return value_ == rhs.get(); }
template<Field::FieldTypes FIELDTYPE_RHS, class TYPE_RHS, WireFormatter::WireType WIRETYPE_RHS, uint32_t SIZE_RHS>
bool operator!=(const FieldTemplate<FIELDTYPE_RHS, TYPE_RHS, WIRETYPE_RHS, SIZE_RHS>& rhs) { return value_ != rhs.get(); }
template<Field::FieldTypes FIELDTYPE_RHS, class TYPE_RHS, WireFormatter::WireType WIRETYPE_RHS, uint32_t SIZE_RHS>
bool operator>(const FieldTemplate<FIELDTYPE_RHS, TYPE_RHS, WIRETYPE_RHS, SIZE_RHS>& rhs) { return value_ > rhs.get(); }
template<Field::FieldTypes FIELDTYPE_RHS, class TYPE_RHS, WireFormatter::WireType WIRETYPE_RHS, uint32_t SIZE_RHS>
bool operator<(const FieldTemplate<FIELDTYPE_RHS, TYPE_RHS, WIRETYPE_RHS, SIZE_RHS>& rhs) { return value_ < rhs.get(); }
template<Field::FieldTypes FIELDTYPE_RHS, class TYPE_RHS, WireFormatter::WireType WIRETYPE_RHS, uint32_t SIZE_RHS>
bool operator>=(const FieldTemplate<FIELDTYPE_RHS, TYPE_RHS, WIRETYPE_RHS, SIZE_RHS>& rhs) { return value_ >= rhs.get(); }
template<Field::FieldTypes FIELDTYPE_RHS, class TYPE_RHS, WireFormatter::WireType WIRETYPE_RHS, uint32_t SIZE_RHS>
bool operator<=(const FieldTemplate<FIELDTYPE_RHS, TYPE_RHS, WIRETYPE_RHS, SIZE_RHS>& rhs) { return value_ <= rhs.get(); }

void clear() { value_ = static_cast<VARIABLE_TYPE>(0); }

Expand All @@ -231,6 +231,22 @@ namespace EmbeddedProto
return calcBuffer.get_size();
}

//! When serialized with the most unfavrouble value how much bytes does this field need.
/*!
This function takes into account the field number and tag combination.
\param[in] field_number We need to include the field number. This because large field numbers require more bytes.
\return The number of bytes required at most.
*/
static constexpr uint32_t max_serialized_size(const uint32_t field_number)
{
return MAX_SER_SIZE + WireFormatter::VarintSize(WireFormatter::MakeTag(field_number, WIRETYPE));
}

static constexpr uint32_t max_serialized_size()
{
return MAX_SER_SIZE;
}

#ifdef MSG_TO_STRING

//! Write all the data in this field to a human readable string.
Expand Down Expand Up @@ -438,22 +454,22 @@ namespace EmbeddedProto
};


using int32 = FieldTemplate<Field::FieldTypes::int32, int32_t, WireFormatter::WireType::VARINT>;
using int64 = FieldTemplate<Field::FieldTypes::int64, int64_t, WireFormatter::WireType::VARINT>;
using uint32 = FieldTemplate<Field::FieldTypes::uint32, uint32_t, WireFormatter::WireType::VARINT>;
using uint64 = FieldTemplate<Field::FieldTypes::uint64, uint64_t, WireFormatter::WireType::VARINT>;
using sint32 = FieldTemplate<Field::FieldTypes::sint32, int32_t, WireFormatter::WireType::VARINT>;
using sint64 = FieldTemplate<Field::FieldTypes::sint64, int64_t, WireFormatter::WireType::VARINT>;
using boolean = FieldTemplate<Field::FieldTypes::boolean, bool, WireFormatter::WireType::VARINT>;
using fixed32 = FieldTemplate<Field::FieldTypes::fixed32, uint32_t, WireFormatter::WireType::FIXED32>;
using fixed64 = FieldTemplate<Field::FieldTypes::fixed64, uint64_t, WireFormatter::WireType::FIXED64>;
using sfixed32 = FieldTemplate<Field::FieldTypes::sfixed32, int32_t, WireFormatter::WireType::FIXED32>;
using sfixed64 = FieldTemplate<Field::FieldTypes::sfixed64, int64_t, WireFormatter::WireType::FIXED64>;
using floatfixed = FieldTemplate<Field::FieldTypes::floatfixed, float, WireFormatter::WireType::FIXED32>;
using doublefixed = FieldTemplate<Field::FieldTypes::doublefixed, double, WireFormatter::WireType::FIXED64>;

template<class ENUM_TYPE>
using enumeration = FieldTemplate<Field::FieldTypes::enumeration, ENUM_TYPE, WireFormatter::WireType::VARINT>;
using int32 = FieldTemplate<Field::FieldTypes::int32, int32_t, WireFormatter::WireType::VARINT, 5>;
using int64 = FieldTemplate<Field::FieldTypes::int64, int64_t, WireFormatter::WireType::VARINT, 10>;
using uint32 = FieldTemplate<Field::FieldTypes::uint32, uint32_t, WireFormatter::WireType::VARINT, 5>;
using uint64 = FieldTemplate<Field::FieldTypes::uint64, uint64_t, WireFormatter::WireType::VARINT, 10>;
using sint32 = FieldTemplate<Field::FieldTypes::sint32, int32_t, WireFormatter::WireType::VARINT, 5>;
using sint64 = FieldTemplate<Field::FieldTypes::sint64, int64_t, WireFormatter::WireType::VARINT, 10>;
using boolean = FieldTemplate<Field::FieldTypes::boolean, bool, WireFormatter::WireType::VARINT, 1>;
using fixed32 = FieldTemplate<Field::FieldTypes::fixed32, uint32_t, WireFormatter::WireType::FIXED32, 4>;
using fixed64 = FieldTemplate<Field::FieldTypes::fixed64, uint64_t, WireFormatter::WireType::FIXED64, 8>;
using sfixed32 = FieldTemplate<Field::FieldTypes::sfixed32, int32_t, WireFormatter::WireType::FIXED32, 4>;
using sfixed64 = FieldTemplate<Field::FieldTypes::sfixed64, int64_t, WireFormatter::WireType::FIXED64, 8>;
using floatfixed = FieldTemplate<Field::FieldTypes::floatfixed, float, WireFormatter::WireType::FIXED32, 4>;
using doublefixed = FieldTemplate<Field::FieldTypes::doublefixed, double, WireFormatter::WireType::FIXED64, 8>;

template<class ENUM_TYPE, uint32_t MAX_SER_SIZE>
using enumeration = FieldTemplate<Field::FieldTypes::enumeration, ENUM_TYPE, WireFormatter::WireType::VARINT, MAX_SER_SIZE>;

} // End of namespace EmbeddedProto.
#endif
2 changes: 1 addition & 1 deletion src/ReadBufferFixedSize.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ namespace EmbeddedProto
//! \see ::EmbeddedProto::ReadBufferInterface::get_size()
uint32_t get_size() const override
{
return write_index_;
return write_index_ - read_index_;
}

//! \see ::EmbeddedProto::ReadBufferInterface::get_max_size()
Expand Down
8 changes: 4 additions & 4 deletions src/RepeatedField.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,20 +55,20 @@ namespace EmbeddedProto
struct is_specialization_of_FieldTemplate : std::false_type {};

//! Definition of a trait to check if DATA_TYPE is a specialization of the FieldTemplate.
template<Field::FieldTypes F, typename V, WireFormatter::WireType W>
struct is_specialization_of_FieldTemplate<::EmbeddedProto::FieldTemplate<F,V,W>> : std::true_type {};
template<Field::FieldTypes F, typename V, WireFormatter::WireType W, uint32_t S>
struct is_specialization_of_FieldTemplate<::EmbeddedProto::FieldTemplate<F,V,W,S>> : std::true_type {};

//! This class only supports Field and FieldTemplate classes as template parameter.
static_assert(std::is_base_of<::EmbeddedProto::Field, DATA_TYPE>::value || is_specialization_of_FieldTemplate<DATA_TYPE>::value,
"A Field can only be used as template paramter.");

public:

//! Check how this field shoeld be serialized, packed or not.
static constexpr bool REPEATED_FIELD_IS_PACKED =
!(std::is_base_of<MessageInterface, DATA_TYPE>::value
|| std::is_base_of<internal::BaseStringBytes, DATA_TYPE>::value);

public:

RepeatedField() = default;
~RepeatedField() override = default;

Expand Down
26 changes: 26 additions & 0 deletions src/RepeatedFieldFixedSize.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,32 @@ namespace EmbeddedProto
//! Return a reference to the internal data storage array.
const std::array<DATA_TYPE, MAX_LENGTH>& get_data_const() const { return data_; }

//! When serialized with the most unfavrouble value how much bytes does this field need.
/*!
This function takes into account the field number and tag combination.
\param[in] field_number We need to include the field number. This because large field numbers require more bytes.
\return The number of bytes required at most.
*/
static constexpr uint32_t max_serialized_size(const uint32_t field_number)
{
return RepeatedField<DATA_TYPE>::REPEATED_FIELD_IS_PACKED
? max_packed_serialized_size(field_number)
: max_unpacked_serialized_size(field_number);
}

static constexpr uint32_t max_packed_serialized_size(const uint32_t field_number)
{
return WireFormatter::VarintSize(WireFormatter::MakeTag(field_number,
WireFormatter::WireType::LENGTH_DELIMITED))
+ WireFormatter::VarintSize(MAX_LENGTH)
+ (MAX_LENGTH * DATA_TYPE::max_serialized_size());
}

static constexpr uint32_t max_unpacked_serialized_size(const uint32_t field_number)
{
return MAX_LENGTH * DATA_TYPE::max_serialized_size(field_number);
}

private:

//! Number of item in the data array.
Expand Down
18 changes: 18 additions & 0 deletions src/WireFormatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,24 @@ namespace EmbeddedProto
FIXED32 = 5, //!< fixed32, sfixed32, float
};

//! Calculate the number of bytes a varint value will take.
/*!
\param[in] value The value of which to calculate the size.
\return The number of bytes required for serializing the varint.
*/
static constexpr uint32_t VarintSize(const uint64_t value)
{
return (value < (1ULL << 7)) ? 1 :
(value < (1ULL << 14)) ? 2 :
(value < (1ULL << 21)) ? 3 :
(value < (1ULL << 28)) ? 4 :
(value < (1ULL << 35)) ? 5 :
(value < (1ULL << 42)) ? 6 :
(value < (1ULL << 49)) ? 7 :
(value < (1ULL << 56)) ? 8 :
(value < (1ULL << 63)) ? 9 : 10;
}

//! Encode a signed integer using the zig zag method
/*!
As specified the right-shift must be arithmetic, hence the cast is after the shift. The
Expand Down