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