diff options
Diffstat (limited to 'libfreerdp/utils/ringbuffer.c')
-rw-r--r-- | libfreerdp/utils/ringbuffer.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/libfreerdp/utils/ringbuffer.c b/libfreerdp/utils/ringbuffer.c new file mode 100644 index 0000000..d8390de --- /dev/null +++ b/libfreerdp/utils/ringbuffer.c @@ -0,0 +1,295 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2014 Thincast Technologies GmbH + * Copyright 2014 Hardening <contact@hardening-consulting.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <freerdp/config.h> + +#include <freerdp/utils/ringbuffer.h> + +#include <stdlib.h> +#include <string.h> +#include <winpr/assert.h> + +#include <winpr/crt.h> +#include <freerdp/log.h> + +#define TAG FREERDP_TAG("utils.ringbuffer") + +#ifdef WITH_DEBUG_RINGBUFFER +#define DEBUG_RINGBUFFER(...) WLog_DBG(TAG, __VA_ARGS__) +#else +#define DEBUG_RINGBUFFER(...) \ + do \ + { \ + } while (0) +#endif + +BOOL ringbuffer_init(RingBuffer* rb, size_t initialSize) +{ + rb->buffer = malloc(initialSize); + + if (!rb->buffer) + return FALSE; + + rb->readPtr = rb->writePtr = 0; + rb->initialSize = rb->size = rb->freeSize = initialSize; + DEBUG_RINGBUFFER("ringbuffer_init(%p)", (void*)rb); + return TRUE; +} + +size_t ringbuffer_used(const RingBuffer* rb) +{ + return rb->size - rb->freeSize; +} + +size_t ringbuffer_capacity(const RingBuffer* rb) +{ + return rb->size; +} + +void ringbuffer_destroy(RingBuffer* rb) +{ + DEBUG_RINGBUFFER("ringbuffer_destroy(%p)", (void*)rb); + free(rb->buffer); + rb->buffer = NULL; +} + +static BOOL ringbuffer_realloc(RingBuffer* rb, size_t targetSize) +{ + BYTE* newData = NULL; + DEBUG_RINGBUFFER("ringbuffer_realloc(%p): targetSize: %" PRIdz "", (void*)rb, targetSize); + + if (rb->writePtr == rb->readPtr) + { + /* when no size is used we can realloc() and set the heads at the + * beginning of the buffer + */ + newData = (BYTE*)realloc(rb->buffer, targetSize); + + if (!newData) + return FALSE; + + rb->readPtr = rb->writePtr = 0; + rb->buffer = newData; + } + else if ((rb->writePtr >= rb->readPtr) && (rb->writePtr < targetSize)) + { + /* we reallocate only if we're in that case, realloc don't touch read + * and write heads + * + * readPtr writePtr + * | | + * v v + * [............|XXXXXXXXXXXXXX|..........] + */ + newData = (BYTE*)realloc(rb->buffer, targetSize); + + if (!newData) + return FALSE; + + rb->buffer = newData; + } + else + { + /* in case of malloc the read head is moved at the beginning of the new buffer + * and the write head is set accordingly + */ + newData = (BYTE*)malloc(targetSize); + + if (!newData) + return FALSE; + + if (rb->readPtr < rb->writePtr) + { + /* readPtr writePtr + * | | + * v v + * [............|XXXXXXXXXXXXXX|..........] + */ + memcpy(newData, rb->buffer + rb->readPtr, ringbuffer_used(rb)); + } + else + { + /* writePtr readPtr + * | | + * v v + * [XXXXXXXXXXXX|..............|XXXXXXXXXX] + */ + BYTE* dst = newData; + memcpy(dst, rb->buffer + rb->readPtr, rb->size - rb->readPtr); + dst += (rb->size - rb->readPtr); + + if (rb->writePtr) + memcpy(dst, rb->buffer, rb->writePtr); + } + + rb->writePtr = rb->size - rb->freeSize; + rb->readPtr = 0; + free(rb->buffer); + rb->buffer = newData; + } + + rb->freeSize += (targetSize - rb->size); + rb->size = targetSize; + return TRUE; +} + +/** + * Write to a ringbuffer + * + * @param rb A pointer to the ringbuffer + * @param ptr A pointer to the data to write + * @param sz The number of bytes to write + * + * @return \b TRUE for success, \b FALSE for failure + */ +BOOL ringbuffer_write(RingBuffer* rb, const BYTE* ptr, size_t sz) +{ + size_t toWrite = 0; + size_t remaining = 0; + DEBUG_RINGBUFFER("ringbuffer_write(%p): sz: %" PRIdz "", (void*)rb, sz); + + if ((rb->freeSize <= sz) && !ringbuffer_realloc(rb, rb->size + sz)) + return FALSE; + + /* the write could be split in two + * readHead writeHead + * | | + * v v + * [ ################ ] + */ + toWrite = sz; + remaining = sz; + + if (rb->size - rb->writePtr < sz) + toWrite = rb->size - rb->writePtr; + + if (toWrite) + { + memcpy(rb->buffer + rb->writePtr, ptr, toWrite); + remaining -= toWrite; + ptr += toWrite; + } + + if (remaining) + memcpy(rb->buffer, ptr, remaining); + + rb->writePtr = (rb->writePtr + sz) % rb->size; + rb->freeSize -= sz; + return TRUE; +} + +BYTE* ringbuffer_ensure_linear_write(RingBuffer* rb, size_t sz) +{ + DEBUG_RINGBUFFER("ringbuffer_ensure_linear_write(%p): sz: %" PRIdz "", (void*)rb, sz); + + if (rb->freeSize < sz) + { + if (!ringbuffer_realloc(rb, rb->size + sz - rb->freeSize + 32)) + return NULL; + } + + if (rb->writePtr == rb->readPtr) + { + rb->writePtr = rb->readPtr = 0; + } + + if (rb->writePtr + sz < rb->size) + return rb->buffer + rb->writePtr; + + /* + * to add: ....... + * [ XXXXXXXXX ] + * + * result: + * [XXXXXXXXX....... ] + */ + memmove(rb->buffer, rb->buffer + rb->readPtr, rb->writePtr - rb->readPtr); + rb->readPtr = 0; + rb->writePtr = rb->size - rb->freeSize; + return rb->buffer + rb->writePtr; +} + +BOOL ringbuffer_commit_written_bytes(RingBuffer* rb, size_t sz) +{ + DEBUG_RINGBUFFER("ringbuffer_commit_written_bytes(%p): sz: %" PRIdz "", (void*)rb, sz); + + if (sz < 1) + return TRUE; + + if (rb->writePtr + sz > rb->size) + return FALSE; + + rb->writePtr = (rb->writePtr + sz) % rb->size; + rb->freeSize -= sz; + return TRUE; +} + +int ringbuffer_peek(const RingBuffer* rb, DataChunk chunks[2], size_t sz) +{ + size_t remaining = sz; + size_t toRead = 0; + int chunkIndex = 0; + int status = 0; + DEBUG_RINGBUFFER("ringbuffer_peek(%p): sz: %" PRIdz "", (void*)rb, sz); + + if (sz < 1) + return 0; + + if ((rb->size - rb->freeSize) < sz) + remaining = rb->size - rb->freeSize; + + toRead = remaining; + + if ((rb->readPtr + remaining) > rb->size) + toRead = rb->size - rb->readPtr; + + if (toRead) + { + chunks[0].data = rb->buffer + rb->readPtr; + chunks[0].size = toRead; + remaining -= toRead; + chunkIndex++; + status++; + } + + if (remaining) + { + chunks[chunkIndex].data = rb->buffer; + chunks[chunkIndex].size = remaining; + status++; + } + + return status; +} + +void ringbuffer_commit_read_bytes(RingBuffer* rb, size_t sz) +{ + DEBUG_RINGBUFFER("ringbuffer_commit_read_bytes(%p): sz: %" PRIdz "", (void*)rb, sz); + + if (sz < 1) + return; + + WINPR_ASSERT(rb->size - rb->freeSize >= sz); + rb->readPtr = (rb->readPtr + sz) % rb->size; + rb->freeSize += sz; + + /* when we reach a reasonable free size, we can go back to the original size */ + if ((rb->size != rb->initialSize) && (ringbuffer_used(rb) < rb->initialSize / 2)) + ringbuffer_realloc(rb, rb->initialSize); +} |