icculus@10681
|
1 |
/*
|
icculus@10681
|
2 |
Simple DirectMedia Layer
|
slouken@11811
|
3 |
Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
|
icculus@10681
|
4 |
|
icculus@10681
|
5 |
This software is provided 'as-is', without any express or implied
|
icculus@10681
|
6 |
warranty. In no event will the authors be held liable for any damages
|
icculus@10681
|
7 |
arising from the use of this software.
|
icculus@10681
|
8 |
|
icculus@10681
|
9 |
Permission is granted to anyone to use this software for any purpose,
|
icculus@10681
|
10 |
including commercial applications, and to alter it and redistribute it
|
icculus@10681
|
11 |
freely, subject to the following restrictions:
|
icculus@10681
|
12 |
|
icculus@10681
|
13 |
1. The origin of this software must not be misrepresented; you must not
|
icculus@10681
|
14 |
claim that you wrote the original software. If you use this software
|
icculus@10681
|
15 |
in a product, an acknowledgment in the product documentation would be
|
icculus@10681
|
16 |
appreciated but is not required.
|
icculus@10681
|
17 |
2. Altered source versions must be plainly marked as such, and must not be
|
icculus@10681
|
18 |
misrepresented as being the original software.
|
icculus@10681
|
19 |
3. This notice may not be removed or altered from any source distribution.
|
icculus@10681
|
20 |
*/
|
icculus@10681
|
21 |
|
icculus@10681
|
22 |
#include "./SDL_internal.h"
|
icculus@10681
|
23 |
#include "SDL.h"
|
icculus@10681
|
24 |
#include "./SDL_dataqueue.h"
|
icculus@10681
|
25 |
#include "SDL_assert.h"
|
icculus@10681
|
26 |
|
icculus@10681
|
27 |
typedef struct SDL_DataQueuePacket
|
icculus@10681
|
28 |
{
|
icculus@10681
|
29 |
size_t datalen; /* bytes currently in use in this packet. */
|
icculus@10681
|
30 |
size_t startpos; /* bytes currently consumed in this packet. */
|
icculus@10681
|
31 |
struct SDL_DataQueuePacket *next; /* next item in linked list. */
|
icculus@10681
|
32 |
Uint8 data[SDL_VARIABLE_LENGTH_ARRAY]; /* packet data */
|
icculus@10681
|
33 |
} SDL_DataQueuePacket;
|
icculus@10681
|
34 |
|
icculus@10681
|
35 |
struct SDL_DataQueue
|
icculus@10681
|
36 |
{
|
icculus@10681
|
37 |
SDL_DataQueuePacket *head; /* device fed from here. */
|
icculus@10681
|
38 |
SDL_DataQueuePacket *tail; /* queue fills to here. */
|
icculus@10681
|
39 |
SDL_DataQueuePacket *pool; /* these are unused packets. */
|
icculus@10681
|
40 |
size_t packet_size; /* size of new packets */
|
icculus@10681
|
41 |
size_t queued_bytes; /* number of bytes of data in the queue. */
|
icculus@10681
|
42 |
};
|
icculus@10681
|
43 |
|
icculus@10681
|
44 |
static void
|
icculus@10681
|
45 |
SDL_FreeDataQueueList(SDL_DataQueuePacket *packet)
|
icculus@10681
|
46 |
{
|
icculus@10681
|
47 |
while (packet) {
|
icculus@10681
|
48 |
SDL_DataQueuePacket *next = packet->next;
|
icculus@10681
|
49 |
SDL_free(packet);
|
icculus@10681
|
50 |
packet = next;
|
icculus@10681
|
51 |
}
|
icculus@10681
|
52 |
}
|
icculus@10681
|
53 |
|
icculus@10681
|
54 |
|
icculus@10681
|
55 |
/* this all expects that you managed thread safety elsewhere. */
|
icculus@10681
|
56 |
|
icculus@10681
|
57 |
SDL_DataQueue *
|
icculus@10681
|
58 |
SDL_NewDataQueue(const size_t _packetlen, const size_t initialslack)
|
icculus@10681
|
59 |
{
|
icculus@10681
|
60 |
SDL_DataQueue *queue = (SDL_DataQueue *) SDL_malloc(sizeof (SDL_DataQueue));
|
icculus@10681
|
61 |
|
icculus@10681
|
62 |
if (!queue) {
|
icculus@10681
|
63 |
SDL_OutOfMemory();
|
icculus@10681
|
64 |
return NULL;
|
icculus@10681
|
65 |
} else {
|
icculus@10681
|
66 |
const size_t packetlen = _packetlen ? _packetlen : 1024;
|
icculus@10681
|
67 |
const size_t wantpackets = (initialslack + (packetlen - 1)) / packetlen;
|
icculus@10681
|
68 |
size_t i;
|
icculus@10681
|
69 |
|
icculus@10681
|
70 |
SDL_zerop(queue);
|
icculus@10681
|
71 |
queue->packet_size = packetlen;
|
icculus@10681
|
72 |
|
icculus@10681
|
73 |
for (i = 0; i < wantpackets; i++) {
|
icculus@10681
|
74 |
SDL_DataQueuePacket *packet = (SDL_DataQueuePacket *) SDL_malloc(sizeof (SDL_DataQueuePacket) + packetlen);
|
icculus@10681
|
75 |
if (packet) { /* don't care if this fails, we'll deal later. */
|
icculus@10681
|
76 |
packet->datalen = 0;
|
icculus@10681
|
77 |
packet->startpos = 0;
|
icculus@10681
|
78 |
packet->next = queue->pool;
|
icculus@10681
|
79 |
queue->pool = packet;
|
icculus@10681
|
80 |
}
|
icculus@10681
|
81 |
}
|
icculus@10681
|
82 |
}
|
icculus@10681
|
83 |
|
icculus@10681
|
84 |
return queue;
|
icculus@10681
|
85 |
}
|
icculus@10681
|
86 |
|
icculus@10681
|
87 |
void
|
icculus@10681
|
88 |
SDL_FreeDataQueue(SDL_DataQueue *queue)
|
icculus@10681
|
89 |
{
|
icculus@10681
|
90 |
if (queue) {
|
icculus@10681
|
91 |
SDL_FreeDataQueueList(queue->head);
|
icculus@10681
|
92 |
SDL_FreeDataQueueList(queue->pool);
|
icculus@10681
|
93 |
SDL_free(queue);
|
icculus@10681
|
94 |
}
|
icculus@10681
|
95 |
}
|
icculus@10681
|
96 |
|
icculus@10681
|
97 |
void
|
icculus@10681
|
98 |
SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack)
|
icculus@10681
|
99 |
{
|
icculus@10681
|
100 |
const size_t packet_size = queue ? queue->packet_size : 1;
|
icculus@10681
|
101 |
const size_t slackpackets = (slack + (packet_size-1)) / packet_size;
|
icculus@10681
|
102 |
SDL_DataQueuePacket *packet;
|
icculus@10681
|
103 |
SDL_DataQueuePacket *prev = NULL;
|
icculus@10681
|
104 |
size_t i;
|
icculus@10681
|
105 |
|
icculus@10681
|
106 |
if (!queue) {
|
icculus@10681
|
107 |
return;
|
icculus@10681
|
108 |
}
|
icculus@10681
|
109 |
|
icculus@10681
|
110 |
packet = queue->head;
|
icculus@10681
|
111 |
|
icculus@10681
|
112 |
/* merge the available pool and the current queue into one list. */
|
icculus@10681
|
113 |
if (packet) {
|
icculus@10681
|
114 |
queue->tail->next = queue->pool;
|
icculus@10681
|
115 |
} else {
|
icculus@10681
|
116 |
packet = queue->pool;
|
icculus@10681
|
117 |
}
|
icculus@10681
|
118 |
|
icculus@10681
|
119 |
/* Remove the queued packets from the device. */
|
icculus@10681
|
120 |
queue->tail = NULL;
|
icculus@10681
|
121 |
queue->head = NULL;
|
icculus@10681
|
122 |
queue->queued_bytes = 0;
|
icculus@10681
|
123 |
queue->pool = packet;
|
icculus@10681
|
124 |
|
icculus@10681
|
125 |
/* Optionally keep some slack in the pool to reduce malloc pressure. */
|
icculus@10681
|
126 |
for (i = 0; packet && (i < slackpackets); i++) {
|
icculus@10681
|
127 |
prev = packet;
|
icculus@10681
|
128 |
packet = packet->next;
|
icculus@10681
|
129 |
}
|
icculus@10681
|
130 |
|
icculus@10681
|
131 |
if (prev) {
|
icculus@10681
|
132 |
prev->next = NULL;
|
icculus@10681
|
133 |
} else {
|
icculus@10681
|
134 |
queue->pool = NULL;
|
icculus@10681
|
135 |
}
|
icculus@10681
|
136 |
|
icculus@10681
|
137 |
SDL_FreeDataQueueList(packet); /* free extra packets */
|
icculus@10681
|
138 |
}
|
icculus@10681
|
139 |
|
icculus@10743
|
140 |
static SDL_DataQueuePacket *
|
icculus@10743
|
141 |
AllocateDataQueuePacket(SDL_DataQueue *queue)
|
icculus@10743
|
142 |
{
|
icculus@10743
|
143 |
SDL_DataQueuePacket *packet;
|
icculus@10743
|
144 |
|
icculus@10743
|
145 |
SDL_assert(queue != NULL);
|
icculus@10743
|
146 |
|
icculus@10743
|
147 |
packet = queue->pool;
|
icculus@10743
|
148 |
if (packet != NULL) {
|
icculus@10743
|
149 |
/* we have one available in the pool. */
|
icculus@10743
|
150 |
queue->pool = packet->next;
|
icculus@10743
|
151 |
} else {
|
icculus@10743
|
152 |
/* Have to allocate a new one! */
|
icculus@10743
|
153 |
packet = (SDL_DataQueuePacket *) SDL_malloc(sizeof (SDL_DataQueuePacket) + queue->packet_size);
|
icculus@10743
|
154 |
if (packet == NULL) {
|
icculus@10743
|
155 |
return NULL;
|
icculus@10743
|
156 |
}
|
icculus@10743
|
157 |
}
|
icculus@10743
|
158 |
|
icculus@10743
|
159 |
packet->datalen = 0;
|
icculus@10743
|
160 |
packet->startpos = 0;
|
icculus@10743
|
161 |
packet->next = NULL;
|
icculus@10743
|
162 |
|
icculus@10743
|
163 |
SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
|
icculus@10743
|
164 |
if (queue->tail == NULL) {
|
icculus@10743
|
165 |
queue->head = packet;
|
icculus@10743
|
166 |
} else {
|
icculus@10743
|
167 |
queue->tail->next = packet;
|
icculus@10743
|
168 |
}
|
icculus@10743
|
169 |
queue->tail = packet;
|
icculus@10743
|
170 |
return packet;
|
icculus@10743
|
171 |
}
|
icculus@10743
|
172 |
|
icculus@10743
|
173 |
|
icculus@10681
|
174 |
int
|
icculus@10681
|
175 |
SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *_data, const size_t _len)
|
icculus@10681
|
176 |
{
|
icculus@10681
|
177 |
size_t len = _len;
|
icculus@10681
|
178 |
const Uint8 *data = (const Uint8 *) _data;
|
icculus@10681
|
179 |
const size_t packet_size = queue ? queue->packet_size : 0;
|
icculus@10681
|
180 |
SDL_DataQueuePacket *orighead;
|
icculus@10681
|
181 |
SDL_DataQueuePacket *origtail;
|
icculus@10681
|
182 |
size_t origlen;
|
icculus@10681
|
183 |
size_t datalen;
|
icculus@10681
|
184 |
|
icculus@10681
|
185 |
if (!queue) {
|
icculus@10681
|
186 |
return SDL_InvalidParamError("queue");
|
icculus@10681
|
187 |
}
|
icculus@10681
|
188 |
|
icculus@10681
|
189 |
orighead = queue->head;
|
icculus@10681
|
190 |
origtail = queue->tail;
|
icculus@10681
|
191 |
origlen = origtail ? origtail->datalen : 0;
|
icculus@10681
|
192 |
|
icculus@10681
|
193 |
while (len > 0) {
|
icculus@10681
|
194 |
SDL_DataQueuePacket *packet = queue->tail;
|
icculus@10681
|
195 |
SDL_assert(!packet || (packet->datalen <= packet_size));
|
icculus@10681
|
196 |
if (!packet || (packet->datalen >= packet_size)) {
|
icculus@10681
|
197 |
/* tail packet missing or completely full; we need a new packet. */
|
icculus@10743
|
198 |
packet = AllocateDataQueuePacket(queue);
|
icculus@10743
|
199 |
if (!packet) {
|
icculus@10743
|
200 |
/* uhoh, reset so we've queued nothing new, free what we can. */
|
icculus@10743
|
201 |
if (!origtail) {
|
icculus@10743
|
202 |
packet = queue->head; /* whole queue. */
|
icculus@10743
|
203 |
} else {
|
icculus@10743
|
204 |
packet = origtail->next; /* what we added to existing queue. */
|
icculus@10743
|
205 |
origtail->next = NULL;
|
icculus@10743
|
206 |
origtail->datalen = origlen;
|
icculus@10743
|
207 |
}
|
icculus@10743
|
208 |
queue->head = orighead;
|
icculus@10743
|
209 |
queue->tail = origtail;
|
icculus@10743
|
210 |
queue->pool = NULL;
|
icculus@10681
|
211 |
|
icculus@10743
|
212 |
SDL_FreeDataQueueList(packet); /* give back what we can. */
|
icculus@10743
|
213 |
return SDL_OutOfMemory();
|
icculus@10681
|
214 |
}
|
icculus@10681
|
215 |
}
|
icculus@10681
|
216 |
|
icculus@10681
|
217 |
datalen = SDL_min(len, packet_size - packet->datalen);
|
icculus@10681
|
218 |
SDL_memcpy(packet->data + packet->datalen, data, datalen);
|
icculus@10681
|
219 |
data += datalen;
|
icculus@10681
|
220 |
len -= datalen;
|
icculus@10681
|
221 |
packet->datalen += datalen;
|
icculus@10681
|
222 |
queue->queued_bytes += datalen;
|
icculus@10681
|
223 |
}
|
icculus@10681
|
224 |
|
icculus@10681
|
225 |
return 0;
|
icculus@10681
|
226 |
}
|
icculus@10681
|
227 |
|
icculus@10681
|
228 |
size_t
|
icculus@11582
|
229 |
SDL_PeekIntoDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
|
icculus@11582
|
230 |
{
|
icculus@11582
|
231 |
size_t len = _len;
|
icculus@11582
|
232 |
Uint8 *buf = (Uint8 *) _buf;
|
icculus@11582
|
233 |
Uint8 *ptr = buf;
|
icculus@11582
|
234 |
SDL_DataQueuePacket *packet;
|
icculus@11582
|
235 |
|
icculus@11582
|
236 |
if (!queue) {
|
icculus@11582
|
237 |
return 0;
|
icculus@11582
|
238 |
}
|
icculus@11582
|
239 |
|
icculus@11582
|
240 |
for (packet = queue->head; len && packet; packet = packet->next) {
|
icculus@11582
|
241 |
const size_t avail = packet->datalen - packet->startpos;
|
icculus@11582
|
242 |
const size_t cpy = SDL_min(len, avail);
|
icculus@11582
|
243 |
SDL_assert(queue->queued_bytes >= avail);
|
icculus@11582
|
244 |
|
icculus@11582
|
245 |
SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
|
icculus@11582
|
246 |
ptr += cpy;
|
icculus@11582
|
247 |
len -= cpy;
|
icculus@11582
|
248 |
}
|
icculus@11582
|
249 |
|
icculus@11582
|
250 |
return (size_t) (ptr - buf);
|
icculus@11582
|
251 |
}
|
icculus@11582
|
252 |
|
icculus@11582
|
253 |
size_t
|
icculus@10681
|
254 |
SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
|
icculus@10681
|
255 |
{
|
icculus@10681
|
256 |
size_t len = _len;
|
icculus@10681
|
257 |
Uint8 *buf = (Uint8 *) _buf;
|
icculus@10681
|
258 |
Uint8 *ptr = buf;
|
icculus@10681
|
259 |
SDL_DataQueuePacket *packet;
|
icculus@10681
|
260 |
|
icculus@10681
|
261 |
if (!queue) {
|
icculus@10681
|
262 |
return 0;
|
icculus@10681
|
263 |
}
|
icculus@10681
|
264 |
|
icculus@10681
|
265 |
while ((len > 0) && ((packet = queue->head) != NULL)) {
|
icculus@10681
|
266 |
const size_t avail = packet->datalen - packet->startpos;
|
icculus@10681
|
267 |
const size_t cpy = SDL_min(len, avail);
|
icculus@10681
|
268 |
SDL_assert(queue->queued_bytes >= avail);
|
icculus@10681
|
269 |
|
icculus@10681
|
270 |
SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
|
icculus@10681
|
271 |
packet->startpos += cpy;
|
icculus@10681
|
272 |
ptr += cpy;
|
icculus@10681
|
273 |
queue->queued_bytes -= cpy;
|
icculus@10681
|
274 |
len -= cpy;
|
icculus@10681
|
275 |
|
icculus@10681
|
276 |
if (packet->startpos == packet->datalen) { /* packet is done, put it in the pool. */
|
icculus@10681
|
277 |
queue->head = packet->next;
|
icculus@10681
|
278 |
SDL_assert((packet->next != NULL) || (packet == queue->tail));
|
icculus@10681
|
279 |
packet->next = queue->pool;
|
icculus@10681
|
280 |
queue->pool = packet;
|
icculus@10681
|
281 |
}
|
icculus@10681
|
282 |
}
|
icculus@10681
|
283 |
|
icculus@10681
|
284 |
SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
|
icculus@10681
|
285 |
|
icculus@10681
|
286 |
if (queue->head == NULL) {
|
icculus@10681
|
287 |
queue->tail = NULL; /* in case we drained the queue entirely. */
|
icculus@10681
|
288 |
}
|
icculus@10681
|
289 |
|
icculus@10681
|
290 |
return (size_t) (ptr - buf);
|
icculus@10681
|
291 |
}
|
icculus@10681
|
292 |
|
icculus@10681
|
293 |
size_t
|
icculus@10681
|
294 |
SDL_CountDataQueue(SDL_DataQueue *queue)
|
icculus@10681
|
295 |
{
|
icculus@10681
|
296 |
return queue ? queue->queued_bytes : 0;
|
icculus@10681
|
297 |
}
|
icculus@10681
|
298 |
|
icculus@10743
|
299 |
void *
|
icculus@10743
|
300 |
SDL_ReserveSpaceInDataQueue(SDL_DataQueue *queue, const size_t len)
|
icculus@10743
|
301 |
{
|
icculus@10743
|
302 |
SDL_DataQueuePacket *packet;
|
icculus@10743
|
303 |
|
icculus@10743
|
304 |
if (!queue) {
|
icculus@10743
|
305 |
SDL_InvalidParamError("queue");
|
icculus@10743
|
306 |
return NULL;
|
icculus@10743
|
307 |
} else if (len == 0) {
|
icculus@10743
|
308 |
SDL_InvalidParamError("len");
|
icculus@10743
|
309 |
return NULL;
|
icculus@10743
|
310 |
} else if (len > queue->packet_size) {
|
icculus@10743
|
311 |
SDL_SetError("len is larger than packet size");
|
icculus@10743
|
312 |
return NULL;
|
icculus@10743
|
313 |
}
|
icculus@10743
|
314 |
|
icculus@10743
|
315 |
packet = queue->head;
|
icculus@10743
|
316 |
if (packet) {
|
icculus@10743
|
317 |
const size_t avail = queue->packet_size - packet->datalen;
|
icculus@10743
|
318 |
if (len <= avail) { /* we can use the space at end of this packet. */
|
icculus@10743
|
319 |
void *retval = packet->data + packet->datalen;
|
icculus@10743
|
320 |
packet->datalen += len;
|
icculus@10743
|
321 |
queue->queued_bytes += len;
|
icculus@10743
|
322 |
return retval;
|
icculus@10743
|
323 |
}
|
icculus@10743
|
324 |
}
|
icculus@10743
|
325 |
|
icculus@10743
|
326 |
/* Need a fresh packet. */
|
icculus@10743
|
327 |
packet = AllocateDataQueuePacket(queue);
|
icculus@10743
|
328 |
if (!packet) {
|
icculus@10743
|
329 |
SDL_OutOfMemory();
|
icculus@10743
|
330 |
return NULL;
|
icculus@10743
|
331 |
}
|
icculus@10743
|
332 |
|
icculus@10743
|
333 |
packet->datalen = len;
|
icculus@10743
|
334 |
queue->queued_bytes += len;
|
icculus@10743
|
335 |
return packet->data;
|
icculus@10743
|
336 |
}
|
icculus@10743
|
337 |
|
icculus@10681
|
338 |
/* vi: set ts=4 sw=4 expandtab: */
|
icculus@10681
|
339 |
|