/** * FreeRDP: A Remote Desktop Protocol Implementation * * Copyright 2014 Thincast Technologies GmbH * Copyright 2014 Hardening * * 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 #include #include #include #include #include #include #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); }