2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "./SDL_internal.h"
24 #include "./SDL_dataqueue.h"
26 typedef struct SDL_DataQueuePacket
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;
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. */
44 SDL_FreeDataQueueList(SDL_DataQueuePacket *packet)
47 SDL_DataQueuePacket *next = packet->next;
54 /* this all expects that you managed thread safety elsewhere. */
57 SDL_NewDataQueue(const size_t _packetlen, const size_t initialslack)
59 SDL_DataQueue *queue = (SDL_DataQueue *) SDL_malloc(sizeof (SDL_DataQueue));
65 const size_t packetlen = _packetlen ? _packetlen : 1024;
66 const size_t wantpackets = (initialslack + (packetlen - 1)) / packetlen;
70 queue->packet_size = packetlen;
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. */
77 packet->next = queue->pool;
87 SDL_FreeDataQueue(SDL_DataQueue *queue)
90 SDL_FreeDataQueueList(queue->head);
91 SDL_FreeDataQueueList(queue->pool);
97 SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack)
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;
109 packet = queue->head;
111 /* merge the available pool and the current queue into one list. */
113 queue->tail->next = queue->pool;
115 packet = queue->pool;
118 /* Remove the queued packets from the device. */
121 queue->queued_bytes = 0;
122 queue->pool = packet;
124 /* Optionally keep some slack in the pool to reduce malloc pressure. */
125 for (i = 0; packet && (i < slackpackets); i++) {
127 packet = packet->next;
136 SDL_FreeDataQueueList(packet); /* free extra packets */
139 static SDL_DataQueuePacket *
140 AllocateDataQueuePacket(SDL_DataQueue *queue)
142 SDL_DataQueuePacket *packet;
144 SDL_assert(queue != NULL);
146 packet = queue->pool;
147 if (packet != NULL) {
148 /* we have one available in the pool. */
149 queue->pool = packet->next;
151 /* Have to allocate a new one! */
152 packet = (SDL_DataQueuePacket *) SDL_malloc(sizeof (SDL_DataQueuePacket) + queue->packet_size);
153 if (packet == NULL) {
159 packet->startpos = 0;
162 SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
163 if (queue->tail == NULL) {
164 queue->head = packet;
166 queue->tail->next = packet;
168 queue->tail = packet;
174 SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *_data, const size_t _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;
185 return SDL_InvalidParamError("queue");
188 orighead = queue->head;
189 origtail = queue->tail;
190 origlen = origtail ? origtail->datalen : 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);
199 /* uhoh, reset so we've queued nothing new, free what we can. */
201 packet = queue->head; /* whole queue. */
203 packet = origtail->next; /* what we added to existing queue. */
204 origtail->next = NULL;
205 origtail->datalen = origlen;
207 queue->head = orighead;
208 queue->tail = origtail;
211 SDL_FreeDataQueueList(packet); /* give back what we can. */
212 return SDL_OutOfMemory();
216 datalen = SDL_min(len, packet_size - packet->datalen);
217 SDL_memcpy(packet->data + packet->datalen, data, datalen);
220 packet->datalen += datalen;
221 queue->queued_bytes += datalen;
228 SDL_PeekIntoDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
231 Uint8 *buf = (Uint8 *) _buf;
233 SDL_DataQueuePacket *packet;
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);
244 SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
249 return (size_t) (ptr - buf);
253 SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
256 Uint8 *buf = (Uint8 *) _buf;
258 SDL_DataQueuePacket *packet;
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);
269 SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
270 packet->startpos += cpy;
272 queue->queued_bytes -= cpy;
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;
283 SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
285 if (queue->head == NULL) {
286 queue->tail = NULL; /* in case we drained the queue entirely. */
289 return (size_t) (ptr - buf);
293 SDL_CountDataQueue(SDL_DataQueue *queue)
295 return queue ? queue->queued_bytes : 0;
299 SDL_ReserveSpaceInDataQueue(SDL_DataQueue *queue, const size_t len)
301 SDL_DataQueuePacket *packet;
304 SDL_InvalidParamError("queue");
306 } else if (len == 0) {
307 SDL_InvalidParamError("len");
309 } else if (len > queue->packet_size) {
310 SDL_SetError("len is larger than packet size");
314 packet = queue->head;
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;
325 /* Need a fresh packet. */
326 packet = AllocateDataQueuePacket(queue);
332 packet->datalen = len;
333 queue->queued_bytes += len;
337 /* vi: set ts=4 sw=4 expandtab: */