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

Packit Service fa4841
/**
Packit Service fa4841
 * WinPR: Windows Portable Runtime
Packit Service fa4841
 * BipBuffer
Packit Service fa4841
 *
Packit Service fa4841
 * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
Packit Service fa4841
 *
Packit Service fa4841
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit Service fa4841
 * you may not use this file except in compliance with the License.
Packit Service fa4841
 * You may obtain a copy of the License at
Packit Service fa4841
 *
Packit Service fa4841
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit Service fa4841
 *
Packit Service fa4841
 * Unless required by applicable law or agreed to in writing, software
Packit Service fa4841
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit Service fa4841
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit Service fa4841
 * See the License for the specific language governing permissions and
Packit Service fa4841
 * limitations under the License.
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
#ifdef HAVE_CONFIG_H
Packit Service fa4841
#include "config.h"
Packit Service fa4841
#endif
Packit Service fa4841
Packit Service b1ea74
#include <limits.h>
Packit Service fa4841
#include <winpr/crt.h>
Packit Service fa4841
#include <winpr/sysinfo.h>
Packit Service fa4841
Packit Service fa4841
#include <winpr/collections.h>
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * The Bip Buffer - The Circular Buffer with a Twist:
Packit Service fa4841
 * http://www.codeproject.com/Articles/3479/The-Bip-Buffer-The-Circular-Buffer-with-a-Twist
Packit Service fa4841
 */
Packit Service fa4841
Packit Service b1ea74
#define BipBlock_Clear(_bbl) _bbl.index = _bbl.size = 0
Packit Service fa4841
Packit Service fa4841
#define BipBlock_Copy(_dst, _src) \
Packit Service b1ea74
	_dst.index = _src.index;      \
Packit Service fa4841
	_dst.size = _src.size
