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

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