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