// Copyright (c) FIRST and other WPILib contributors. // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. #pragma once #include #include #include #include #include #include #include #include #include "wpi/MathExtras.h" #include "wpi/StringMap.h" #include "wpi/bit.h" namespace wpi { template class SmallVectorImpl; class DynamicStruct; class MutableDynamicStruct; class StructDescriptor; class StructDescriptorDatabase; /** * Known data types for raw struct dynamic fields (see StructFieldDescriptor). */ enum class StructFieldType { kBool, kChar, kInt8, kInt16, kInt32, kInt64, kUint8, kUint16, kUint32, kUint64, kFloat, kDouble, kStruct }; /** * Raw struct dynamic field descriptor. */ class StructFieldDescriptor { struct private_init {}; friend class DynamicStruct; friend class MutableDynamicStruct; friend class StructDescriptor; friend class StructDescriptorDatabase; public: /** * Set of enumerated values. */ using EnumValues = std::vector>; StructFieldDescriptor(const StructDescriptor* parent, std::string_view name, StructFieldType type, size_t size, size_t arraySize, unsigned int bitWidth, EnumValues enumValues, const StructDescriptor* structDesc, const private_init&); /** * Gets the dynamic struct this field is contained in. * * @return struct descriptor */ const StructDescriptor* GetParent() const { return m_parent; } /** * Gets the field name. * * @return field name */ const std::string& GetName() const { return m_name; } /** * Gets the field type. * * @return field type */ StructFieldType GetType() const { return m_type; } /** * Returns whether the field type is a signed integer. * * @return true if signed integer, false otherwise */ bool IsInt() const { return m_type == StructFieldType::kInt8 || m_type == StructFieldType::kInt16 || m_type == StructFieldType::kInt32 || m_type == StructFieldType::kInt64; } /** * Returns whether the field type is an unsigned integer. * * @return true if unsigned integer, false otherwise */ bool IsUint() const { return m_type == StructFieldType::kUint8 || m_type == StructFieldType::kUint16 || m_type == StructFieldType::kUint32 || m_type == StructFieldType::kUint64; } /** * Gets the underlying storage size of the field, in bytes. * * @return number of bytes */ size_t GetSize() const { return m_size; } /** * Gets the storage offset of the field, in bytes. * * @return number of bytes from the start of the struct */ size_t GetOffset() const { return m_offset; } /** * Gets the bit width of the field, in bits. * * @return number of bits */ unsigned int GetBitWidth() const { return m_bitWidth == 0 ? m_size * 8 : m_bitWidth; } /** * Gets the bit mask for the field. The mask is always the least significant * bits (it is not shifted). * * @return bit mask */ uint64_t GetBitMask() const { return m_bitMask; } /** * Gets the bit shift for the field (LSB=0). * * @return number of bits */ unsigned int GetBitShift() const { return m_bitShift; } /** * Returns whether the field is an array. * * @return true if array */ bool IsArray() const { return m_arraySize > 1; } /** * Gets the array size. Returns 1 if non-array. * * @return number of elements */ size_t GetArraySize() const { return m_arraySize; } /** * Returns whether the field has enumerated values. * * @return true if there are enumerated values */ bool HasEnum() const { return !m_enum.empty(); } /** * Gets the enumerated values. * * @return set of enumerated values */ const EnumValues& GetEnumValues() { return m_enum; } /** * Gets the struct descriptor for a struct data type. * * @return struct descriptor; returns null for non-struct */ const StructDescriptor* GetStruct() const { return m_struct; } /** * Gets the minimum unsigned integer value that can be stored in this field. * * @return minimum value */ uint64_t GetUintMin() const { return 0; } /** * Gets the maximum unsigned integer value that can be stored in this field. * * @return maximum value */ uint64_t GetUintMax() const { return m_bitMask; } /** * Gets the minimum signed integer value that can be stored in this field. * * @return minimum value */ int64_t GetIntMin() const { return static_cast(-(m_bitMask >> 1)) - 1; } /** * Gets the maximum signed integer value that can be stored in this field. * * @return maximum value */ int64_t GetIntMax() const { return m_bitMask >> 1; } /** * Returns whether the field is a bitfield. * * @return true if bitfield */ bool IsBitField() const { return m_bitShift != 0 || m_bitWidth != (m_size * 8); } private: // note: constructor fills in everything except offset and shift const StructDescriptor* m_parent; std::string m_name; size_t m_size; size_t m_offset = 0; size_t m_arraySize; // 1 for non-arrays EnumValues m_enum; const StructDescriptor* m_struct; // nullptr for non-structs uint64_t m_bitMask; StructFieldType m_type; unsigned int m_bitWidth; unsigned int m_bitShift = 0; }; /** * Raw struct dynamic struct descriptor. */ class StructDescriptor { struct private_init {}; friend class StructDescriptorDatabase; public: StructDescriptor(std::string_view name, const private_init&) : m_name{name} {} /** * Gets the struct name. * * @return name */ const std::string& GetName() const { return m_name; } /** * Gets the struct schema. * * @return schema */ const std::string& GetSchema() const { return m_schema; } /** * Returns whether the struct is valid (e.g. the struct is fully defined and * field offsets computed). * * @return true if valid */ bool IsValid() const { return m_valid; } /** * Returns the struct size, in bytes. Not valid unless IsValid() is true. * * @return size in bytes */ size_t GetSize() const { assert(m_valid); return m_size; } /** * Gets a field descriptor by name. Note the field cannot be accessed until * the struct is valid. * * @param name field name * @return field descriptor, or nullptr if not found */ const StructFieldDescriptor* FindFieldByName(std::string_view name) const; /** * Gets all field descriptors. Note fields cannot be accessed until the struct * is valid. * * @return field descriptors */ const std::vector& GetFields() const { return m_fields; } private: bool CheckCircular( wpi::SmallVectorImpl& stack) const; std::string CalculateOffsets( wpi::SmallVectorImpl& stack); std::string m_name; std::string m_schema; std::vector m_references; std::vector m_fields; StringMap m_fieldsByName; size_t m_size = 0; bool m_valid = false; }; /** * Database of raw struct dynamic descriptors. */ class StructDescriptorDatabase { public: /** * Adds a structure schema to the database. If the struct references other * structs that have not yet been added, it will not be valid until those * structs are also added. * * @param[in] name structure name * @param[in] schema structure schema * @param[out] err detailed error, if nullptr is returned * @return Added struct, or nullptr on error */ const StructDescriptor* Add(std::string_view name, std::string_view schema, std::string* err); /** * Finds a structure in the database by name. * * @param name structure name * @return struct descriptor, or nullptr if not found */ const StructDescriptor* Find(std::string_view name) const; private: StringMap> m_structs; }; /** * Dynamic (run-time) read-only access to a serialized raw struct. */ class DynamicStruct { public: /** * Constructs a new dynamic struct. Note: the passed data is a span; no copy * is made, so it's necessary for the lifetime of the referenced data to be * longer than this object. * * @param desc struct descriptor * @param data serialized data */ DynamicStruct(const StructDescriptor* desc, std::span data) : m_desc{desc}, m_data{data} {} /** * Gets the struct descriptor. * * @return struct descriptor */ const StructDescriptor* GetDescriptor() const { return m_desc; } /** * Gets the serialized data. * * @return data */ std::span GetData() const { return m_data; } /** * Gets a struct field descriptor by name. * * @param name field name * @return field descriptor, or nullptr if no field with that name exists */ const StructFieldDescriptor* FindField(std::string_view name) const { return m_desc->FindFieldByName(name); } /** * Gets the value of a boolean field. * * @param field field descriptor * @param arrIndex array index (must be less than field array size) * @return field value */ bool GetBoolField(const StructFieldDescriptor* field, size_t arrIndex = 0) const { assert(field->m_type == StructFieldType::kBool); return GetFieldImpl(field, arrIndex); } /** * Gets the value of a signed integer field. * * @param field field descriptor * @param arrIndex array index (must be less than field array size) * @return field value */ int64_t GetIntField(const StructFieldDescriptor* field, size_t arrIndex = 0) const { assert(field->IsInt()); return GetFieldImpl(field, arrIndex); } /** * Gets the value of an unsigned integer field. * * @param field field descriptor * @param arrIndex array index (must be less than field array size) * @return field value */ uint64_t GetUintField(const StructFieldDescriptor* field, size_t arrIndex = 0) const { assert(field->IsUint()); return GetFieldImpl(field, arrIndex); } /** * Gets the value of a float field. * * @param field field descriptor * @param arrIndex array index (must be less than field array size) * @return field value */ float GetFloatField(const StructFieldDescriptor* field, size_t arrIndex = 0) const { assert(field->m_type == StructFieldType::kFloat); return bit_cast( static_cast(GetFieldImpl(field, arrIndex))); } /** * Gets the value of a double field. * * @param field field descriptor * @param arrIndex array index (must be less than field array size) * @return field value */ double GetDoubleField(const StructFieldDescriptor* field, size_t arrIndex = 0) const { assert(field->m_type == StructFieldType::kDouble); return bit_cast(GetFieldImpl(field, arrIndex)); } /** * Gets the value of a char or char array field. * * @param field field descriptor * @return field value */ std::string_view GetStringField(const StructFieldDescriptor* field) const { assert(field->m_type == StructFieldType::kChar); assert(field->m_parent == m_desc); assert(m_desc->IsValid()); return {reinterpret_cast(&m_data[field->m_offset]), field->m_arraySize}; } /** * Gets the value of a struct field. * * @param field field descriptor * @param arrIndex array index (must be less than field array size) * @return field value */ DynamicStruct GetStructField(const StructFieldDescriptor* field, size_t arrIndex = 0) const { assert(field->m_type == StructFieldType::kStruct); assert(field->m_parent == m_desc); assert(m_desc->IsValid()); assert(arrIndex < field->m_arraySize); return DynamicStruct{field->m_struct, m_data.subspan(field->m_offset + arrIndex * field->m_struct->GetSize())}; } protected: const StructDescriptor* m_desc; private: uint64_t GetFieldImpl(const StructFieldDescriptor* field, size_t arrIndex) const; std::span m_data; }; /** * Dynamic (run-time) mutable access to a serialized raw struct. */ class MutableDynamicStruct : public DynamicStruct { public: /** * Constructs a new dynamic struct. Note: the passed data is a span; no copy * is made, so it's necessary for the lifetime of the referenced data to be * longer than this object. * * @param desc struct descriptor * @param data serialized data */ MutableDynamicStruct(const StructDescriptor* desc, std::span data) : DynamicStruct{desc, data}, m_data{data} {} /** * Gets the serialized data. * * @return data */ std::span GetData() { return m_data; } using DynamicStruct::GetData; /** * Overwrites the entire serialized struct by copying data from a span. * * @param data replacement data for the struct */ void SetData(std::span data); /** * Sets the value of a boolean field. * * @param field field descriptor * @param value field value * @param arrIndex array index (must be less than field array size) */ void SetBoolField(const StructFieldDescriptor* field, bool value, size_t arrIndex = 0) { assert(field->m_type == StructFieldType::kBool); SetFieldImpl(field, value ? 1 : 0, arrIndex); } /** * Sets the value of a signed integer field. * * @param field field descriptor * @param value field value * @param arrIndex array index (must be less than field array size) */ void SetIntField(const StructFieldDescriptor* field, int64_t value, size_t arrIndex = 0) { assert(field->IsInt()); SetFieldImpl(field, value, arrIndex); } /** * Sets the value of an unsigned integer field. * * @param field field descriptor * @param value field value * @param arrIndex array index (must be less than field array size) */ void SetUintField(const StructFieldDescriptor* field, uint64_t value, size_t arrIndex = 0) { assert(field->IsUint()); SetFieldImpl(field, value, arrIndex); } /** * Sets the value of a float field. * * @param field field descriptor * @param value field value * @param arrIndex array index (must be less than field array size) */ void SetFloatField(const StructFieldDescriptor* field, float value, size_t arrIndex = 0) { assert(field->m_type == StructFieldType::kFloat); SetFieldImpl(field, bit_cast(value), arrIndex); } /** * Sets the value of a double field. * * @param field field descriptor * @param value field value * @param arrIndex array index (must be less than field array size) */ void SetDoubleField(const StructFieldDescriptor* field, double value, size_t arrIndex = 0) { assert(field->m_type == StructFieldType::kDouble); SetFieldImpl(field, bit_cast(value), arrIndex); } /** * Sets the value of a char or char array field. * * @param field field descriptor * @param value field value */ void SetStringField(const StructFieldDescriptor* field, std::string_view value); /** * Sets the value of a struct field. * * @param field field descriptor * @param value field value * @param arrIndex array index (must be less than field array size) */ void SetStructField(const StructFieldDescriptor* field, const DynamicStruct& value, size_t arrIndex = 0); /** * Gets the value of a struct field. * * @param field field descriptor * @param arrIndex array index (must be less than field array size) * @return field value */ MutableDynamicStruct GetStructField(const StructFieldDescriptor* field, size_t arrIndex = 0) { assert(field->m_type == StructFieldType::kStruct); assert(field->m_parent == m_desc); assert(m_desc->IsValid()); assert(arrIndex < field->m_arraySize); return MutableDynamicStruct{ field->m_struct, m_data.subspan(field->m_offset + arrIndex * field->m_struct->GetSize())}; } using DynamicStruct::GetStructField; private: void SetFieldImpl(const StructFieldDescriptor* field, uint64_t value, size_t arrIndex); std::span m_data; }; namespace impl { struct DSOData { explicit DSOData(size_t size) : m_dataStore(size) {} explicit DSOData(std::span data) : m_dataStore{data.begin(), data.end()} {} std::vector m_dataStore; }; } // namespace impl /** * Dynamic (run-time) mutable access to a serialized raw struct, with internal * data storage. */ class DynamicStructObject : private impl::DSOData, public MutableDynamicStruct { /** * Constructs a new dynamic struct object. The descriptor must be valid. * * @param desc struct descriptor */ explicit DynamicStructObject(const StructDescriptor* desc) : DSOData{desc->GetSize()}, MutableDynamicStruct{desc, m_dataStore} {} /** * Constructs a new dynamic struct object. Makes a copy of the serialized * data so there are no lifetime constraints on the data parameter. * * @param desc struct descriptor * @param data serialized data */ DynamicStructObject(const StructDescriptor* desc, std::span data) : DSOData{data}, MutableDynamicStruct{desc, m_dataStore} { assert(data.size() >= desc->GetSize()); } // can't be movable due to span references DynamicStructObject(DynamicStructObject&&) = delete; DynamicStructObject& operator=(DynamicStructObject&&) = delete; }; } // namespace wpi