/* Implementation of a ring buffer for reports * * Copyright 2015 CodeWeavers, Aric Stewart * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #define NONAMELESSUNION #include "hid.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(hid); #define POINTER_UNUSED 0xffffffff #define BASE_BUFFER_SIZE 32 #define MIN_BUFFER_SIZE 2 #define MAX_BUFFER_SIZE 512 struct ReportRingBuffer { UINT start, end, size; UINT *pointers; UINT pointer_alloc; UINT buffer_size; CRITICAL_SECTION lock; BYTE *buffer; }; struct ReportRingBuffer* RingBuffer_Create(UINT buffer_size) { struct ReportRingBuffer *ring; int i; TRACE("Create Ring Buffer with buffer size %i\n",buffer_size); ring = HeapAlloc(GetProcessHeap(), 0, sizeof(*ring)); if (!ring) return NULL; ring->start = ring->end = 0; ring->size = BASE_BUFFER_SIZE; ring->buffer_size = buffer_size; ring->pointer_alloc = 2; ring->pointers = HeapAlloc(GetProcessHeap(), 0, sizeof(UINT) * ring->pointer_alloc); if (!ring->pointers) { HeapFree(GetProcessHeap(), 0, ring); return NULL; } for (i = 0; i < ring->pointer_alloc; i++) ring->pointers[i] = POINTER_UNUSED; ring->buffer = HeapAlloc(GetProcessHeap(), 0, buffer_size * ring->size); if (!ring->buffer) { HeapFree(GetProcessHeap(), 0, ring->pointers); HeapFree(GetProcessHeap(), 0, ring); return NULL; } InitializeCriticalSection(&ring->lock); ring->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": RingBuffer.lock"); return ring; } void RingBuffer_Destroy(struct ReportRingBuffer *ring) { HeapFree(GetProcessHeap(), 0, ring->buffer); HeapFree(GetProcessHeap(), 0, ring->pointers); ring->lock.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&ring->lock); HeapFree(GetProcessHeap(), 0, ring); } UINT RingBuffer_GetBufferSize(struct ReportRingBuffer *ring) { return ring->buffer_size; } UINT RingBuffer_GetSize(struct ReportRingBuffer *ring) { return ring->size; } NTSTATUS RingBuffer_SetSize(struct ReportRingBuffer *ring, UINT size) { BYTE* new_buffer; int i; if (size < MIN_BUFFER_SIZE || size > MAX_BUFFER_SIZE) return STATUS_INVALID_PARAMETER; if (size == ring->size) return STATUS_SUCCESS; EnterCriticalSection(&ring->lock); ring->start = ring->end = 0; for (i = 0; i < ring->pointer_alloc; i++) { if (ring->pointers[i] != POINTER_UNUSED) ring->pointers[i] = 0; } new_buffer = HeapAlloc(GetProcessHeap(), 0, ring->buffer_size * size); if (!new_buffer) { LeaveCriticalSection(&ring->lock); return STATUS_NO_MEMORY; } HeapFree(GetProcessHeap(), 0, ring->buffer); ring->buffer = new_buffer; ring->size = size; LeaveCriticalSection(&ring->lock); return STATUS_SUCCESS; } void RingBuffer_ReadNew(struct ReportRingBuffer *ring, UINT index, void *output, UINT *size) { void *ret = NULL; EnterCriticalSection(&ring->lock); if (index >= ring->pointer_alloc || ring->pointers[index] == POINTER_UNUSED) { LeaveCriticalSection(&ring->lock); *size = 0; return; } if (ring->pointers[index] == ring->end) { LeaveCriticalSection(&ring->lock); *size = 0; } else { ret = &ring->buffer[ring->pointers[index] * ring->buffer_size]; memcpy(output, ret, ring->buffer_size); ring->pointers[index]++; if (ring->pointers[index] == ring->size) ring->pointers[index] = 0; LeaveCriticalSection(&ring->lock); *size = ring->buffer_size; } } void RingBuffer_Read(struct ReportRingBuffer *ring, UINT index, void *output, UINT *size) { int pointer; void *ret = NULL; EnterCriticalSection(&ring->lock); if (index >= ring->pointer_alloc || ring->pointers[index] == POINTER_UNUSED || ring->end == ring->start) { LeaveCriticalSection(&ring->lock); *size = 0; return; } pointer = ring->pointers[index]; if (pointer == ring->end) pointer--; if (pointer < 0) pointer = ring->size - 1; ret = &ring->buffer[pointer * ring->buffer_size]; memcpy(output, ret, ring->buffer_size); if (pointer == ring->pointers[index]) { ring->pointers[index]++; if (ring->pointers[index] == ring->size) ring->pointers[index] = 0; } LeaveCriticalSection(&ring->lock); *size = ring->buffer_size; } UINT RingBuffer_AddPointer(struct ReportRingBuffer *ring) { UINT idx; EnterCriticalSection(&ring->lock); for (idx = 0; idx < ring->pointer_alloc; idx++) if (ring->pointers[idx] == POINTER_UNUSED) break; if (idx >= ring->pointer_alloc) { int count = idx = ring->pointer_alloc; ring->pointer_alloc *= 2; ring->pointers = HeapReAlloc(GetProcessHeap(), 0, ring->pointers, sizeof(UINT) * ring->pointer_alloc); for( ;count < ring->pointer_alloc; count++) ring->pointers[count] = POINTER_UNUSED; } ring->pointers[idx] = ring->end; LeaveCriticalSection(&ring->lock); return idx; } void RingBuffer_RemovePointer(struct ReportRingBuffer *ring, UINT index) { EnterCriticalSection(&ring->lock); if (index < ring->pointer_alloc) ring->pointers[index] = POINTER_UNUSED; LeaveCriticalSection(&ring->lock); } void RingBuffer_Write(struct ReportRingBuffer *ring, void *data) { UINT i; EnterCriticalSection(&ring->lock); memcpy(&ring->buffer[ring->end * ring->buffer_size], data, ring->buffer_size); ring->end++; if (ring->end == ring->size) ring->end = 0; if (ring->start == ring->end) { ring->start++; if (ring->start == ring->size) ring->start = 0; } for (i = 0; i < ring->pointer_alloc; i++) if (ring->pointers[i] == ring->end) ring->pointers[i] = ring->start; LeaveCriticalSection(&ring->lock); }