#pragma once
#include <stdint.h>
#include <cassert>
#include <span>
#include <string>
#include <string_view>
#include <unordered_set>
#include <utility>
#include <vector>
#include "wpi/StringMap.h"
#include "wpi/bit.h"
namespace wpi {
template <typename T>
class SmallVectorImpl;
class DynamicStruct;
class MutableDynamicStruct;
class StructDescriptor;
class StructDescriptorDatabase;
enum class StructFieldType {
kBool,
kChar,
kInt8,
kInt16,
kInt32,
kInt64,
kUint8,
kUint16,
kUint32,
kUint64,
kFloat,
kDouble,
kStruct
};
class StructFieldDescriptor {
struct private_init {};
friend class DynamicStruct;
friend class MutableDynamicStruct;
friend class StructDescriptor;
friend class StructDescriptorDatabase;
public:
using EnumValues = std::vector<std::pair<std::string, int64_t>>;
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&);
const StructDescriptor* GetParent() const { return m_parent; }
const std::string& GetName() const { return m_name; }
StructFieldType GetType() const { return m_type; }
bool IsInt() const {
return m_type == StructFieldType::kInt8 ||
m_type == StructFieldType::kInt16 ||
m_type == StructFieldType::kInt32 ||
m_type == StructFieldType::kInt64;
}
bool IsUint() const {
return m_type == StructFieldType::kUint8 ||
m_type == StructFieldType::kUint16 ||
m_type == StructFieldType::kUint32 ||
m_type == StructFieldType::kUint64;
}
size_t GetSize() const { return m_size; }
size_t GetOffset() const { return m_offset; }
unsigned int GetBitWidth() const {
return m_bitWidth == 0 ? m_size * 8 : m_bitWidth;
}
uint64_t GetBitMask() const { return m_bitMask; }
unsigned int GetBitShift() const { return m_bitShift; }
bool IsArray() const { return m_arraySize > 1; }
size_t GetArraySize() const { return m_arraySize; }
bool HasEnum() const { return !m_enum.empty(); }
const EnumValues& GetEnumValues() { return m_enum; }
const StructDescriptor* GetStruct() const { return m_struct; }
uint64_t GetUintMin() const { return 0; }
uint64_t GetUintMax() const { return m_bitMask; }
int64_t GetIntMin() const {
return static_cast<int64_t>(-(m_bitMask >> 1)) - 1;
}
int64_t GetIntMax() const { return m_bitMask >> 1; }
bool IsBitField() const {
return (m_bitShift != 0 || m_bitWidth != (m_size * 8)) &&
m_struct == nullptr;
}
private:
const StructDescriptor* m_parent;
std::string m_name;
size_t m_size;
size_t m_offset = 0;
size_t m_arraySize; EnumValues m_enum;
const StructDescriptor* m_struct; uint64_t m_bitMask;
StructFieldType m_type;
unsigned int m_bitWidth;
unsigned int m_bitShift = 0;
};
class StructDescriptor {
struct private_init {};
friend class StructDescriptorDatabase;
public:
StructDescriptor(std::string_view name, const private_init&) : m_name{name} {}
const std::string& GetName() const { return m_name; }
const std::string& GetSchema() const { return m_schema; }
bool IsValid() const { return m_valid; }
size_t GetSize() const {
assert(m_valid);
return m_size;
}
const StructFieldDescriptor* FindFieldByName(std::string_view name) const;
const std::vector<StructFieldDescriptor>& GetFields() const {
return m_fields;
}
private:
bool CheckCircular(
wpi::SmallVectorImpl<const StructDescriptor*>& stack) const;
std::string CalculateOffsets(
wpi::SmallVectorImpl<const StructDescriptor*>& stack);
std::string m_name;
std::string m_schema;
std::vector<StructDescriptor*> m_references;
std::vector<StructFieldDescriptor> m_fields;
StringMap<size_t> m_fieldsByName;
size_t m_size = 0;
bool m_valid = false;
};
class StructDescriptorDatabase {
public:
const StructDescriptor* Add(std::string_view name, std::string_view schema,
std::string* err);
const StructDescriptor* Find(std::string_view name) const;
private:
StringMap<StructDescriptor> m_structs;
};
class DynamicStruct {
public:
DynamicStruct(const StructDescriptor* desc, std::span<const uint8_t> data)
: m_desc{desc}, m_data{data} {}
const StructDescriptor* GetDescriptor() const { return m_desc; }
std::span<const uint8_t> GetData() const { return m_data; }
const StructFieldDescriptor* FindField(std::string_view name) const {
return m_desc->FindFieldByName(name);
}
bool GetBoolField(const StructFieldDescriptor* field,
size_t arrIndex = 0) const {
assert(field->m_type == StructFieldType::kBool);
return GetFieldImpl(field, arrIndex);
}
int64_t GetIntField(const StructFieldDescriptor* field,
size_t arrIndex = 0) const {
assert(field->IsInt());
uint64_t raw = GetFieldImpl(field, arrIndex);
switch (field->m_size) {
case 1:
return static_cast<int8_t>(raw);
case 2:
return static_cast<int16_t>(raw);
case 4:
return static_cast<int32_t>(raw);
default:
return raw;
}
}
uint64_t GetUintField(const StructFieldDescriptor* field,
size_t arrIndex = 0) const {
assert(field->IsUint());
return GetFieldImpl(field, arrIndex);
}
float GetFloatField(const StructFieldDescriptor* field,
size_t arrIndex = 0) const {
assert(field->m_type == StructFieldType::kFloat);
return bit_cast<float>(
static_cast<uint32_t>(GetFieldImpl(field, arrIndex)));
}
double GetDoubleField(const StructFieldDescriptor* field,
size_t arrIndex = 0) const {
assert(field->m_type == StructFieldType::kDouble);
return bit_cast<double>(GetFieldImpl(field, arrIndex));
}
std::string_view GetStringField(const StructFieldDescriptor* field) const;
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<const uint8_t> m_data;
};
class MutableDynamicStruct : public DynamicStruct {
public:
MutableDynamicStruct(const StructDescriptor* desc, std::span<uint8_t> data)
: DynamicStruct{desc, data}, m_data{data} {}
std::span<uint8_t> GetData() { return m_data; }
using DynamicStruct::GetData;
void SetData(std::span<const uint8_t> data);
void SetBoolField(const StructFieldDescriptor* field, bool value,
size_t arrIndex = 0) {
assert(field->m_type == StructFieldType::kBool);
SetFieldImpl(field, value ? 1 : 0, arrIndex);
}
void SetIntField(const StructFieldDescriptor* field, int64_t value,
size_t arrIndex = 0) {
assert(field->IsInt());
SetFieldImpl(field, value, arrIndex);
}
void SetUintField(const StructFieldDescriptor* field, uint64_t value,
size_t arrIndex = 0) {
assert(field->IsUint());
SetFieldImpl(field, value, arrIndex);
}
void SetFloatField(const StructFieldDescriptor* field, float value,
size_t arrIndex = 0) {
assert(field->m_type == StructFieldType::kFloat);
SetFieldImpl(field, bit_cast<uint32_t>(value), arrIndex);
}
void SetDoubleField(const StructFieldDescriptor* field, double value,
size_t arrIndex = 0) {
assert(field->m_type == StructFieldType::kDouble);
SetFieldImpl(field, bit_cast<uint64_t>(value), arrIndex);
}
bool SetStringField(const StructFieldDescriptor* field,
std::string_view value);
void SetStructField(const StructFieldDescriptor* field,
const DynamicStruct& value, size_t arrIndex = 0);
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<uint8_t> m_data;
};
namespace impl {
struct DSOData {
explicit DSOData(size_t size) : m_dataStore(size) {}
explicit DSOData(std::span<const uint8_t> data)
: m_dataStore{data.begin(), data.end()} {}
std::vector<uint8_t> m_dataStore;
};
}
class DynamicStructObject : private impl::DSOData, public MutableDynamicStruct {
explicit DynamicStructObject(const StructDescriptor* desc)
: DSOData{desc->GetSize()}, MutableDynamicStruct{desc, m_dataStore} {}
DynamicStructObject(const StructDescriptor* desc,
std::span<const uint8_t> data)
: DSOData{data}, MutableDynamicStruct{desc, m_dataStore} {
assert(data.size() >= desc->GetSize());
}
DynamicStructObject(DynamicStructObject&&) = delete;
DynamicStructObject& operator=(DynamicStructObject&&) = delete;
};
}