Added SDL_ReserveSpaceInDataQueue() to make space without copying data.

This commit is contained in:
Ryan C. Gordon 2016-12-27 23:48:43 -05:00
parent 18d9b23c65
commit 2e2572a4f1
2 changed files with 103 additions and 34 deletions

View File

@ -137,6 +137,40 @@ SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack)
SDL_FreeDataQueueList(packet); /* free extra packets */ SDL_FreeDataQueueList(packet); /* free extra packets */
} }
static SDL_DataQueuePacket *
AllocateDataQueuePacket(SDL_DataQueue *queue)
{
SDL_DataQueuePacket *packet;
SDL_assert(queue != NULL);
packet = queue->pool;
if (packet != NULL) {
/* we have one available in the pool. */
queue->pool = packet->next;
} else {
/* Have to allocate a new one! */
packet = (SDL_DataQueuePacket *) SDL_malloc(sizeof (SDL_DataQueuePacket) + queue->packet_size);
if (packet == NULL) {
return NULL;
}
}
packet->datalen = 0;
packet->startpos = 0;
packet->next = NULL;
SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
if (queue->tail == NULL) {
queue->head = packet;
} else {
queue->tail->next = packet;
}
queue->tail = packet;
return packet;
}
int int
SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *_data, const size_t _len) SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *_data, const size_t _len)
{ {
@ -161,42 +195,23 @@ SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *_data, const size_t _len)
SDL_assert(!packet || (packet->datalen <= packet_size)); SDL_assert(!packet || (packet->datalen <= packet_size));
if (!packet || (packet->datalen >= packet_size)) { if (!packet || (packet->datalen >= packet_size)) {
/* tail packet missing or completely full; we need a new packet. */ /* tail packet missing or completely full; we need a new packet. */
packet = queue->pool; packet = AllocateDataQueuePacket(queue);
if (packet != NULL) { if (!packet) {
/* we have one available in the pool. */ /* uhoh, reset so we've queued nothing new, free what we can. */
queue->pool = packet->next; if (!origtail) {
} else { packet = queue->head; /* whole queue. */
/* Have to allocate a new one! */ } else {
packet = (SDL_DataQueuePacket *) SDL_malloc(sizeof (SDL_DataQueuePacket) + packet_size); packet = origtail->next; /* what we added to existing queue. */
if (packet == NULL) { origtail->next = NULL;
/* uhoh, reset so we've queued nothing new, free what we can. */ origtail->datalen = origlen;
if (!origtail) {
packet = queue->head; /* whole queue. */
} else {
packet = origtail->next; /* what we added to existing queue. */
origtail->next = NULL;
origtail->datalen = origlen;
}
queue->head = orighead;
queue->tail = origtail;
queue->pool = NULL;
SDL_FreeDataQueueList(packet); /* give back what we can. */
return SDL_OutOfMemory();
} }
} queue->head = orighead;
packet->datalen = 0; queue->tail = origtail;
packet->startpos = 0; queue->pool = NULL;
packet->next = NULL;
SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0)); SDL_FreeDataQueueList(packet); /* give back what we can. */
if (queue->tail == NULL) { return SDL_OutOfMemory();
queue->head = packet;
} else {
queue->tail->next = packet;
} }
queue->tail = packet;
} }
datalen = SDL_min(len, packet_size - packet->datalen); datalen = SDL_min(len, packet_size - packet->datalen);
@ -256,5 +271,44 @@ SDL_CountDataQueue(SDL_DataQueue *queue)
return queue ? queue->queued_bytes : 0; return queue ? queue->queued_bytes : 0;
} }
void *
SDL_ReserveSpaceInDataQueue(SDL_DataQueue *queue, const size_t len)
{
SDL_DataQueuePacket *packet;
if (!queue) {
SDL_InvalidParamError("queue");
return NULL;
} else if (len == 0) {
SDL_InvalidParamError("len");
return NULL;
} else if (len > queue->packet_size) {
SDL_SetError("len is larger than packet size");
return NULL;
}
packet = queue->head;
if (packet) {
const size_t avail = queue->packet_size - packet->datalen;
if (len <= avail) { /* we can use the space at end of this packet. */
void *retval = packet->data + packet->datalen;
packet->datalen += len;
queue->queued_bytes += len;
return retval;
}
}
/* Need a fresh packet. */
packet = AllocateDataQueuePacket(queue);
if (!packet) {
SDL_OutOfMemory();
return NULL;
}
packet->datalen = len;
queue->queued_bytes += len;
return packet->data;
}
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */

View File

@ -33,6 +33,21 @@ int SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *data, const size_t le
size_t SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *buf, const size_t len); size_t SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *buf, const size_t len);
size_t SDL_CountDataQueue(SDL_DataQueue *queue); size_t SDL_CountDataQueue(SDL_DataQueue *queue);
/* this sets a section of the data queue aside (possibly allocating memory for it)
as if it's been written to, but returns a pointer to that space. You may write
to this space until a read would consume it. Writes (and other calls to this
function) will safely append their data after this reserved space and can
be in flight at the same time. There is no thread safety.
If there isn't an existing block of memory that can contain the reserved
space, one will be allocated for it. You can not (currently) allocate
a space larger than the packetlen requested in SDL_NewDataQueue.
Returned buffer is uninitialized.
This lets you avoid an extra copy in some cases, but it's safer to use
SDL_WriteToDataQueue() unless you know what you're doing.
Returns pointer to buffer of at least (len) bytes, NULL on error.
*/
void *SDL_ReserveSpaceInDataQueue(SDL_DataQueue *queue, const size_t len);
#endif /* SDL_dataqueue_h_ */ #endif /* SDL_dataqueue_h_ */
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */