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