state_wrapper.h (4869B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #pragma once 5 6 #include "common/fifo_queue.h" 7 #include "common/heap_array.h" 8 #include "common/types.h" 9 10 #include <cstring> 11 #include <deque> 12 #include <string> 13 #include <span> 14 #include <type_traits> 15 #include <vector> 16 17 class SmallStringBase; 18 19 class StateWrapper 20 { 21 public: 22 enum class Mode 23 { 24 Read, 25 Write 26 }; 27 28 StateWrapper(std::span<u8> data, Mode mode, u32 version); 29 StateWrapper(std::span<const u8> data, Mode mode, u32 version); 30 StateWrapper(const StateWrapper&) = delete; 31 ~StateWrapper(); 32 33 ALWAYS_INLINE bool HasError() const { return m_error; } 34 ALWAYS_INLINE bool IsReading() const { return (m_mode == Mode::Read); } 35 ALWAYS_INLINE bool IsWriting() const { return (m_mode == Mode::Write); } 36 ALWAYS_INLINE u32 GetVersion() const { return m_version; } 37 ALWAYS_INLINE size_t GetPosition() const { return m_pos; } 38 39 /// Overload for integral or floating-point types. Writes bytes as-is. 40 template<typename T, std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T>, int> = 0> 41 void Do(T* value_ptr) 42 { 43 if (m_mode == Mode::Read) 44 { 45 if (!ReadData(value_ptr, sizeof(T))) [[unlikely]] 46 *value_ptr = static_cast<T>(0); 47 } 48 else 49 { 50 WriteData(value_ptr, sizeof(T)); 51 } 52 } 53 54 /// Overload for enum types. Uses the underlying type. 55 template<typename T, std::enable_if_t<std::is_enum_v<T>, int> = 0> 56 void Do(T* value_ptr) 57 { 58 using TType = std::underlying_type_t<T>; 59 if (m_mode == Mode::Read) 60 { 61 TType temp; 62 if (!ReadData(&temp, sizeof(temp))) [[unlikely]] 63 temp = static_cast<TType>(0); 64 65 *value_ptr = static_cast<T>(temp); 66 } 67 else 68 { 69 const TType temp = static_cast<TType>(*value_ptr); 70 WriteData(&temp, sizeof(temp)); 71 } 72 } 73 74 /// Overload for POD types, such as structs. 75 template<typename T, std::enable_if_t<std::is_standard_layout_v<T> && std::is_trivial_v<T>, int> = 0> 76 void DoPOD(T* value_ptr) 77 { 78 if (m_mode == Mode::Read) 79 { 80 if (!ReadData(value_ptr, sizeof(T))) [[unlikely]] 81 std::memset(value_ptr, 0, sizeof(*value_ptr)); 82 } 83 else 84 { 85 WriteData(value_ptr, sizeof(T)); 86 } 87 } 88 89 template<typename T> 90 void DoArray(T* values, size_t count) 91 { 92 for (size_t i = 0; i < count; i++) 93 Do(&values[i]); 94 } 95 96 template<typename T> 97 void DoPODArray(T* values, size_t count) 98 { 99 for (size_t i = 0; i < count; i++) 100 DoPOD(&values[i]); 101 } 102 103 void DoBytes(void* data, size_t length); 104 void DoBytesEx(void* data, size_t length, u32 version_introduced, const void* default_value); 105 106 void Do(bool* value_ptr); 107 void Do(std::string* value_ptr); 108 void Do(std::string_view* value_ptr); 109 void Do(SmallStringBase* value_ptr); 110 111 template<typename T, size_t N> 112 void Do(std::array<T, N>* data) 113 { 114 DoArray(data->data(), data->size()); 115 } 116 117 template<typename T, size_t N> 118 void Do(FixedHeapArray<T, N>* data) 119 { 120 DoArray(data->data(), data->size()); 121 } 122 123 template<typename T> 124 void Do(std::vector<T>* data) 125 { 126 u32 length = static_cast<u32>(data->size()); 127 Do(&length); 128 if (m_mode == Mode::Read) 129 data->resize(length); 130 DoArray(data->data(), data->size()); 131 } 132 133 template<typename T> 134 void Do(std::deque<T>* data) 135 { 136 u32 length = static_cast<u32>(data->size()); 137 Do(&length); 138 if (m_mode == Mode::Read) 139 { 140 data->clear(); 141 for (u32 i = 0; i < length; i++) 142 { 143 T value; 144 Do(&value); 145 data->push_back(value); 146 } 147 } 148 else 149 { 150 for (u32 i = 0; i < length; i++) 151 Do(&data[i]); 152 } 153 } 154 155 template<typename T, u32 CAPACITY> 156 void Do(FIFOQueue<T, CAPACITY>* data) 157 { 158 u32 size = data->GetSize(); 159 Do(&size); 160 161 if (m_mode == Mode::Read) 162 { 163 T* temp = new T[size]; 164 DoArray(temp, size); 165 data->Clear(); 166 data->PushRange(temp, size); 167 delete[] temp; 168 } 169 else 170 { 171 for (u32 i = 0; i < size; i++) 172 { 173 T temp(data->Peek(i)); 174 Do(&temp); 175 } 176 } 177 } 178 179 bool DoMarker(const char* marker); 180 181 template<typename T> 182 void DoEx(T* data, u32 version_introduced, T default_value) 183 { 184 if (m_mode == Mode::Read && m_version < version_introduced) [[unlikely]] 185 { 186 *data = std::move(default_value); 187 return; 188 } 189 190 Do(data); 191 } 192 193 void SkipBytes(size_t count) 194 { 195 if (m_mode != Mode::Read) 196 { 197 m_error = true; 198 return; 199 } 200 201 m_error = (m_error || (m_pos + count) > m_size); 202 if (!m_error) [[likely]] 203 m_pos += count; 204 } 205 206 private: 207 bool ReadData(void* buf, size_t size); 208 bool WriteData(const void* buf, size_t size); 209 210 u8* m_data; 211 size_t m_size; 212 size_t m_pos = 0; 213 Mode m_mode; 214 u32 m_version; 215 bool m_error = false; 216 };