// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Containers/UnrealString.h" #include "TraceServices/Containers/Tables.h" #include "Templates/Function.h" #include "Common/PagedArray.h" #include "Common/SlabAllocator.h" namespace TraceServices { template static constexpr ETableColumnType GetColumnTypeFromNativeType(); template<> constexpr ETableColumnType GetColumnTypeFromNativeType() { return TableColumnType_Bool; } template<> constexpr ETableColumnType GetColumnTypeFromNativeType() { return TableColumnType_Int; }; template<> constexpr ETableColumnType GetColumnTypeFromNativeType() { return TableColumnType_Int; }; template<> constexpr ETableColumnType GetColumnTypeFromNativeType() { return TableColumnType_Int; }; template<> constexpr ETableColumnType GetColumnTypeFromNativeType() { return TableColumnType_Int; }; template<> constexpr ETableColumnType GetColumnTypeFromNativeType() { return TableColumnType_Int; }; template<> constexpr ETableColumnType GetColumnTypeFromNativeType() { return TableColumnType_Int; }; template<> constexpr ETableColumnType GetColumnTypeFromNativeType() { return TableColumnType_Int; }; template<> constexpr ETableColumnType GetColumnTypeFromNativeType() { return TableColumnType_Int; }; template<> constexpr ETableColumnType GetColumnTypeFromNativeType() { return TableColumnType_Float; }; template<> constexpr ETableColumnType GetColumnTypeFromNativeType() { return TableColumnType_Double; }; template<> constexpr ETableColumnType GetColumnTypeFromNativeType() { return TableColumnType_CString; } struct FColumnValueContainer { FColumnValueContainer(bool Value) { BoolValue = Value; } FColumnValueContainer(int8 Value) { IntValue = Value; } FColumnValueContainer(uint8 Value) { IntValue = Value; } FColumnValueContainer(int32 Value) { IntValue = Value; } FColumnValueContainer(uint32 Value) { IntValue = Value; } FColumnValueContainer(int64 Value) { IntValue = Value; } FColumnValueContainer(uint64 Value) { IntValue = int64(Value); } FColumnValueContainer(float Value) { FloatValue = Value; } FColumnValueContainer(double Value) { DoubleValue = Value; } FColumnValueContainer(const TCHAR* Value) { StringValue = Value; } union { bool BoolValue; int64 IntValue; float FloatValue; double DoubleValue; const TCHAR* StringValue; }; }; template class TTableLayout : public ITableLayout { public: template TTableLayout& AddColumn(ColumnNativeType RowType::* MemberVariableColumn, const TCHAR* ColumnName, uint32 DisplayHintFlags = 0) { Columns.Add({ ColumnName, GetColumnTypeFromNativeType(), DisplayHintFlags, [MemberVariableColumn](const RowType& Row) -> FColumnValueContainer { return FColumnValueContainer(Row.*MemberVariableColumn); } }); return *this; } template TTableLayout& AddColumn(ColumnNativeType(RowType::* MemberFunctionColumn)() const, const TCHAR* ColumnName, uint32 DisplayHintFlags = 0) { Columns.Add({ ColumnName, GetColumnTypeFromNativeType(), DisplayHintFlags, [MemberFunctionColumn](const RowType& Row) -> FColumnValueContainer { return FColumnValueContainer((Row.*MemberFunctionColumn)()); } }); return *this; } template TTableLayout& AddColumn(ColumnNativeType(*FunctionColumn)(const RowType&), const TCHAR* ColumnName, uint32 DisplayHintFlags = 0) { Columns.Add({ ColumnName, GetColumnTypeFromNativeType(), DisplayHintFlags, [FunctionColumn](const RowType& Row) -> FColumnValueContainer { return FColumnValueContainer(FunctionColumn(Row)); } }); return *this; } template TTableLayout& AddColumn(const TCHAR* ColumnName, TFunction Projector, uint32 DisplayHintFlags = 0) { Columns.Add({ ColumnName, GetColumnTypeFromNativeType(), DisplayHintFlags, Projector }); return *this; } uint64 GetColumnCount() const override { return static_cast(Columns.Num()); } const TCHAR* GetColumnName(uint64 ColumnIndex) const override { return *Columns[static_cast(ColumnIndex)].Name; } ETableColumnType GetColumnType(uint64 ColumnIndex) const override { return Columns[static_cast(ColumnIndex)].Type; } void SetColumnType(uint64 ColumnIndex, ETableColumnType ColumnType) { Columns[static_cast(ColumnIndex)].Type = ColumnType; } uint32 GetColumnDisplayHintFlags(uint64 ColumnIndex) const override { return Columns[static_cast(ColumnIndex)].DisplayHintFlags; } FColumnValueContainer GetColumnValue(const RowType& Row, uint64 ColumnIndex) const { return Columns[static_cast(ColumnIndex)].Projector(Row); } private: struct FColumnDeclaration { FString Name; ETableColumnType Type; uint32 DisplayHintFlags; TFunction Projector; }; TArray Columns; }; template class TTableReader : public ITableReader { public: TTableReader(const TTableLayout& InLayout, const TPagedArray& InRows) : Layout(InLayout) , Iterator(InRows.GetIteratorFromItem(0)) { CurrentRow = Iterator.GetCurrentItem(); } virtual bool IsValid() const override { return CurrentRow != nullptr; } virtual void NextRow() override { CurrentRow = Iterator.NextItem(); } virtual void SetRowIndex(uint64 RowIndex) override { CurrentRow = Iterator.SetPosition(RowIndex); } virtual const RowType* GetCurrentRow() const override { return CurrentRow; } virtual bool GetValueBool(uint64 ColumnIndex) const override { if (!CurrentRow) { return false; } ETableColumnType ColumnType = Layout.GetColumnType(ColumnIndex); switch (ColumnType) { case TableColumnType_Bool: return Layout.GetColumnValue(*CurrentRow, ColumnIndex).BoolValue; case TableColumnType_Int: return static_cast(Layout.GetColumnValue(*CurrentRow, ColumnIndex).IntValue); case TableColumnType_Float: return static_cast(Layout.GetColumnValue(*CurrentRow, ColumnIndex).FloatValue); case TableColumnType_Double: return static_cast(Layout.GetColumnValue(*CurrentRow, ColumnIndex).DoubleValue); } return false; } virtual int64 GetValueInt(uint64 ColumnIndex) const override { if (!CurrentRow) { return 0; } ETableColumnType ColumnType = Layout.GetColumnType(ColumnIndex); switch (ColumnType) { case TableColumnType_Bool: return static_cast(Layout.GetColumnValue(*CurrentRow, ColumnIndex).BoolValue); case TableColumnType_Int: return Layout.GetColumnValue(*CurrentRow, ColumnIndex).IntValue; case TableColumnType_Float: return static_cast(Layout.GetColumnValue(*CurrentRow, ColumnIndex).FloatValue); case TableColumnType_Double: return static_cast(Layout.GetColumnValue(*CurrentRow, ColumnIndex).DoubleValue); } return 0; } virtual float GetValueFloat(uint64 ColumnIndex) const override { if (!CurrentRow) { return 0.0; } ETableColumnType ColumnType = Layout.GetColumnType(ColumnIndex); switch (ColumnType) { case TableColumnType_Bool: return static_cast(Layout.GetColumnValue(*CurrentRow, ColumnIndex).BoolValue); case TableColumnType_Int: return static_cast(Layout.GetColumnValue(*CurrentRow, ColumnIndex).IntValue); case TableColumnType_Float: return Layout.GetColumnValue(*CurrentRow, ColumnIndex).FloatValue; case TableColumnType_Double: return static_cast(Layout.GetColumnValue(*CurrentRow, ColumnIndex).DoubleValue); } return 0.0; } virtual double GetValueDouble(uint64 ColumnIndex) const override { if (!CurrentRow) { return 0.0; } ETableColumnType ColumnType = Layout.GetColumnType(ColumnIndex); switch (ColumnType) { case TableColumnType_Bool: return static_cast(Layout.GetColumnValue(*CurrentRow, ColumnIndex).BoolValue); case TableColumnType_Int: return static_cast(Layout.GetColumnValue(*CurrentRow, ColumnIndex).IntValue); case TableColumnType_Float: return static_cast(Layout.GetColumnValue(*CurrentRow, ColumnIndex).FloatValue); case TableColumnType_Double: return Layout.GetColumnValue(*CurrentRow, ColumnIndex).DoubleValue; } return 0.0; } virtual const TCHAR* GetValueCString(uint64 ColumnIndex) const override { if (!CurrentRow) { return TEXT(""); } ETableColumnType ColumnType = Layout.GetColumnType(ColumnIndex); if (ColumnType == TableColumnType_CString) { const TCHAR* CStringValue = Layout.GetColumnValue(*CurrentRow, ColumnIndex).StringValue; return CStringValue ? CStringValue : TEXT(""); } return TEXT(""); } private: const TTableLayout& Layout; typename TPagedArray::TIterator Iterator; const RowType* CurrentRow; }; template class TTableBase : public ITable { public: TTableBase() = default; TTableBase(TTableLayout InLayout) : Layout(InLayout) { } virtual ~TTableBase() = default; const ITableLayout& GetLayout() const override { return Layout; } uint64 GetRowCount() const override { return GetRows().Num(); } ITableReader* CreateReader() const override { return new TTableReader(Layout, GetRows()); } TTableLayout& EditLayout() { return Layout; } private: virtual const TPagedArray& GetRows() const = 0; TTableLayout Layout; }; template class TTableView : public TTableBase { public: TTableView(const TPagedArray& InRows) : Rows(InRows) { } private: virtual const TPagedArray& GetRows() const override { return Rows; } const TPagedArray& Rows; }; template class TTable : public TTableBase { public: TTable() : Allocator(AllocatorSlabSize) , Rows(Allocator, 1024) { } TTable(TTableLayout Layout) : TTableBase(Layout) , Allocator(AllocatorSlabSize) , Rows(Allocator, 1024) { } RowType& AddRow() { return Rows.PushBack(); } protected: FSlabAllocator Allocator; private: virtual const TPagedArray& GetRows() const override { return Rows; } TPagedArray Rows; }; } // namespace TraceServices