Packit Service fa4841
Packit Service fa4841
void BipBuffer_Clear(wBipBuffer* bb)
Packit Service fa4841
{
Packit Service fa4841
	BipBlock_Clear(bb->blockA);
Packit Service fa4841
	BipBlock_Clear(bb->blockB);
Packit Service fa4841
	BipBlock_Clear(bb->readR);
Packit Service fa4841
	BipBlock_Clear(bb->writeR);
Packit Service fa4841
}
Packit Service fa4841
Packit Service b1ea74
static BOOL BipBuffer_AllocBuffer(wBipBuffer* bb, size_t size)
Packit Service fa4841
{
Packit Service fa4841
	if (size < 1)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	size += size % bb->pageSize;
Packit Service b1ea74
	bb->buffer = (BYTE*)malloc(size);
Packit Service fa4841
Packit Service fa4841
	if (!bb->buffer)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	bb->size = size;
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
BOOL BipBuffer_Grow(wBipBuffer* bb, size_t size)
Packit Service fa4841
{
Packit Service fa4841
	BYTE* block;
Packit Service fa4841
	BYTE* buffer;
Packit Service fa4841
	size_t blockSize = 0;
Packit Service fa4841
	size_t commitSize = 0;
Packit Service fa4841
	size += size % bb->pageSize;
Packit Service fa4841
Packit Service fa4841
	if (size <= bb->size)
Packit Service fa4841
		return TRUE;
Packit Service fa4841
Packit Service b1ea74
	buffer = (BYTE*)malloc(size);
Packit Service fa4841
Packit Service fa4841
	if (!buffer)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	block = BipBuffer_ReadTryReserve(bb, 0, &blockSize);
Packit Service fa4841
Packit Service fa4841
	if (block)
Packit Service fa4841
	{
Packit Service fa4841
		CopyMemory(&buffer[commitSize], block, blockSize);
Packit Service fa4841
		BipBuffer_ReadCommit(bb, blockSize);
Packit Service fa4841
		commitSize += blockSize;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	block = BipBuffer_ReadTryReserve(bb, 0, &blockSize);
Packit Service fa4841
Packit Service fa4841
	if (block)
Packit Service fa4841
	{
Packit Service fa4841
		CopyMemory(&buffer[commitSize], block, blockSize);
Packit Service fa4841
		BipBuffer_ReadCommit(bb, blockSize);
Packit Service fa4841
		commitSize += blockSize;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	BipBuffer_Clear(bb);
Packit Service fa4841
	free(bb->buffer);
Packit Service fa4841
	bb->buffer = buffer;
Packit Service fa4841
	bb->size = size;
Packit Service fa4841
	bb->blockA.index = 0;
Packit Service fa4841
	bb->blockA.size = commitSize;
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service b1ea74
static void BipBuffer_FreeBuffer(wBipBuffer* bb)
Packit Service fa4841
{
Packit Service fa4841
	if (bb->buffer)
Packit Service fa4841
	{
Packit Service fa4841
		free(bb->buffer);
Packit Service fa4841
		bb->buffer = NULL;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	BipBuffer_Clear(bb);
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
size_t BipBuffer_UsedSize(wBipBuffer* bb)
Packit Service fa4841
{
Packit Service fa4841
	return bb->blockA.size + bb->blockB.size;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
size_t BipBuffer_BufferSize(wBipBuffer* bb)
Packit Service fa4841
{
Packit Service fa4841
	return bb->size;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
BYTE* BipBuffer_WriteTryReserve(wBipBuffer* bb, size_t size, size_t* reserved)
Packit Service fa4841
{
Packit Service fa4841
	size_t reservable;
Packit Service fa4841
Packit Service fa4841
	if (!reserved)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	if (!bb->blockB.size)
Packit Service fa4841
	{
Packit Service fa4841
		/* block B does not exist */
Packit Service fa4841
		reservable = bb->size - bb->blockA.index - bb->blockA.size; /* space after block A */
Packit Service fa4841
Packit Service fa4841
		if (reservable >= bb->blockA.index)
Packit Service fa4841
		{
Packit Service fa4841
			if (reservable == 0)
Packit Service fa4841
				return NULL;
Packit Service fa4841
Packit Service fa4841
			if (size < reservable)
Packit Service fa4841
				reservable = size;
Packit Service fa4841
Packit Service fa4841
			bb->writeR.size = reservable;
Packit Service fa4841
			*reserved = reservable;
Packit Service fa4841
			bb->writeR.index = bb->blockA.index + bb->blockA.size;
Packit Service fa4841
			return &bb->buffer[bb->writeR.index];
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
		if (bb->blockA.index == 0)
Packit Service fa4841
			return NULL;
Packit Service fa4841
Packit Service fa4841
		if (bb->blockA.index < size)
Packit Service fa4841
			size = bb->blockA.index;
Packit Service fa4841
Packit Service fa4841
		bb->writeR.size = size;
Packit Service fa4841
		*reserved = size;
Packit Service fa4841
		bb->writeR.index = 0;
Packit Service fa4841
		return bb->buffer;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	/* block B exists */
Packit Service fa4841
	reservable = bb->blockA.index - bb->blockB.index - bb->blockB.size; /* space after block B */
Packit Service fa4841
Packit Service fa4841
	if (size < reservable)
Packit Service fa4841
		reservable = size;
Packit Service fa4841
Packit Service fa4841
	if (reservable == 0)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	bb->writeR.size = reservable;
Packit Service fa4841
	*reserved = reservable;
Packit Service fa4841
	bb->writeR.index = bb->blockB.index + bb->blockB.size;
Packit Service fa4841
	return &bb->buffer[bb->writeR.index];
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
BYTE* BipBuffer_WriteReserve(wBipBuffer* bb, size_t size)
Packit Service fa4841
{
Packit Service fa4841
	BYTE* block = NULL;
Packit Service fa4841
	size_t reserved = 0;
Packit Service fa4841
	block = BipBuffer_WriteTryReserve(bb, size, &reserved);
Packit Service fa4841
Packit Service fa4841
	if (reserved == size)
Packit Service fa4841
		return block;
Packit Service fa4841
Packit Service fa4841
	if (!BipBuffer_Grow(bb, size))
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	block = BipBuffer_WriteTryReserve(bb, size, &reserved);
Packit Service fa4841
	return block;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
void BipBuffer_WriteCommit(wBipBuffer* bb, size_t size)
Packit Service fa4841
{
Packit Service fa4841
	if (size == 0)
Packit Service fa4841
	{
Packit Service fa4841
		BipBlock_Clear(bb->writeR);
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (size > bb->writeR.size)
Packit Service fa4841
		size = bb->writeR.size;
Packit Service fa4841
Packit Service fa4841
	if ((bb->blockA.size == 0) && (bb->blockB.size == 0))
Packit Service fa4841
	{
Packit Service fa4841
		bb->blockA.index = bb->writeR.index;
Packit Service fa4841
		bb->blockA.size = size;
Packit Service fa4841
		BipBlock_Clear(bb->writeR);
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (bb->writeR.index == (bb->blockA.size + bb->blockA.index))
Packit Service fa4841
		bb->blockA.size += size;
Packit Service fa4841
	else
Packit Service fa4841
		bb->blockB.size += size;
Packit Service fa4841
Packit Service fa4841
	BipBlock_Clear(bb->writeR);
Packit Service fa4841
}
Packit Service fa4841
Packit Service b1ea74
SSIZE_T BipBuffer_Write(wBipBuffer* bb, const BYTE* data, size_t size)
Packit Service fa4841
{
Packit Service b1ea74
	size_t status = 0;
Packit Service fa4841
	BYTE* block = NULL;
Packit Service fa4841
	size_t writeSize = 0;
Packit Service fa4841
	size_t blockSize = 0;
Packit Service fa4841
Packit Service b1ea74
	if (size == 0)
Packit Service b1ea74
		return 0;
Packit Service b1ea74
Packit Service b1ea74
	if (!bb || !data)
Packit Service fa4841
		return -1;
Packit Service fa4841
Packit Service b1ea74
	if (size > SSIZE_MAX)
Packit Service b1ea74
		size = SSIZE_MAX;
Packit Service b1ea74
Packit Service fa4841
	block = BipBuffer_WriteReserve(bb, size);
Packit Service fa4841
Packit Service fa4841
	if (!block)
Packit Service fa4841
		return -1;
Packit Service fa4841
Packit Service fa4841
	block = BipBuffer_WriteTryReserve(bb, size - status, &blockSize);
Packit Service fa4841
Packit Service fa4841
	if (block)
Packit Service fa4841
	{
Packit Service fa4841
		writeSize = size - status;
Packit Service fa4841
Packit Service fa4841
		if (writeSize > blockSize)
Packit Service fa4841
			writeSize = blockSize;
Packit Service fa4841
Packit Service fa4841
		CopyMemory(block, &data[status], writeSize);
Packit Service fa4841
		BipBuffer_WriteCommit(bb, writeSize);
Packit Service b1ea74
		status += writeSize;
Packit Service fa4841
Packit Service fa4841
		if ((status == size) || (writeSize < blockSize))
Packit Service b1ea74
			return (SSIZE_T)status;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	block = BipBuffer_WriteTryReserve(bb, size - status, &blockSize);
Packit Service fa4841
Packit Service fa4841
	if (block)
Packit Service fa4841
	{
Packit Service fa4841
		writeSize = size - status;
Packit Service fa4841
Packit Service fa4841
		if (writeSize > blockSize)
Packit Service fa4841
			writeSize = blockSize;
Packit Service fa4841
Packit Service fa4841
		CopyMemory(block, &data[status], writeSize);
Packit Service fa4841
		BipBuffer_WriteCommit(bb, writeSize);
Packit Service b1ea74
		status += writeSize;
Packit Service fa4841
Packit Service fa4841
		if ((status == size) || (writeSize < blockSize))
Packit Service b1ea74
			return (SSIZE_T)status;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service b1ea74
	return (SSIZE_T)status;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
BYTE* BipBuffer_ReadTryReserve(wBipBuffer* bb, size_t size, size_t* reserved)
Packit Service fa4841
{
Packit Service fa4841
	size_t reservable = 0;
Packit Service fa4841
Packit Service fa4841
	if (!reserved)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	if (bb->blockA.size == 0)
Packit Service fa4841
	{
Packit Service fa4841
		*reserved = 0;
Packit Service fa4841
		return NULL;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	reservable = bb->blockA.size;
Packit Service fa4841
Packit Service fa4841
	if (size && (reservable > size))
Packit Service fa4841
		reservable = size;
Packit Service fa4841
Packit Service fa4841
	*reserved = reservable;
Packit Service fa4841
	return &bb->buffer[bb->blockA.index];
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
BYTE* BipBuffer_ReadReserve(wBipBuffer* bb, size_t size)
Packit Service fa4841
{
Packit Service fa4841
	BYTE* block = NULL;
Packit Service fa4841
	size_t reserved = 0;
Packit Service fa4841
Packit Service fa4841
	if (BipBuffer_UsedSize(bb) < size)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	block = BipBuffer_ReadTryReserve(bb, size, &reserved);
Packit Service fa4841
Packit Service fa4841
	if (reserved == size)
Packit Service fa4841
		return block;
Packit Service fa4841
Packit Service fa4841
	if (!BipBuffer_Grow(bb, bb->size + 1))
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	block = BipBuffer_ReadTryReserve(bb, size, &reserved);
Packit Service fa4841
Packit Service fa4841
	if (reserved != size)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	return block;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
void BipBuffer_ReadCommit(wBipBuffer* bb, size_t size)
Packit Service fa4841
{
Packit Service fa4841
	if (!bb)
Packit Service fa4841
		return;
Packit Service fa4841
Packit Service fa4841
	if (size >= bb->blockA.size)
Packit Service fa4841
	{
Packit Service fa4841
		BipBlock_Copy(bb->blockA, bb->blockB);
Packit Service fa4841
		BipBlock_Clear(bb->blockB);
Packit Service fa4841
	}
Packit Service fa4841
	else
Packit Service fa4841
	{
Packit Service fa4841
		bb->blockA.size -= size;
Packit Service fa4841
		bb->blockA.index += size;
Packit Service fa4841
	}
Packit Service fa4841
}
Packit Service fa4841
Packit Service b1ea74
SSIZE_T BipBuffer_Read(wBipBuffer* bb, BYTE* data, size_t size)
Packit Service fa4841
{
Packit Service b1ea74
	size_t status = 0;
Packit Service fa4841
	BYTE* block = NULL;
Packit Service fa4841
	size_t readSize = 0;
Packit Service fa4841
	size_t blockSize = 0;
Packit Service fa4841
Packit Service b1ea74
	if (size == 0)
Packit Service b1ea74
		return 0;
Packit Service b1ea74
Packit Service b1ea74
	if (!bb || !data)
Packit Service fa4841
		return -1;
Packit Service fa4841
Packit Service b1ea74
	if (size > SSIZE_MAX)
Packit Service b1ea74
		size = SSIZE_MAX;
Packit Service b1ea74
Packit Service fa4841
	block = BipBuffer_ReadTryReserve(bb, 0, &blockSize);
Packit Service fa4841
Packit Service fa4841
	if (block)
Packit Service fa4841
	{
Packit Service fa4841
		readSize = size - status;
Packit Service fa4841
Packit Service fa4841
		if (readSize > blockSize)
Packit Service fa4841
			readSize = blockSize;
Packit Service fa4841
Packit Service fa4841
		CopyMemory(&data[status], block, readSize);
Packit Service fa4841
		BipBuffer_ReadCommit(bb, readSize);
Packit Service b1ea74
		status += readSize;
Packit Service fa4841
Packit Service fa4841
		if ((status == size) || (readSize < blockSize))
Packit Service b1ea74
			return (SSIZE_T)status;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	block = BipBuffer_ReadTryReserve(bb, 0, &blockSize);
Packit Service fa4841
Packit Service fa4841
	if (block)
Packit Service fa4841
	{
Packit Service fa4841
		readSize = size - status;
Packit Service fa4841
Packit Service fa4841
		if (readSize > blockSize)
Packit Service fa4841
			readSize = blockSize;
Packit Service fa4841
Packit Service fa4841
		CopyMemory(&data[status], block, readSize);
Packit Service fa4841
		BipBuffer_ReadCommit(bb, readSize);
Packit Service b1ea74
		status += readSize;
Packit Service fa4841
Packit Service fa4841
		if ((status == size) || (readSize < blockSize))
Packit Service b1ea74
			return (SSIZE_T)status;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service b1ea74
	return (SSIZE_T)status;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Construction, Destruction
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
wBipBuffer* BipBuffer_New(size_t size)
Packit Service fa4841
{
Packit Service fa4841
	wBipBuffer* bb;
Packit Service b1ea74
	bb = (wBipBuffer*)calloc(1, sizeof(wBipBuffer));
Packit Service fa4841
Packit Service fa4841
	if (bb)
Packit Service fa4841
	{
Packit Service fa4841
		SYSTEM_INFO si;
Packit Service fa4841
		GetSystemInfo(&si);
Packit Service b1ea74
		bb->pageSize = (size_t)si.dwPageSize;
Packit Service fa4841
Packit Service fa4841
		if (bb->pageSize < 4096)
Packit Service fa4841
			bb->pageSize = 4096;
Packit Service fa4841
Packit Service fa4841
		if (!BipBuffer_AllocBuffer(bb, size))
Packit Service fa4841
		{
Packit Service fa4841
			free(bb);
Packit Service fa4841
			return NULL;
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return bb;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
void BipBuffer_Free(wBipBuffer* bb)
Packit Service fa4841
{
Packit Service fa4841
	if (!bb)
Packit Service fa4841
		return;
Packit Service fa4841
Packit Service fa4841
	BipBuffer_FreeBuffer(bb);
Packit Service fa4841
	free(bb);
Packit Service fa4841
}