FreeRDP/winpr/libwinpr/utils/collections/BipBuffer.c

457 lines
8.3 KiB
C
Raw Normal View History

/**
* WinPR: Windows Portable Runtime
* BipBuffer
*
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
2019-01-30 11:36:21 +03:00
#include <limits.h>
#include <winpr/crt.h>
2015-03-14 01:37:48 +03:00
#include <winpr/sysinfo.h>
#include <winpr/collections.h>
struct _wBipBlock
{
size_t index;
size_t size;
};
typedef struct _wBipBlock wBipBlock;
struct _wBipBuffer
{
size_t size;
BYTE* buffer;
size_t pageSize;
wBipBlock blockA;
wBipBlock blockB;
wBipBlock readR;
wBipBlock writeR;
};
/**
* The Bip Buffer - The Circular Buffer with a Twist:
* http://www.codeproject.com/Articles/3479/The-Bip-Buffer-The-Circular-Buffer-with-a-Twist
*/
static INLINE void BipBlock_Clear(wBipBlock* _bbl)
{
_bbl->index = _bbl->size = 0;
}
2015-03-14 03:22:21 +03:00
static INLINE void BipBlock_Copy(wBipBlock* _dst, const wBipBlock* _src)
{
_dst->index = _src->index;
_dst->size = _src->size;
}
2015-03-14 03:22:21 +03:00
void BipBuffer_Clear(wBipBuffer* bb)
{
BipBlock_Clear(&bb->blockA);
BipBlock_Clear(&bb->blockB);
BipBlock_Clear(&bb->readR);
BipBlock_Clear(&bb->writeR);
}
2019-11-20 13:30:14 +03:00
static BOOL BipBuffer_AllocBuffer(wBipBuffer* bb, size_t size)
{
if (size < 1)
return FALSE;
2015-03-14 01:37:48 +03:00
size += size % bb->pageSize;
2019-11-06 17:24:51 +03:00
bb->buffer = (BYTE*)malloc(size);
if (!bb->buffer)
return FALSE;
bb->size = size;
return TRUE;
}
BOOL BipBuffer_Grow(wBipBuffer* bb, size_t size)
{
BYTE* block;
BYTE* buffer;
size_t blockSize = 0;
size_t commitSize = 0;
2015-03-14 01:37:48 +03:00
size += size % bb->pageSize;
if (size <= bb->size)
return TRUE;
2019-11-06 17:24:51 +03:00
buffer = (BYTE*)malloc(size);
if (!buffer)
return FALSE;
2015-03-14 01:37:48 +03:00
block = BipBuffer_ReadTryReserve(bb, 0, &blockSize);
if (block)
{
CopyMemory(&buffer[commitSize], block, blockSize);
2015-03-14 01:37:48 +03:00
BipBuffer_ReadCommit(bb, blockSize);
commitSize += blockSize;
}
2015-03-14 01:37:48 +03:00
block = BipBuffer_ReadTryReserve(bb, 0, &blockSize);
if (block)
{
CopyMemory(&buffer[commitSize], block, blockSize);
2015-03-14 01:37:48 +03:00
BipBuffer_ReadCommit(bb, blockSize);
commitSize += blockSize;
}
BipBuffer_Clear(bb);
free(bb->buffer);
bb->buffer = buffer;
bb->size = size;
2015-03-14 03:22:21 +03:00
bb->blockA.index = 0;
bb->blockA.size = commitSize;
return TRUE;
}
2019-11-20 13:30:14 +03:00
static void BipBuffer_FreeBuffer(wBipBuffer* bb)
{
if (bb->buffer)
{
free(bb->buffer);
bb->buffer = NULL;
}
BipBuffer_Clear(bb);
}
2015-03-14 01:37:48 +03:00
size_t BipBuffer_UsedSize(wBipBuffer* bb)
{
2015-03-14 03:22:21 +03:00
return bb->blockA.size + bb->blockB.size;
}
2015-03-14 01:37:48 +03:00
size_t BipBuffer_BufferSize(wBipBuffer* bb)
{
2015-03-14 01:37:48 +03:00
return bb->size;
}
2015-03-14 01:37:48 +03:00
BYTE* BipBuffer_WriteTryReserve(wBipBuffer* bb, size_t size, size_t* reserved)
{
2015-03-14 01:37:48 +03:00
size_t reservable;
if (!reserved)
return NULL;
2015-03-14 03:22:21 +03:00
if (!bb->blockB.size)
{
/* block B does not exist */
2015-03-14 03:22:21 +03:00
reservable = bb->size - bb->blockA.index - bb->blockA.size; /* space after block A */
2015-03-14 03:22:21 +03:00
if (reservable >= bb->blockA.index)
{
2015-03-14 01:37:48 +03:00
if (reservable == 0)
return NULL;
2015-03-14 01:37:48 +03:00
if (size < reservable)
reservable = size;
2015-03-14 03:22:21 +03:00
bb->writeR.size = reservable;
2015-03-14 01:37:48 +03:00
*reserved = reservable;
2015-03-14 03:22:21 +03:00
bb->writeR.index = bb->blockA.index + bb->blockA.size;
return &bb->buffer[bb->writeR.index];
}
2015-03-14 03:22:21 +03:00
if (bb->blockA.index == 0)
return NULL;
2015-03-14 03:22:21 +03:00
if (bb->blockA.index < size)
size = bb->blockA.index;
2015-03-14 03:22:21 +03:00
bb->writeR.size = size;
*reserved = size;
2015-03-14 03:22:21 +03:00
bb->writeR.index = 0;
return bb->buffer;
}
/* block B exists */
2015-03-14 03:22:21 +03:00
reservable = bb->blockA.index - bb->blockB.index - bb->blockB.size; /* space after block B */
2015-03-14 01:37:48 +03:00
if (size < reservable)
reservable = size;
2015-03-14 01:37:48 +03:00
if (reservable == 0)
return NULL;
2015-03-14 03:22:21 +03:00
bb->writeR.size = reservable;
2015-03-14 01:37:48 +03:00
*reserved = reservable;
2015-03-14 03:22:21 +03:00
bb->writeR.index = bb->blockB.index + bb->blockB.size;
return &bb->buffer[bb->writeR.index];
}
2015-03-14 01:37:48 +03:00
BYTE* BipBuffer_WriteReserve(wBipBuffer* bb, size_t size)
{
BYTE* block = NULL;
size_t reserved = 0;
2015-03-14 01:37:48 +03:00
block = BipBuffer_WriteTryReserve(bb, size, &reserved);
if (reserved == size)
return block;
if (!BipBuffer_Grow(bb, size))
return NULL;
2015-03-14 01:37:48 +03:00
block = BipBuffer_WriteTryReserve(bb, size, &reserved);
return block;
}
2015-03-14 01:37:48 +03:00
void BipBuffer_WriteCommit(wBipBuffer* bb, size_t size)
{
if (size == 0)
{
BipBlock_Clear(&bb->writeR);
return;
}
2015-03-14 03:22:21 +03:00
if (size > bb->writeR.size)
size = bb->writeR.size;
2015-03-14 03:22:21 +03:00
if ((bb->blockA.size == 0) && (bb->blockB.size == 0))
{
2015-03-14 03:22:21 +03:00
bb->blockA.index = bb->writeR.index;
bb->blockA.size = size;
BipBlock_Clear(&bb->writeR);
return;
}
2015-03-14 03:22:21 +03:00
if (bb->writeR.index == (bb->blockA.size + bb->blockA.index))
bb->blockA.size += size;
else
2015-03-14 03:22:21 +03:00
bb->blockB.size += size;
BipBlock_Clear(&bb->writeR);
2015-03-14 03:22:21 +03:00
}
SSIZE_T BipBuffer_Write(wBipBuffer* bb, const BYTE* data, size_t size)
2015-03-14 03:22:21 +03:00
{
2019-01-30 11:36:21 +03:00
size_t status = 0;
2015-03-14 03:22:21 +03:00
BYTE* block = NULL;
size_t writeSize = 0;
size_t blockSize = 0;
if (size == 0)
return 0;
if (!bb || !data)
2015-03-14 03:22:21 +03:00
return -1;
if (size > SSIZE_MAX)
size = SSIZE_MAX;
2015-03-14 03:22:21 +03:00
block = BipBuffer_WriteReserve(bb, size);
if (!block)
return -1;
block = BipBuffer_WriteTryReserve(bb, size - status, &blockSize);
if (block)
{
writeSize = size - status;
if (writeSize > blockSize)
writeSize = blockSize;
CopyMemory(block, &data[status], writeSize);
BipBuffer_WriteCommit(bb, writeSize);
2019-01-30 11:36:21 +03:00
status += writeSize;
2015-03-14 03:22:21 +03:00
if ((status == size) || (writeSize < blockSize))
2019-01-30 11:36:21 +03:00
return (SSIZE_T)status;
2015-03-14 03:22:21 +03:00
}
block = BipBuffer_WriteTryReserve(bb, size - status, &blockSize);
if (block)
{
writeSize = size - status;
if (writeSize > blockSize)
writeSize = blockSize;
CopyMemory(block, &data[status], writeSize);
BipBuffer_WriteCommit(bb, writeSize);
2019-01-30 11:36:21 +03:00
status += writeSize;
2015-03-14 03:22:21 +03:00
if ((status == size) || (writeSize < blockSize))
2019-01-30 11:36:21 +03:00
return (SSIZE_T)status;
2015-03-14 03:22:21 +03:00
}
2019-01-30 11:36:21 +03:00
return (SSIZE_T)status;
}
2015-03-14 01:37:48 +03:00
BYTE* BipBuffer_ReadTryReserve(wBipBuffer* bb, size_t size, size_t* reserved)
{
2015-03-14 01:37:48 +03:00
size_t reservable = 0;
if (!reserved)
return NULL;
2015-03-14 03:22:21 +03:00
if (bb->blockA.size == 0)
{
2015-03-14 01:37:48 +03:00
*reserved = 0;
return NULL;
}
2015-03-14 03:22:21 +03:00
reservable = bb->blockA.size;
2015-03-14 01:37:48 +03:00
if (size && (reservable > size))
reservable = size;
*reserved = reservable;
2015-03-14 03:22:21 +03:00
return &bb->buffer[bb->blockA.index];
}
2015-03-14 01:37:48 +03:00
BYTE* BipBuffer_ReadReserve(wBipBuffer* bb, size_t size)
{
BYTE* block = NULL;
size_t reserved = 0;
2015-03-14 03:22:21 +03:00
if (BipBuffer_UsedSize(bb) < size)
return NULL;
2015-03-14 01:37:48 +03:00
block = BipBuffer_ReadTryReserve(bb, size, &reserved);
if (reserved == size)
return block;
2015-03-14 03:22:21 +03:00
if (!BipBuffer_Grow(bb, bb->size + 1))
2015-03-14 01:37:48 +03:00
return NULL;
block = BipBuffer_ReadTryReserve(bb, size, &reserved);
if (reserved != size)
return NULL;
2015-03-14 01:37:48 +03:00
return block;
}
void BipBuffer_ReadCommit(wBipBuffer* bb, size_t size)
{
if (!bb)
return;
2015-03-14 03:22:21 +03:00
if (size >= bb->blockA.size)
{
BipBlock_Copy(&bb->blockA, &bb->blockB);
BipBlock_Clear(&bb->blockB);
}
else
{
2015-03-14 03:22:21 +03:00
bb->blockA.size -= size;
bb->blockA.index += size;
}
}
2019-01-30 11:36:21 +03:00
SSIZE_T BipBuffer_Read(wBipBuffer* bb, BYTE* data, size_t size)
{
2019-01-30 11:36:21 +03:00
size_t status = 0;
BYTE* block = NULL;
size_t readSize = 0;
size_t blockSize = 0;
if (size == 0)
return 0;
if (!bb || !data)
return -1;
if (size > SSIZE_MAX)
size = SSIZE_MAX;
2015-03-14 01:37:48 +03:00
block = BipBuffer_ReadTryReserve(bb, 0, &blockSize);
if (block)
{
readSize = size - status;
if (readSize > blockSize)
readSize = blockSize;
CopyMemory(&data[status], block, readSize);
2015-03-14 01:37:48 +03:00
BipBuffer_ReadCommit(bb, readSize);
2019-01-30 11:36:21 +03:00
status += readSize;
if ((status == size) || (readSize < blockSize))
2019-01-30 11:36:21 +03:00
return (SSIZE_T)status;
}
2015-03-14 01:37:48 +03:00
block = BipBuffer_ReadTryReserve(bb, 0, &blockSize);
if (block)
{
readSize = size - status;
if (readSize > blockSize)
readSize = blockSize;
CopyMemory(&data[status], block, readSize);
2015-03-14 01:37:48 +03:00
BipBuffer_ReadCommit(bb, readSize);
2019-01-30 11:36:21 +03:00
status += readSize;
if ((status == size) || (readSize < blockSize))
2019-01-30 11:36:21 +03:00
return (SSIZE_T)status;
}
2019-01-30 11:36:21 +03:00
return (SSIZE_T)status;
}
/**
* Construction, Destruction
*/
wBipBuffer* BipBuffer_New(size_t size)
{
wBipBuffer* bb;
2019-11-06 17:24:51 +03:00
bb = (wBipBuffer*)calloc(1, sizeof(wBipBuffer));
if (bb)
{
2015-03-14 01:37:48 +03:00
SYSTEM_INFO si;
GetSystemInfo(&si);
2019-11-06 17:24:51 +03:00
bb->pageSize = (size_t)si.dwPageSize;
2015-03-14 01:37:48 +03:00
if (bb->pageSize < 4096)
bb->pageSize = 4096;
if (!BipBuffer_AllocBuffer(bb, size))
{
free(bb);
return NULL;
}
}
return bb;
}
void BipBuffer_Free(wBipBuffer* bb)
{
if (!bb)
return;
BipBuffer_FreeBuffer(bb);
free(bb);
}