src/SDL_dataqueue.c
author Sam Lantinga
Fri, 27 Jan 2017 06:05:50 -0800
changeset 10856 486aa38c6a88
parent 10743 8ccdeceaae6d
child 11582 cc0b1273a381
permissions -rw-r--r--
Added Thrustmaster Wheel FFB entry to the list of wheels
     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 static SDL_DataQueuePacket *
   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
   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
   229 SDL_ReadFromDataQueue(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     while ((len > 0) && ((packet = queue->head) != NULL)) {
   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         packet->startpos += cpy;
   247         ptr += cpy;
   248         queue->queued_bytes -= cpy;
   249         len -= cpy;
   250 
   251         if (packet->startpos == packet->datalen) {  /* packet is done, put it in the pool. */
   252             queue->head = packet->next;
   253             SDL_assert((packet->next != NULL) || (packet == queue->tail));
   254             packet->next = queue->pool;
   255             queue->pool = packet;
   256         }
   257     }
   258 
   259     SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
   260 
   261     if (queue->head == NULL) {
   262         queue->tail = NULL;  /* in case we drained the queue entirely. */
   263     }
   264 
   265     return (size_t) (ptr - buf);
   266 }
   267 
   268 size_t
   269 SDL_CountDataQueue(SDL_DataQueue *queue)
   270 {
   271     return queue ? queue->queued_bytes : 0;
   272 }
   273 
   274 void *
   275 SDL_ReserveSpaceInDataQueue(SDL_DataQueue *queue, const size_t len)
   276 {
   277     SDL_DataQueuePacket *packet;
   278 
   279     if (!queue) {
   280         SDL_InvalidParamError("queue");
   281         return NULL;
   282     } else if (len == 0) {
   283         SDL_InvalidParamError("len");
   284         return NULL;
   285     } else if (len > queue->packet_size) {
   286         SDL_SetError("len is larger than packet size");
   287         return NULL;
   288     }
   289 
   290     packet = queue->head;
   291     if (packet) {
   292         const size_t avail = queue->packet_size - packet->datalen;
   293         if (len <= avail) {  /* we can use the space at end of this packet. */
   294             void *retval = packet->data + packet->datalen;
   295             packet->datalen += len;
   296             queue->queued_bytes += len;
   297             return retval;
   298         }
   299     }
   300 
   301     /* Need a fresh packet. */
   302     packet = AllocateDataQueuePacket(queue);
   303     if (!packet) {
   304         SDL_OutOfMemory();
   305         return NULL;
   306     }
   307 
   308     packet->datalen = len;
   309     queue->queued_bytes += len;
   310     return packet->data;
   311 }
   312 
   313 /* vi: set ts=4 sw=4 expandtab: */
   314