1 // Copyright (c) 2013- PPSSPP Project.
2 
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6 
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 // GNU General Public License 2.0 for more details.
11 
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14 
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17 
18 #pragma once
19 
20 #include <map>
21 #include <cstdint>
22 #include <cstring>
23 #include "Common/Log.h"
24 #include "Common/Serialize/Serializer.h"
25 
26 struct BufferQueue {
27 	BufferQueue(int size = 0x20000) {
28 		alloc(size);
29 	}
30 
~BufferQueueBufferQueue31 	~BufferQueue() {
32 		if (bufQueue)
33 			delete [] bufQueue;
34 	}
35 
allocBufferQueue36 	bool alloc(int size) {
37 		_assert_(size > 0);
38 		if (bufQueue)
39 			delete [] bufQueue;
40 		bufQueue = new unsigned char[size];
41 		bufQueueSize = size;
42 		clear();
43 		return true;
44 	}
45 
clearBufferQueue46 	void clear() {
47 		start = 0;
48 		end = 0;
49 		filled = 0;
50 	}
51 
getQueueSizeBufferQueue52 	inline int getQueueSize() {
53 		return filled;
54 	}
55 
getRemainSizeBufferQueue56 	inline int getRemainSize() {
57 		return bufQueueSize - getQueueSize();
58 	}
59 
60 	bool push(const unsigned char *buf, int addsize, s64 pts = 0) {
61 		int space = getRemainSize();
62 		if (space < addsize || addsize < 0)
63 			return false;
64 		savePts(pts);
65 		if (end + addsize <= bufQueueSize) {
66 			// If end is before start, there's enough space.  Otherwise, we're nearing the queue size.
67 			memcpy(bufQueue + end, buf, addsize);
68 			end += addsize;
69 			if (end == bufQueueSize)
70 				end = 0;
71 		} else {
72 			// Time to wrap end.  Fill what remains, then fill before start.
73 			_assert_(end >= start);
74 			int firstSize = bufQueueSize - end;
75 			memcpy(bufQueue + end, buf, firstSize);
76 			memcpy(bufQueue, buf + firstSize, addsize - firstSize);
77 			end = addsize - firstSize;
78 		}
79 		filled += addsize;
80 		verifyQueueSize();
81 		return true;
82 	}
83 
84 	int pop_front(unsigned char *buf, int wantedsize, s64 *pts = nullptr) {
85 		if (wantedsize <= 0)
86 			return 0;
87 		int bytesgot = getQueueSize();
88 		if (wantedsize < bytesgot)
89 			bytesgot = wantedsize;
90 		if (pts != nullptr) {
91 			*pts = findPts(bytesgot);
92 		}
93 
94 		int firstSize = bufQueueSize - start;
95 		if (buf) {
96 			if (bytesgot <= firstSize) {
97 				memcpy(buf, bufQueue + start, bytesgot);
98 			} else {
99 				memcpy(buf, bufQueue + start, firstSize);
100 				memcpy(buf + firstSize, bufQueue, bytesgot - firstSize);
101 			}
102 		}
103 		if (bytesgot <= firstSize)
104 			start += bytesgot;
105 		else
106 			start = bytesgot - firstSize;
107 		if (start == bufQueueSize)
108 			start = 0;
109 		filled -= bytesgot;
110 		verifyQueueSize();
111 		return bytesgot;
112 	}
113 
get_frontBufferQueue114 	int get_front(unsigned char *buf, int wantedsize) {
115 		if (wantedsize <= 0)
116 			return 0;
117 		int bytesgot = getQueueSize();
118 		if (wantedsize < bytesgot)
119 			bytesgot = wantedsize;
120 		int firstSize = bufQueueSize - start;
121 		if (bytesgot <= firstSize) {
122 			memcpy(buf, bufQueue + start, bytesgot);
123 		} else {
124 			memcpy(buf, bufQueue + start, firstSize);
125 			memcpy(buf + firstSize, bufQueue, bytesgot - firstSize);
126 		}
127 		return bytesgot;
128 	}
129 
130 	void DoState(PointerWrap &p);
131 
132 private:
savePtsBufferQueue133 	void savePts(u64 pts) {
134 		if (pts != 0) {
135 			ptsMarks[end] = pts;
136 		}
137 	}
138 
findPtsBufferQueue139 	u64 findPts(std::map<u32, s64>::iterator earliest, std::map<u32, s64>::iterator latest) {
140 		u64 pts = 0;
141 		// Take the first one, that is the pts of this packet.
142 		if (earliest != latest) {
143 			pts = earliest->second;
144 		}
145 		ptsMarks.erase(earliest, latest);
146 		return pts;
147 	}
148 
findPtsBufferQueue149 	u64 findPts(int packetSize) {
150 		auto earliest = ptsMarks.lower_bound(start);
151 		auto latest = ptsMarks.lower_bound(start + packetSize);
152 
153 		u64 pts = findPts(earliest, latest);
154 
155 		// If it wraps around, we have to look at the other half too.
156 		if (start + packetSize > bufQueueSize) {
157 			earliest = ptsMarks.begin();
158 			latest = ptsMarks.lower_bound(start + packetSize - bufQueueSize);
159 			// This also clears the range, so we always call on wrap.
160 			u64 latePts = findPts(earliest, latest);
161 			if (pts == 0)
162 				pts = latePts;
163 		}
164 
165 		return pts;
166 	}
167 
calcQueueSizeBufferQueue168 	inline int calcQueueSize() {
169 		if (end < start) {
170 			return bufQueueSize + end - start;
171 		}
172 		return end - start;
173 	}
174 
verifyQueueSizeBufferQueue175 	inline void verifyQueueSize() {
176 		_assert_(calcQueueSize() == filled || (end == start && filled == bufQueueSize));
177 	}
178 
179 	uint8_t *bufQueue = nullptr;
180 	// Model: end may be less than start, indicating the space between end and start is free.
181 	// If end equals start, we're empty.
182 	int start = 0, end = 0;
183 	int filled = 0;
184 	int bufQueueSize = 0;
185 
186 	std::map<u32, s64> ptsMarks;
187 };
188