summaryrefslogtreecommitdiff
path: root/Runtime/Containers/GrowingRingbuffer.h
blob: c4af13574d5ec90e8066d873f560ddd70be5b573 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#ifndef RUNTIME_CONTAINERS_GROWINGRINGBUFFER_H
#define RUNTIME_CONTAINERS_GROWINGRINGBUFFER_H

#include "Ringbuffer.h"
#include "Runtime/Allocator/MemoryMacros.h"
#include "Runtime/Threads/AtomicOps.h"

// --------------------------------------------------------------------
// A Ringbuffer with the ability to grow
// Concurrently supports one consumer and one producer
//
// The functions in this class should be synchronized with
// Ringbuffer to fit Ringbuffer template functions.
// --------------------------------------------------------------------
class GrowingRingbuffer
{
public:
	GrowingRingbuffer(MemLabelId label, UInt32 maxSize, UInt32 initialSize = 1024)
	: m_Label(label)
	, m_MinSize(initialSize)
	, m_MaxSize(maxSize)
	{
		InitializeBuffers();
	}

	~GrowingRingbuffer() { DeleteBuffers(); }

	void* WritePtr(UInt32* nBytes) const;
	void WritePtrUpdate(void* writePtr, UInt32 nBytesWritten);

	const void* ReadPtr(UInt32* nBytes) const;
	void ReadPtrUpdate(const void* readPtr, UInt32 nBytesRead);

	UInt32 GetAllocatedSize() const { return m_AllocatedSize; }
	UInt32 GetAvailableSize() const { return m_AvailableSize; }
	UInt32 GetFreeSize() const { return m_MaxSize - m_AvailableSize; }
	UInt32 GetSize() const { return m_MaxSize; }

	void Reset();

private:
	void InitializeBuffers();
	void DeleteBuffers();

	struct RingbufferLink:public Ringbuffer
	{
		RingbufferLink(MemLabelId label, UInt32 size) : Ringbuffer(label, size)	, next(NULL) { }
		RingbufferLink* volatile 	next;
	};

	UInt32						m_MaxSize;
	UInt32						m_MinSize;
	MemLabelId					m_Label;
	volatile UInt32				m_AllocatedSize;
	volatile UInt32				m_AvailableSize;
	RingbufferLink* volatile	m_ReadBuffer;
	RingbufferLink* volatile	m_WriteBuffer;
};

inline void GrowingRingbuffer::InitializeBuffers()
{
	m_WriteBuffer	= m_ReadBuffer = new RingbufferLink(m_Label, m_MinSize);
	m_AllocatedSize	= m_ReadBuffer->GetSize();
	m_AvailableSize = 0;
}

inline void GrowingRingbuffer::DeleteBuffers()
{
	RingbufferLink* buffer = m_ReadBuffer;
	while (buffer)
	{
		RingbufferLink* current = buffer;
		buffer = current->next;
		delete current;
	}
}

inline void GrowingRingbuffer::Reset()
{
	DeleteBuffers();
	InitializeBuffers();
}

FORCE_INLINE void* GrowingRingbuffer::WritePtr(UInt32* nBytes) const
{
	*nBytes = std::min(*nBytes, GetFreeSize());
	return m_WriteBuffer->WritePtr(nBytes);
}

FORCE_INLINE const void* GrowingRingbuffer::ReadPtr(UInt32* nBytes) const
{
	return m_ReadBuffer->ReadPtr(nBytes);
}

FORCE_INLINE void GrowingRingbuffer::WritePtrUpdate(void* writePtr, UInt32 nBytesWritten)
{
	m_WriteBuffer->WritePtrUpdate(writePtr, nBytesWritten);
	AtomicAdd((volatile int*) &m_AvailableSize, nBytesWritten);
	if (m_WriteBuffer->GetFreeSize() == 0 && GetFreeSize() > 0)
	{
		RingbufferLink* emptyBuffer = new RingbufferLink(m_Label, m_AllocatedSize);
		m_WriteBuffer->next = emptyBuffer;
		m_WriteBuffer = emptyBuffer;
		AtomicAdd((volatile int*) &m_AllocatedSize, emptyBuffer->GetSize());
	}
}

FORCE_INLINE void GrowingRingbuffer::ReadPtrUpdate(const void* readPtr, UInt32 nBytesRead)
{
	m_ReadBuffer->ReadPtrUpdate(readPtr, nBytesRead);
	AtomicSub((volatile int*) &m_AvailableSize, nBytesRead);
	if (m_ReadBuffer->next && m_ReadBuffer->GetAvailableSize() == 0)
	{
		AtomicSub((volatile int*) &m_AllocatedSize, m_ReadBuffer->GetSize());
		RingbufferLink* emptyBuffer = m_ReadBuffer;
		m_ReadBuffer = m_ReadBuffer->next;
		delete emptyBuffer;
	}
}

#endif // RUNTIME_CONTAINERS_GROWINGRINGBUFFER_H