src/SDL_dataqueue.c
author Sam Lantinga
Sun, 01 Jan 2017 18:33:28 -0800
changeset 10737 3406a0f8b041
parent 10681 34b25c97b17f
child 10743 8ccdeceaae6d
permissions -rw-r--r--
Updated copyright for 2017
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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
    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 *
    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
    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
    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 int
   141 SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *_data, const size_t _len)
   142 {
   143     size_t len = _len;
   144     const Uint8 *data = (const Uint8 *) _data;
   145     const size_t packet_size = queue ? queue->packet_size : 0;
   146     SDL_DataQueuePacket *orighead;
   147     SDL_DataQueuePacket *origtail;
   148     size_t origlen;
   149     size_t datalen;
   150 
   151     if (!queue) {
   152         return SDL_InvalidParamError("queue");
   153     }
   154 
   155     orighead = queue->head;
   156     origtail = queue->tail;
   157     origlen = origtail ? origtail->datalen : 0;
   158 
   159     while (len > 0) {
   160         SDL_DataQueuePacket *packet = queue->tail;
   161         SDL_assert(!packet || (packet->datalen <= packet_size));
   162         if (!packet || (packet->datalen >= packet_size)) {
   163             /* tail packet missing or completely full; we need a new packet. */
   164             packet = queue->pool;
   165             if (packet != NULL) {
   166                 /* we have one available in the pool. */
   167                 queue->pool = packet->next;
   168             } else {
   169                 /* Have to allocate a new one! */
   170                 packet = (SDL_DataQueuePacket *) SDL_malloc(sizeof (SDL_DataQueuePacket) + packet_size);
   171                 if (packet == NULL) {
   172                     /* uhoh, reset so we've queued nothing new, free what we can. */
   173                     if (!origtail) {
   174                         packet = queue->head;  /* whole queue. */
   175                     } else {
   176                         packet = origtail->next;  /* what we added to existing queue. */
   177                         origtail->next = NULL;
   178                         origtail->datalen = origlen;
   179                     }
   180                     queue->head = orighead;
   181                     queue->tail = origtail;
   182                     queue->pool = NULL;
   183 
   184                     SDL_FreeDataQueueList(packet);  /* give back what we can. */
   185 
   186                     return SDL_OutOfMemory();
   187                 }
   188             }
   189             packet->datalen = 0;
   190             packet->startpos = 0;
   191             packet->next = NULL;
   192                 
   193             SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
   194             if (queue->tail == NULL) {
   195                 queue->head = packet;
   196             } else {
   197                 queue->tail->next = packet;
   198             }
   199             queue->tail = packet;
   200         }
   201 
   202         datalen = SDL_min(len, packet_size - packet->datalen);
   203         SDL_memcpy(packet->data + packet->datalen, data, datalen);
   204         data += datalen;
   205         len -= datalen;
   206         packet->datalen += datalen;
   207         queue->queued_bytes += datalen;
   208     }
   209 
   210     return 0;
   211 }
   212 
   213 size_t
   214 SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
   215 {
   216     size_t len = _len;
   217     Uint8 *buf = (Uint8 *) _buf;
   218     Uint8 *ptr = buf;
   219     SDL_DataQueuePacket *packet;
   220 
   221     if (!queue) {
   222         return 0;
   223     }
   224 
   225     while ((len > 0) && ((packet = queue->head) != NULL)) {
   226         const size_t avail = packet->datalen - packet->startpos;
   227         const size_t cpy = SDL_min(len, avail);
   228         SDL_assert(queue->queued_bytes >= avail);
   229 
   230         SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
   231         packet->startpos += cpy;
   232         ptr += cpy;
   233         queue->queued_bytes -= cpy;
   234         len -= cpy;
   235 
   236         if (packet->startpos == packet->datalen) {  /* packet is done, put it in the pool. */
   237             queue->head = packet->next;
   238             SDL_assert((packet->next != NULL) || (packet == queue->tail));
   239             packet->next = queue->pool;
   240             queue->pool = packet;
   241         }
   242     }
   243 
   244     SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
   245 
   246     if (queue->head == NULL) {
   247         queue->tail = NULL;  /* in case we drained the queue entirely. */
   248     }
   249 
   250     return (size_t) (ptr - buf);
   251 }
   252 
   253 size_t
   254 SDL_CountDataQueue(SDL_DataQueue *queue)
   255 {
   256     return queue ? queue->queued_bytes : 0;
   257 }
   258 
   259 /* vi: set ts=4 sw=4 expandtab: */
   260