feat: add alignment
This commit is contained in:
@@ -5,13 +5,68 @@
|
|||||||
|
|
||||||
namespace sp {
|
namespace sp {
|
||||||
|
|
||||||
template <typename ValueType>
|
/**
|
||||||
|
* \brief Example usage :
|
||||||
|
* sp::BitField<std::uint16_t, sp::Field<std::uint16_t, 12>, sp::Field<std::uint8_t, 4>>;
|
||||||
|
*/
|
||||||
|
template <typename TContainer, typename... TFields>
|
||||||
|
class BitField {
|
||||||
|
using AllFields = std::tuple<TFields...>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename... T>
|
||||||
|
BitField(const std::tuple<T...>& args) {
|
||||||
|
Apply<0, T...>(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
BitField() {}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
BitField& operator=(const std::tuple<T...>& args) {
|
||||||
|
Apply<0, T...>(args);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AllFields& GetFields() {
|
||||||
|
return m_Fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AllFields& GetFields() const {
|
||||||
|
return m_Fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t FIndex>
|
||||||
|
auto& GetField() {
|
||||||
|
return std::get<FIndex>(this->GetFields()).GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <int IOffset, typename... T, std::enable_if_t<IOffset >= sizeof...(T), bool> = true>
|
||||||
|
void Apply(const std::tuple<T...>& args) {}
|
||||||
|
|
||||||
|
template <int IOffset, typename... T, std::enable_if_t<!(IOffset >= sizeof...(T)), bool> = true>
|
||||||
|
void Apply(const std::tuple<T...>& args) {
|
||||||
|
this->GetField<IOffset>() = std::get<IOffset>(args);
|
||||||
|
Apply<1 + IOffset, T...>(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
TContainer m_Value;
|
||||||
|
AllFields m_Fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* \tparam ValueType the type of the value to store
|
||||||
|
* \tparam IAlignment 0 means no alignment
|
||||||
|
*/
|
||||||
|
template <typename ValueType, int IAlignment>
|
||||||
class Field {
|
class Field {
|
||||||
public:
|
public:
|
||||||
// Provide an access to the stored value
|
// Provide an access to the stored value
|
||||||
ValueType& GetValue() {
|
ValueType& GetValue() {
|
||||||
return m_Value;
|
return m_Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ValueType& GetValue() const {
|
const ValueType& GetValue() const {
|
||||||
return m_Value;
|
return m_Value;
|
||||||
}
|
}
|
||||||
@@ -21,6 +76,10 @@ class Field {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr int GetAlignment() const {
|
||||||
|
return IAlignment;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ValueType m_Value;
|
ValueType m_Value;
|
||||||
};
|
};
|
||||||
@@ -42,7 +101,7 @@ struct FieldsBuilder<std::tuple<TFields...>> {
|
|||||||
|
|
||||||
template <typename TField, typename... TFields>
|
template <typename TField, typename... TFields>
|
||||||
struct FieldsBuilder<TField, TFields...> {
|
struct FieldsBuilder<TField, TFields...> {
|
||||||
using Type = sp::tuple_cat_t<std::tuple<Field<TField>>, typename FieldsBuilder<TFields...>::Type>;
|
using Type = sp::tuple_cat_t<std::tuple<Field<TField, 0>>, typename FieldsBuilder<TFields...>::Type>;
|
||||||
};
|
};
|
||||||
} // namespace details
|
} // namespace details
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ class MessageImplFieldsBase : public TBase {
|
|||||||
AllFields& GetFields() {
|
AllFields& GetFields() {
|
||||||
return m_Fields;
|
return m_Fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AllFields& GetFields() const {
|
const AllFields& GetFields() const {
|
||||||
return m_Fields;
|
return m_Fields;
|
||||||
}
|
}
|
||||||
@@ -119,23 +120,75 @@ class MessageImplFieldsBase : public TBase {
|
|||||||
|
|
||||||
template <typename TBase>
|
template <typename TBase>
|
||||||
class MessageImplFieldsReadBase : public TBase {
|
class MessageImplFieldsReadBase : public TBase {
|
||||||
protected:
|
private:
|
||||||
|
// normal reading
|
||||||
|
template <typename TField>
|
||||||
|
void ReadField(Field<TField, 0>& field, DataBuffer& buffer) {
|
||||||
|
this->ReadData(field.GetValue(), buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reading field in bitfield
|
||||||
|
template <typename TFieldType, typename TField, int IAlignment>
|
||||||
|
void ReadField(Field<TField, IAlignment>& field, TFieldType data, std::size_t offset) {
|
||||||
|
static constexpr std::size_t TotalBitCount = sizeof(TFieldType) * 8;
|
||||||
|
// we suppose that the first element is at the highest bits
|
||||||
|
field.GetValue() = (data >> TotalBitCount - IAlignment - offset) & ((1 << IAlignment) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reading bitfield
|
||||||
|
template <typename TContainer, typename TFirst, typename... TFields>
|
||||||
|
void ReadField(Field<BitField<TContainer, TFirst, TFields...>, 0>& field, DataBuffer& buffer) {
|
||||||
|
TContainer data;
|
||||||
|
this->ReadData(data, buffer);
|
||||||
|
std::size_t offset = 0;
|
||||||
|
TupleForEach(
|
||||||
|
[data, this, &offset](auto& field) {
|
||||||
|
this->ReadField(field, data, offset);
|
||||||
|
offset += field.GetAlignment();
|
||||||
|
},
|
||||||
|
field.GetValue().GetFields());
|
||||||
|
}
|
||||||
|
|
||||||
void ReadImpl(DataBuffer& buffer) override {
|
void ReadImpl(DataBuffer& buffer) override {
|
||||||
auto& allFields = this->GetFields();
|
auto& allFields = this->GetFields();
|
||||||
std::apply([&buffer, this](auto& field) {
|
TupleForEach([&buffer, this](auto& field) { this->ReadField(field, buffer); }, allFields);
|
||||||
this->ReadData(field.GetValue(), buffer);
|
|
||||||
}, allFields);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename TBase>
|
template <typename TBase>
|
||||||
class MessageImplFieldsWriteBase : public TBase {
|
class MessageImplFieldsWriteBase : public TBase {
|
||||||
protected:
|
private:
|
||||||
|
// normal writing
|
||||||
|
template <typename TField>
|
||||||
|
void WriteField(Field<TField, 0>& field, DataBuffer& buffer) {
|
||||||
|
this->WriteData(field.GetValue(), buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// writing field in bitfield
|
||||||
|
template <typename TFieldType, typename TField, int IAlignment>
|
||||||
|
void WriteField(Field<TField, IAlignment>& field, TFieldType& data, std::size_t offset) {
|
||||||
|
static constexpr std::size_t TotalBitCount = sizeof(TFieldType) * 8;
|
||||||
|
// we suppose that the first element is at the highest bits
|
||||||
|
data |= (field.GetValue() & ((1 << IAlignment) - 1)) << TotalBitCount - IAlignment - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// writing bitfield
|
||||||
|
template <typename TContainer, typename TFirst, typename... TFields>
|
||||||
|
void WriteField(Field<BitField<TContainer, TFirst, TFields...>, 0>& field, DataBuffer& buffer) {
|
||||||
|
TContainer data = 0;
|
||||||
|
std::size_t offset = 0;
|
||||||
|
TupleForEach(
|
||||||
|
[&data, this, &offset](auto& field) {
|
||||||
|
this->WriteField(field, data, offset);
|
||||||
|
offset += field.GetAlignment();
|
||||||
|
},
|
||||||
|
field.GetValue().GetFields());
|
||||||
|
this->WriteData(data, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
void WriteImpl(DataBuffer& buffer) override {
|
void WriteImpl(DataBuffer& buffer) override {
|
||||||
auto& allFields = this->GetFields();
|
auto& allFields = this->GetFields();
|
||||||
std::apply([&buffer, this](auto& field) {
|
TupleForEach([&buffer, this](auto& field) { this->WriteField(field, buffer); }, allFields);
|
||||||
this->WriteData(field.GetValue(), buffer);
|
|
||||||
}, allFields);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user