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