src/SDL_dataqueue.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 09 Dec 2019 13:54:05 -0800
changeset 13323 a8804daa1c3d
parent 12503 806492103856
permissions -rw-r--r--
The PowerA Mini controller blocks while writing feature reports, so don't try to use the HIDAPI driver for it on Linux
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 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_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
   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
   294 SDL_CountDataQueue(SDL_DataQueue *queue)
   295 {
   296     return queue ? queue->queued_bytes : 0;
   297 }
   298 
   299 void *
   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