Blame channels/rdpsnd/client/ios/TPCircularBuffer.h

Packit 1fb8d4
//
Packit 1fb8d4
//  TPCircularBuffer.h
Packit 1fb8d4
//  Circular/Ring buffer implementation
Packit 1fb8d4
//
Packit 1fb8d4
//  https://github.com/michaeltyson/TPCircularBuffer
Packit 1fb8d4
//
Packit 1fb8d4
//  Created by Michael Tyson on 10/12/2011.
Packit 1fb8d4
//
Packit 1fb8d4
//
Packit 1fb8d4
//  This implementation makes use of a virtual memory mapping technique that inserts a virtual copy
Packit Service 5a9772
//  of the buffer memory directly after the buffer's end, negating the need for any buffer
Packit Service 5a9772
//  wrap-around logic. Clients can simply use the returned memory address as if it were contiguous
Packit Service 5a9772
//  space.
Packit Service 5a9772
//
Packit 1fb8d4
//  The implementation is thread-safe in the case of a single producer and single consumer.
Packit 1fb8d4
//
Packit 1fb8d4
//  Virtual memory technique originally proposed by Philip Howard (http://vrb.slashusr.org/), and
Packit 1fb8d4
//  adapted to Darwin by Kurt Revis (http://www.snoize.com,
Packit 1fb8d4
//  http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz)
Packit 1fb8d4
//
Packit 1fb8d4
//
Packit 1fb8d4
//  Copyright (C) 2012-2013 A Tasty Pixel
Packit 1fb8d4
//
Packit 1fb8d4
//  This software is provided 'as-is', without any express or implied
Packit 1fb8d4
//  warranty.  In no event will the authors be held liable for any damages
Packit 1fb8d4
//  arising from the use of this software.
Packit 1fb8d4
//
Packit 1fb8d4
//  Permission is granted to anyone to use this software for any purpose,
Packit 1fb8d4
//  including commercial applications, and to alter it and redistribute it
Packit 1fb8d4
//  freely, subject to the following restrictions:
Packit 1fb8d4
//
Packit 1fb8d4
//  1. The origin of this software must not be misrepresented; you must not
Packit 1fb8d4
//     claim that you wrote the original software. If you use this software
Packit 1fb8d4
//     in a product, an acknowledgment in the product documentation would be
Packit 1fb8d4
//     appreciated but is not required.
Packit 1fb8d4
//
Packit 1fb8d4
//  2. Altered source versions must be plainly marked as such, and must not be
Packit 1fb8d4
//     misrepresented as being the original software.
Packit 1fb8d4
//
Packit 1fb8d4
//  3. This notice may not be removed or altered from any source distribution.
Packit 1fb8d4
//
Packit 1fb8d4
Packit 1fb8d4
#ifndef TPCircularBuffer_h
Packit 1fb8d4
#define TPCircularBuffer_h
Packit 1fb8d4
Packit 1fb8d4
#include <libkern/OSAtomic.h>
Packit 1fb8d4
#include <string.h>
Packit 1fb8d4
#include <assert.h>
Packit 1fb8d4
Packit 1fb8d4
#ifdef __cplusplus
Packit Service 5a9772
extern "C"
Packit Service 5a9772
{
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit Service 5a9772
	typedef struct
Packit Service 5a9772
	{
Packit Service 5a9772
		void* buffer;
Packit Service 5a9772
		int32_t length;
Packit Service 5a9772
		int32_t tail;
Packit Service 5a9772
		int32_t head;
Packit Service 5a9772
		volatile int32_t fillCount;
Packit Service 5a9772
	} TPCircularBuffer;
Packit Service 5a9772
Packit Service 5a9772
	/*!
Packit Service 5a9772
	 * Initialise buffer
Packit Service 5a9772
	 *
Packit Service 5a9772
	 *  Note that the length is advisory only: Because of the way the
Packit Service 5a9772
	 *  memory mirroring technique works, the true buffer length will
Packit Service 5a9772
	 *  be multiples of the device page size (e.g. 4096 bytes)
Packit Service 5a9772
	 *
Packit Service 5a9772
	 * @param buffer Circular buffer
Packit Service 5a9772
	 * @param length Length of buffer
Packit Service 5a9772
	 */
Packit Service 5a9772
	bool TPCircularBufferInit(TPCircularBuffer* buffer, int32_t length);
Packit Service 5a9772
Packit Service 5a9772
	/*!
Packit Service 5a9772
	 * Cleanup buffer
Packit Service 5a9772
	 *
Packit Service 5a9772
	 *  Releases buffer resources.
Packit Service 5a9772
	 */
Packit Service 5a9772
	void TPCircularBufferCleanup(TPCircularBuffer* buffer);
Packit Service 5a9772
Packit Service 5a9772
	/*!
Packit Service 5a9772
	 * Clear buffer
Packit Service 5a9772
	 *
Packit Service 5a9772
	 *  Resets buffer to original, empty state.
Packit Service 5a9772
	 *
Packit Service 5a9772
	 *  This is safe for use by consumer while producer is accessing
Packit Service 5a9772
	 *  buffer.
Packit Service 5a9772
	 */
Packit Service 5a9772
	void TPCircularBufferClear(TPCircularBuffer* buffer);
Packit Service 5a9772
Packit Service 5a9772
	// Reading (consuming)
Packit Service 5a9772
Packit Service 5a9772
	/*!
Packit Service 5a9772
	 * Access end of buffer
Packit Service 5a9772
	 *
Packit Service 5a9772
	 *  This gives you a pointer to the end of the buffer, ready
Packit Service 5a9772
	 *  for reading, and the number of available bytes to read.
Packit Service 5a9772
	 *
Packit Service 5a9772
	 * @param buffer Circular buffer
Packit Service 5a9772
	 * @param availableBytes On output, the number of bytes ready for reading
Packit Service 5a9772
	 * @return Pointer to the first bytes ready for reading, or NULL if buffer is empty
Packit Service 5a9772
	 */
Packit Service 5a9772
	static __inline__ __attribute__((always_inline)) void*
Packit Service 5a9772
	TPCircularBufferTail(TPCircularBuffer* buffer, int32_t* availableBytes)
Packit Service 5a9772
	{
Packit Service 5a9772
		*availableBytes = buffer->fillCount;
Packit Service 5a9772
		if (*availableBytes == 0)
Packit Service 5a9772
			return NULL;
Packit Service 5a9772
		return (void*)((char*)buffer->buffer + buffer->tail);
Packit Service 5a9772
	}
Packit Service 5a9772
Packit Service 5a9772
	/*!
Packit Service 5a9772
	 * Consume bytes in buffer
Packit Service 5a9772
	 *
Packit Service 5a9772
	 *  This frees up the just-read bytes, ready for writing again.
Packit Service 5a9772
	 *
Packit Service 5a9772
	 * @param buffer Circular buffer
Packit Service 5a9772
	 * @param amount Number of bytes to consume
Packit Service 5a9772
	 */
Packit Service 5a9772
	static __inline__ __attribute__((always_inline)) void
Packit Service 5a9772
	TPCircularBufferConsume(TPCircularBuffer* buffer, int32_t amount)
Packit Service 5a9772
	{
Packit Service 5a9772
		buffer->tail = (buffer->tail + amount) % buffer->length;
Packit Service 5a9772
		OSAtomicAdd32Barrier(-amount, &buffer->fillCount);
Packit Service 5a9772
		assert(buffer->fillCount >= 0);
Packit Service 5a9772
	}
Packit Service 5a9772
Packit Service 5a9772
	/*!
Packit Service 5a9772
	 * Version of TPCircularBufferConsume without the memory barrier, for more optimal use in
Packit Service 5a9772
	 * single-threaded contexts
Packit Service 5a9772
	 */
Packit Service 5a9772
	static __inline__ __attribute__((always_inline)) void
Packit Service 5a9772
	TPCircularBufferConsumeNoBarrier(TPCircularBuffer* buffer, int32_t amount)
Packit Service 5a9772
	{
Packit Service 5a9772
		buffer->tail = (buffer->tail + amount) % buffer->length;
Packit Service 5a9772
		buffer->fillCount -= amount;
Packit Service 5a9772
		assert(buffer->fillCount >= 0);
Packit Service 5a9772
	}
Packit Service 5a9772
Packit Service 5a9772
	/*!
Packit Service 5a9772
	 * Access front of buffer
Packit Service 5a9772
	 *
Packit Service 5a9772
	 *  This gives you a pointer to the front of the buffer, ready
Packit Service 5a9772
	 *  for writing, and the number of available bytes to write.
Packit Service 5a9772
	 *
Packit Service 5a9772
	 * @param buffer Circular buffer
Packit Service 5a9772
	 * @param availableBytes On output, the number of bytes ready for writing
Packit Service 5a9772
	 * @return Pointer to the first bytes ready for writing, or NULL if buffer is full
Packit Service 5a9772
	 */
Packit Service 5a9772
	static __inline__ __attribute__((always_inline)) void*
Packit Service 5a9772
	TPCircularBufferHead(TPCircularBuffer* buffer, int32_t* availableBytes)
Packit Service 5a9772
	{
Packit Service 5a9772
		*availableBytes = (buffer->length - buffer->fillCount);
Packit Service 5a9772
		if (*availableBytes == 0)
Packit Service 5a9772
			return NULL;
Packit Service 5a9772
		return (void*)((char*)buffer->buffer + buffer->head);
Packit Service 5a9772
	}
Packit Service 5a9772
Packit Service 5a9772
	// Writing (producing)
Packit Service 5a9772
Packit Service 5a9772
	/*!
Packit Service 5a9772
	 * Produce bytes in buffer
Packit Service 5a9772
	 *
Packit Service 5a9772
	 *  This marks the given section of the buffer ready for reading.
Packit Service 5a9772
	 *
Packit Service 5a9772
	 * @param buffer Circular buffer
Packit Service 5a9772
	 * @param amount Number of bytes to produce
Packit Service 5a9772
	 */
Packit Service 5a9772
	static __inline__ __attribute__((always_inline)) void
Packit Service 5a9772
	TPCircularBufferProduce(TPCircularBuffer* buffer, int amount)
Packit Service 5a9772
	{
Packit Service 5a9772
		buffer->head = (buffer->head + amount) % buffer->length;
Packit Service 5a9772
		OSAtomicAdd32Barrier(amount, &buffer->fillCount);
Packit Service 5a9772
		assert(buffer->fillCount <= buffer->length);
Packit Service 5a9772
	}
Packit Service 5a9772
Packit Service 5a9772
	/*!
Packit Service 5a9772
	 * Version of TPCircularBufferProduce without the memory barrier, for more optimal use in
Packit Service 5a9772
	 * single-threaded contexts
Packit Service 5a9772
	 */
Packit Service 5a9772
	static __inline__ __attribute__((always_inline)) void
Packit Service 5a9772
	TPCircularBufferProduceNoBarrier(TPCircularBuffer* buffer, int amount)
Packit Service 5a9772
	{
Packit Service 5a9772
		buffer->head = (buffer->head + amount) % buffer->length;
Packit Service 5a9772
		buffer->fillCount += amount;
Packit Service 5a9772
		assert(buffer->fillCount <= buffer->length);
Packit Service 5a9772
	}
Packit Service 5a9772
Packit Service 5a9772
	/*!
Packit Service 5a9772
	 * Helper routine to copy bytes to buffer
Packit Service 5a9772
	 *
Packit Service 5a9772
	 *  This copies the given bytes to the buffer, and marks them ready for writing.
Packit Service 5a9772
	 *
Packit Service 5a9772
	 * @param buffer Circular buffer
Packit Service 5a9772
	 * @param src Source buffer
Packit Service 5a9772
	 * @param len Number of bytes in source buffer
Packit Service 5a9772
	 * @return true if bytes copied, false if there was insufficient space
Packit Service 5a9772
	 */
Packit Service 5a9772
	static __inline__ __attribute__((always_inline)) bool
Packit Service 5a9772
	TPCircularBufferProduceBytes(TPCircularBuffer* buffer, const void* src, int32_t len)
Packit Service 5a9772
	{
Packit Service 5a9772
		int32_t space;
Packit Service 5a9772
		void* ptr = TPCircularBufferHead(buffer, &space);
Packit Service 5a9772
		if (space < len)
Packit Service 5a9772
			return false;
Packit Service 5a9772
		memcpy(ptr, src, len);
Packit Service 5a9772
		TPCircularBufferProduce(buffer, len);
Packit Service 5a9772
		return true;
Packit Service 5a9772
	}
Packit 1fb8d4
Packit 1fb8d4
#ifdef __cplusplus
Packit 1fb8d4
}
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#endif