diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj
index 2337cd7c5..b01ef1987 100644
--- a/src/common/common.vcxproj
+++ b/src/common/common.vcxproj
@@ -43,6 +43,7 @@
+
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters
index 385089087..d1e2b8784 100644
--- a/src/common/common.vcxproj.filters
+++ b/src/common/common.vcxproj.filters
@@ -19,6 +19,7 @@
+
diff --git a/src/common/fifo_queue.h b/src/common/fifo_queue.h
new file mode 100644
index 000000000..77fd64924
--- /dev/null
+++ b/src/common/fifo_queue.h
@@ -0,0 +1,181 @@
+#pragma once
+#include "YBaseLib/Assert.h"
+#include "types.h"
+#include
+#include
+
+#ifdef _MSC_VER
+#include // _aligned_malloc
+#else
+#include
+#endif
+
+template
+class FIFOQueue
+{
+public:
+ const T* GetDataPointer() const { return m_ptr; }
+ T* GetDataPointer() { return m_ptr; }
+ const T* GetFrontPointer() const { return m_ptr[m_head]; }
+ T* GetFrontPointer() { return m_ptr[m_head]; }
+ constexpr u32 GetCapacity() const { return CAPACITY; }
+ u32 GetSize() const { return m_size; }
+ bool IsEmpty() const { return m_size > 0; }
+ bool IsFull() const { return m_size == CAPACITY; }
+
+ void Clear()
+ {
+ m_head = 0;
+ m_tail = 0;
+ m_size = 0;
+ }
+
+ template
+ T& Emplace(Args&&... args)
+ {
+ T& ref = PushAndGetReference();
+ new (&ref) T(std::forward(args...));
+ return ref;
+ }
+
+ template, int> = 0>
+ T& Push(const T& value)
+ {
+ T& ref = PushAndGetReference();
+ std::memcpy(&ref, &value, sizeof(T));
+ return ref;
+ }
+
+ template, int> = 0>
+ T& Push(const T& value)
+ {
+ T& ref = PushAndGetReference();
+ new (&ref) T(value);
+ return ref;
+ }
+
+ // faster version of push_back_range for POD types which can be memcpy()ed
+ template, int> = 0>
+ void PushRange(const T* data, u32 size)
+ {
+ Assert((m_size + size) <= CAPACITY);
+ const u32 space_before_end = CAPACITY - m_tail;
+ const u32 size_before_end = (size > space_before_end) ? space_before_end : size;
+ const u32 size_after_end = size - size_before_end;
+
+ std::memcpy(&m_ptr[m_tail], data, sizeof(T) * size_before_end);
+ m_tail = (m_tail + size_before_end) % CAPACITY;
+
+ if (size_after_end > 0)
+ {
+ std::memcpy(&m_ptr[m_tail], data + size_before_end, sizeof(T) * size_after_end);
+ m_tail = (m_tail + size_after_end) % CAPACITY;
+ }
+
+ m_size += size;
+ }
+
+ template, int> = 0>
+ void PushRange(const T* data, u32 size)
+ {
+ Assert((m_size + size) <= CAPACITY);
+ while (size > 0)
+ {
+ T& ref = PushAndGetReference();
+ new (&ref) T(*data);
+ data++;
+ size--;
+ }
+ }
+
+ const T& Peek() const { return m_ptr[m_head]; }
+ const T& Peek(u32 offset) { return m_ptr[(m_head + offset) % CAPACITY]; }
+
+ void RemoveOne()
+ {
+ Assert(m_size > 0);
+ m_ptr[m_head].~T();
+ m_head = (m_head + 1) % CAPACITY;
+ m_size--;
+ }
+
+ // removes and returns moved value
+ T Pop()
+ {
+ Assert(m_size > 0);
+ T val = std::move(m_ptr[m_head]);
+ m_ptr[m_head].~T();
+ m_head = (m_head + 1) % CAPACITY;
+ m_size--;
+ return val;
+ }
+
+protected:
+ FIFOQueue() = default;
+
+ T& PushAndGetReference()
+ {
+ Assert(m_size < CAPACITY);
+ T& ref = m_ptr[m_tail];
+ m_tail = (m_tail + 1) % CAPACITY;
+ m_size++;
+ return ref;
+ }
+
+ T* m_ptr = nullptr;
+ u32 m_head = 0;
+ u32 m_tail = 0;
+ u32 m_size = 0;
+};
+
+template
+class InlineFIFOQueue : public FIFOQueue
+{
+public:
+ InlineFIFOQueue() : FIFOQueue() { m_ptr = m_inline_data; }
+
+private:
+ T m_inline_data[CAPACITY] = {};
+};
+
+template
+class HeapFIFOQueue : public FIFOQueue
+{
+public:
+ HeapFIFOQueue() : FIFOQueue()
+ {
+ if constexpr (ALIGNMENT > 0)
+ {
+#ifdef _MSC_VER
+ m_ptr = static_cast(_aligned_malloc(sizeof(T) * CAPACITY, ALIGNMENT));
+#else
+ m_ptr = static_cast(memalign(ALIGNMENT, sizeof(T) * CAPACITY));
+#endif
+ }
+ else
+ {
+ m_ptr = static_cast(std::malloc(sizeof(T) * CAPACITY));
+ }
+
+ if (!m_ptr)
+ Panic("Heap allocation failed");
+
+ std::memset(m_ptr, 0, sizeof(T) * CAPACITY);
+ }
+
+ ~HeapFIFOQueue()
+ {
+ if constexpr (ALIGNMENT > 0)
+ {
+#ifdef _MSC_VER
+ _aligned_free(m_ptr);
+#else
+ free(m_ptr);
+#endif
+ }
+ else
+ {
+ free(m_ptr);
+ }
+ }
+};
diff --git a/src/common/state_wrapper.h b/src/common/state_wrapper.h
index 35aa39a3f..d0a3cb4b7 100644
--- a/src/common/state_wrapper.h
+++ b/src/common/state_wrapper.h
@@ -1,5 +1,6 @@
#pragma once
#include "YBaseLib/ByteStream.h"
+#include "fifo_queue.h"
#include "types.h"
#include
#include
@@ -134,6 +135,29 @@ public:
}
}
+ template
+ void Do(FIFOQueue* data)
+ {
+ u32 size = data->GetSize();
+ Do(&size);
+
+ if (m_mode == Mode::Read)
+ {
+ T* temp = new T[size];
+ DoArray(temp, size);
+ data->PushRange(temp, size);
+ delete[] temp;
+ }
+ else
+ {
+ for (u32 i = 0; i < size; i++)
+ {
+ T temp(data->Peek(i));
+ Do(&temp);
+ }
+ }
+ }
+
bool DoMarker(const char* marker);
private: