src/SDL_dataqueue.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 14 Jun 2019 13:56:42 -0700
changeset 12860 0f52dd40abe5
parent 12503 806492103856
permissions -rw-r--r--
Worked around "Undefined symbol: ___isPlatformVersionAtLeast()" link error on Xcode 11 beta
icculus@10681
     1
/*
icculus@10681
     2
  Simple DirectMedia Layer
slouken@12503
     3
  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
icculus@10681
     4
icculus@10681
     5
  This software is provided 'as-is', without any express or implied
icculus@10681
     6
  warranty.  In no event will the authors be held liable for any damages
icculus@10681
     7
  arising from the use of this software.
icculus@10681
     8
icculus@10681
     9
  Permission is granted to anyone to use this software for any purpose,
icculus@10681
    10
  including commercial applications, and to alter it and redistribute it
icculus@10681
    11
  freely, subject to the following restrictions:
icculus@10681
    12
icculus@10681
    13
  1. The origin of this software must not be misrepresented; you must not
icculus@10681
    14
     claim that you wrote the original software. If you use this software
icculus@10681
    15
     in a product, an acknowledgment in the product documentation would be
icculus@10681
    16
     appreciated but is not required.
icculus@10681
    17
  2. Altered source versions must be plainly marked as such, and must not be
icculus@10681
    18
     misrepresented as being the original software.
icculus@10681
    19
  3. This notice may not be removed or altered from any source distribution.
icculus@10681
    20
*/
icculus@10681
    21
icculus@10681
    22
#include "./SDL_internal.h"
icculus@10681
    23
#include "SDL.h"
icculus@10681
    24
#include "./SDL_dataqueue.h"
icculus@10681
    25
#include "SDL_assert.h"
icculus@10681
    26
icculus@10681
    27
typedef struct SDL_DataQueuePacket
icculus@10681
    28
{
icculus@10681
    29
    size_t datalen;  /* bytes currently in use in this packet. */
icculus@10681
    30
    size_t startpos;  /* bytes currently consumed in this packet. */
icculus@10681
    31
    struct SDL_DataQueuePacket *next;  /* next item in linked list. */
icculus@10681
    32
    Uint8 data[SDL_VARIABLE_LENGTH_ARRAY];  /* packet data */
icculus@10681
    33
} SDL_DataQueuePacket;
icculus@10681
    34
icculus@10681
    35
struct SDL_DataQueue
icculus@10681
    36
{
icculus@10681
    37
    SDL_DataQueuePacket *head; /* device fed from here. */
icculus@10681
    38
    SDL_DataQueuePacket *tail; /* queue fills to here. */
icculus@10681
    39
    SDL_DataQueuePacket *pool; /* these are unused packets. */
icculus@10681
    40
    size_t packet_size;   /* size of new packets */
icculus@10681
    41
    size_t queued_bytes;  /* number of bytes of data in the queue. */
icculus@10681
    42
};
icculus@10681
    43
icculus@10681
    44
static void
icculus@10681
    45
SDL_FreeDataQueueList(SDL_DataQueuePacket *packet)
icculus@10681
    46
{
icculus@10681
    47
    while (packet) {
icculus@10681
    48
        SDL_DataQueuePacket *next = packet->next;
icculus@10681
    49
        SDL_free(packet);
icculus@10681
    50
        packet = next;
icculus@10681
    51
    }
icculus@10681
    52
}
icculus@10681
    53
icculus@10681
    54
icculus@10681
    55
/* this all expects that you managed thread safety elsewhere. */
icculus@10681
    56
icculus@10681
    57
SDL_DataQueue *
icculus@10681
    58
SDL_NewDataQueue(const size_t _packetlen, const size_t initialslack)
icculus@10681
    59
{
icculus@10681
    60
    SDL_DataQueue *queue = (SDL_DataQueue *) SDL_malloc(sizeof (SDL_DataQueue));
icculus@10681
    61
icculus@10681
    62
    if (!queue) {
icculus@10681
    63
        SDL_OutOfMemory();
icculus@10681
    64
        return NULL;
icculus@10681
    65
    } else {
icculus@10681
    66
        const size_t packetlen = _packetlen ? _packetlen : 1024;
icculus@10681
    67
        const size_t wantpackets = (initialslack + (packetlen - 1)) / packetlen;
icculus@10681
    68
        size_t i;
icculus@10681
    69
icculus@10681
    70
        SDL_zerop(queue);
icculus@10681
    71
        queue->packet_size = packetlen;
icculus@10681
    72
icculus@10681
    73
        for (i = 0; i < wantpackets; i++) {
icculus@10681
    74
            SDL_DataQueuePacket *packet = (SDL_DataQueuePacket *) SDL_malloc(sizeof (SDL_DataQueuePacket) + packetlen);
icculus@10681
    75
            if (packet) { /* don't care if this fails, we'll deal later. */
icculus@10681
    76
                packet->datalen = 0;
icculus@10681
    77
                packet->startpos = 0;
icculus@10681
    78
                packet->next = queue->pool;
icculus@10681
    79
                queue->pool = packet;
icculus@10681
    80
            }
icculus@10681
    81
        }
icculus@10681
    82
    }
icculus@10681
    83
icculus@10681
    84
    return queue;
icculus@10681
    85
}
icculus@10681
    86
icculus@10681
    87
void
icculus@10681
    88
SDL_FreeDataQueue(SDL_DataQueue *queue)
icculus@10681
    89
{
icculus@10681
    90
    if (queue) {
icculus@10681
    91
        SDL_FreeDataQueueList(queue->head);
icculus@10681
    92
        SDL_FreeDataQueueList(queue->pool);
icculus@10681
    93
        SDL_free(queue);
icculus@10681
    94
    }
icculus@10681
    95
}
icculus@10681
    96
icculus@10681
    97
void
icculus@10681
    98
SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack)
icculus@10681
    99
{
icculus@10681
   100
    const size_t packet_size = queue ? queue->packet_size : 1;
icculus@10681
   101
    const size_t slackpackets = (slack + (packet_size-1)) / packet_size;
icculus@10681
   102
    SDL_DataQueuePacket *packet;
icculus@10681
   103
    SDL_DataQueuePacket *prev = NULL;
icculus@10681
   104
    size_t i;
icculus@10681
   105
icculus@10681
   106
    if (!queue) {
icculus@10681
   107
        return;
icculus@10681
   108
    }
icculus@10681
   109
icculus@10681
   110
    packet = queue->head;
icculus@10681
   111
icculus@10681
   112
    /* merge the available pool and the current queue into one list. */
icculus@10681
   113
    if (packet) {
icculus@10681
   114
        queue->tail->next = queue->pool;
icculus@10681
   115
    } else {
icculus@10681
   116
        packet = queue->pool;
icculus@10681
   117
    }
icculus@10681
   118
icculus@10681
   119
    /* Remove the queued packets from the device. */
icculus@10681
   120
    queue->tail = NULL;
icculus@10681
   121
    queue->head = NULL;
icculus@10681
   122
    queue->queued_bytes = 0;
icculus@10681
   123
    queue->pool = packet;
icculus@10681
   124
icculus@10681
   125
    /* Optionally keep some slack in the pool to reduce malloc pressure. */
icculus@10681
   126
    for (i = 0; packet && (i < slackpackets); i++) {
icculus@10681
   127
        prev = packet;
icculus@10681
   128
        packet = packet->next;
icculus@10681
   129
    }
icculus@10681
   130
icculus@10681
   131
    if (prev) {
icculus@10681
   132
        prev->next = NULL;
icculus@10681
   133
    } else {
icculus@10681
   134
        queue->pool = NULL;
icculus@10681
   135
    }
icculus@10681
   136
icculus@10681
   137
    SDL_FreeDataQueueList(packet);  /* free extra packets */
icculus@10681
   138
}
icculus@10681
   139
icculus@10743
   140
static SDL_DataQueuePacket *
icculus@10743
   141
AllocateDataQueuePacket(SDL_DataQueue *queue)
icculus@10743
   142
{
icculus@10743
   143
    SDL_DataQueuePacket *packet;
icculus@10743
   144
icculus@10743
   145
    SDL_assert(queue != NULL);
icculus@10743
   146
icculus@10743
   147
    packet = queue->pool;
icculus@10743
   148
    if (packet != NULL) {
icculus@10743
   149
        /* we have one available in the pool. */
icculus@10743
   150
        queue->pool = packet->next;
icculus@10743
   151
    } else {
icculus@10743
   152
        /* Have to allocate a new one! */
icculus@10743
   153
        packet = (SDL_DataQueuePacket *) SDL_malloc(sizeof (SDL_DataQueuePacket) + queue->packet_size);
icculus@10743
   154
        if (packet == NULL) {
icculus@10743
   155
            return NULL;
icculus@10743
   156
        }
icculus@10743
   157
    }
icculus@10743
   158
icculus@10743
   159
    packet->datalen = 0;
icculus@10743
   160
    packet->startpos = 0;
icculus@10743
   161
    packet->next = NULL;
icculus@10743
   162
                
icculus@10743
   163
    SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
icculus@10743
   164
    if (queue->tail == NULL) {
icculus@10743
   165
        queue->head = packet;
icculus@10743
   166
    } else {
icculus@10743
   167
        queue->tail->next = packet;
icculus@10743
   168
    }
icculus@10743
   169
    queue->tail = packet;
icculus@10743
   170
    return packet;
icculus@10743
   171
}
icculus@10743
   172
icculus@10743
   173
icculus@10681
   174
int
icculus@10681
   175
SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *_data, const size_t _len)
icculus@10681
   176
{
icculus@10681
   177
    size_t len = _len;
icculus@10681
   178
    const Uint8 *data = (const Uint8 *) _data;
icculus@10681
   179
    const size_t packet_size = queue ? queue->packet_size : 0;
icculus@10681
   180
    SDL_DataQueuePacket *orighead;
icculus@10681
   181
    SDL_DataQueuePacket *origtail;
icculus@10681
   182
    size_t origlen;
icculus@10681
   183
    size_t datalen;
icculus@10681
   184
icculus@10681
   185
    if (!queue) {
icculus@10681
   186
        return SDL_InvalidParamError("queue");
icculus@10681
   187
    }
icculus@10681
   188
icculus@10681
   189
    orighead = queue->head;
icculus@10681
   190
    origtail = queue->tail;
icculus@10681
   191
    origlen = origtail ? origtail->datalen : 0;
icculus@10681
   192
icculus@10681
   193
    while (len > 0) {
icculus@10681
   194
        SDL_DataQueuePacket *packet = queue->tail;
icculus@10681
   195
        SDL_assert(!packet || (packet->datalen <= packet_size));
icculus@10681
   196
        if (!packet || (packet->datalen >= packet_size)) {
icculus@10681
   197
            /* tail packet missing or completely full; we need a new packet. */
icculus@10743
   198
            packet = AllocateDataQueuePacket(queue);
icculus@10743
   199
            if (!packet) {
icculus@10743
   200
                /* uhoh, reset so we've queued nothing new, free what we can. */
icculus@10743
   201
                if (!origtail) {
icculus@10743
   202
                    packet = queue->head;  /* whole queue. */
icculus@10743
   203
                } else {
icculus@10743
   204
                    packet = origtail->next;  /* what we added to existing queue. */
icculus@10743
   205
                    origtail->next = NULL;
icculus@10743
   206
                    origtail->datalen = origlen;
icculus@10743
   207
                }
icculus@10743
   208
                queue->head = orighead;
icculus@10743
   209
                queue->tail = origtail;
icculus@10743
   210
                queue->pool = NULL;
icculus@10681
   211
icculus@10743
   212
                SDL_FreeDataQueueList(packet);  /* give back what we can. */
icculus@10743
   213
                return SDL_OutOfMemory();
icculus@10681
   214
            }
icculus@10681
   215
        }
icculus@10681
   216
icculus@10681
   217
        datalen = SDL_min(len, packet_size - packet->datalen);
icculus@10681
   218
        SDL_memcpy(packet->data + packet->datalen, data, datalen);
icculus@10681
   219
        data += datalen;
icculus@10681
   220
        len -= datalen;
icculus@10681
   221
        packet->datalen += datalen;
icculus@10681
   222
        queue->queued_bytes += datalen;
icculus@10681
   223
    }
icculus@10681
   224
icculus@10681
   225
    return 0;
icculus@10681
   226
}
icculus@10681
   227
icculus@10681
   228
size_t
icculus@11582
   229
SDL_PeekIntoDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
icculus@11582
   230
{
icculus@11582
   231
    size_t len = _len;
icculus@11582
   232
    Uint8 *buf = (Uint8 *) _buf;
icculus@11582
   233
    Uint8 *ptr = buf;
icculus@11582
   234
    SDL_DataQueuePacket *packet;
icculus@11582
   235
icculus@11582
   236
    if (!queue) {
icculus@11582
   237
        return 0;
icculus@11582
   238
    }
icculus@11582
   239
icculus@11582
   240
    for (packet = queue->head; len && packet; packet = packet->next) {
icculus@11582
   241
        const size_t avail = packet->datalen - packet->startpos;
icculus@11582
   242
        const size_t cpy = SDL_min(len, avail);
icculus@11582
   243
        SDL_assert(queue->queued_bytes >= avail);
icculus@11582
   244
icculus@11582
   245
        SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
icculus@11582
   246
        ptr += cpy;
icculus@11582
   247
        len -= cpy;
icculus@11582
   248
    }
icculus@11582
   249
icculus@11582
   250
    return (size_t) (ptr - buf);
icculus@11582
   251
}
icculus@11582
   252
icculus@11582
   253
size_t
icculus@10681
   254
SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
icculus@10681
   255
{
icculus@10681
   256
    size_t len = _len;
icculus@10681
   257
    Uint8 *buf = (Uint8 *) _buf;
icculus@10681
   258
    Uint8 *ptr = buf;
icculus@10681
   259
    SDL_DataQueuePacket *packet;
icculus@10681
   260
icculus@10681
   261
    if (!queue) {
icculus@10681
   262
        return 0;
icculus@10681
   263
    }
icculus@10681
   264
icculus@10681
   265
    while ((len > 0) && ((packet = queue->head) != NULL)) {
icculus@10681
   266
        const size_t avail = packet->datalen - packet->startpos;
icculus@10681
   267
        const size_t cpy = SDL_min(len, avail);
icculus@10681
   268
        SDL_assert(queue->queued_bytes >= avail);
icculus@10681
   269
icculus@10681
   270
        SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
icculus@10681
   271
        packet->startpos += cpy;
icculus@10681
   272
        ptr += cpy;
icculus@10681
   273
        queue->queued_bytes -= cpy;
icculus@10681
   274
        len -= cpy;
icculus@10681
   275
icculus@10681
   276
        if (packet->startpos == packet->datalen) {  /* packet is done, put it in the pool. */
icculus@10681
   277
            queue->head = packet->next;
icculus@10681
   278
            SDL_assert((packet->next != NULL) || (packet == queue->tail));
icculus@10681
   279
            packet->next = queue->pool;
icculus@10681
   280
            queue->pool = packet;
icculus@10681
   281
        }
icculus@10681
   282
    }
icculus@10681
   283
icculus@10681
   284
    SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
icculus@10681
   285
icculus@10681
   286
    if (queue->head == NULL) {
icculus@10681
   287
        queue->tail = NULL;  /* in case we drained the queue entirely. */
icculus@10681
   288
    }
icculus@10681
   289
icculus@10681
   290
    return (size_t) (ptr - buf);
icculus@10681
   291
}
icculus@10681
   292
icculus@10681
   293
size_t
icculus@10681
   294
SDL_CountDataQueue(SDL_DataQueue *queue)
icculus@10681
   295
{
icculus@10681
   296
    return queue ? queue->queued_bytes : 0;
icculus@10681
   297
}
icculus@10681
   298
icculus@10743
   299
void *
icculus@10743
   300
SDL_ReserveSpaceInDataQueue(SDL_DataQueue *queue, const size_t len)
icculus@10743
   301
{
icculus@10743
   302
    SDL_DataQueuePacket *packet;
icculus@10743
   303
icculus@10743
   304
    if (!queue) {
icculus@10743
   305
        SDL_InvalidParamError("queue");
icculus@10743
   306
        return NULL;
icculus@10743
   307
    } else if (len == 0) {
icculus@10743
   308
        SDL_InvalidParamError("len");
icculus@10743
   309
        return NULL;
icculus@10743
   310
    } else if (len > queue->packet_size) {
icculus@10743
   311
        SDL_SetError("len is larger than packet size");
icculus@10743
   312
        return NULL;
icculus@10743
   313
    }
icculus@10743
   314
icculus@10743
   315
    packet = queue->head;
icculus@10743
   316
    if (packet) {
icculus@10743
   317
        const size_t avail = queue->packet_size - packet->datalen;
icculus@10743
   318
        if (len <= avail) {  /* we can use the space at end of this packet. */
icculus@10743
   319
            void *retval = packet->data + packet->datalen;
icculus@10743
   320
            packet->datalen += len;
icculus@10743
   321
            queue->queued_bytes += len;
icculus@10743
   322
            return retval;
icculus@10743
   323
        }
icculus@10743
   324
    }
icculus@10743
   325
icculus@10743
   326
    /* Need a fresh packet. */
icculus@10743
   327
    packet = AllocateDataQueuePacket(queue);
icculus@10743
   328
    if (!packet) {
icculus@10743
   329
        SDL_OutOfMemory();
icculus@10743
   330
        return NULL;
icculus@10743
   331
    }
icculus@10743
   332
icculus@10743
   333
    packet->datalen = len;
icculus@10743
   334
    queue->queued_bytes += len;
icculus@10743
   335
    return packet->data;
icculus@10743
   336
}
icculus@10743
   337
icculus@10681
   338
/* vi: set ts=4 sw=4 expandtab: */
icculus@10681
   339