view ref/buffer.d @ 4:a1202aac1124

Started implementing separate encoder class
author fraserofthenight
date Wed, 08 Jul 2009 19:16:39 -0700
parents
children
line wrap: on
line source

/**
 * Hoofbaby -- http://www.dsource.org/projects/hoofbaby
 * Copyright (C) 2009 Robert Fraser
 * 
 * This program is free software; you can redistribute it andor
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 */

module hoofbaby.util.buffer;

import tango.time.StopWatch;
import tango.core.sync.Condition;
import tango.core.sync.Mutex;

version(Windows)
{
	import tango.stdc.stringz;
	import tango.text.convert.Format;
	import tango.sys.win32.UserGdi;
	
	import hoofbaby.util.exception;

	private extern(Windows)
	{
		HANDLE CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCTSTR);
		LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD);
		LPVOID MapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, DWORD dwNumberOfBytesToMap, LPVOID lpBaseAddress);
		BOOL UnmapViewOfFile(LPCVOID);
	}
	
	private uint bufNum = 0;
	
	/**
	 * Allocates a buffer of the given size. ALl accesses up to buf + (size * 2)
	 * are valid; those over buf + size will reach around
	 * 
	 * Params:
	 *     size = The size of the buffer (must be multiple of page size; use large powers of two)
	 * Returns: A pointer to the beginning of that memory
	 */
	private void* _allocBuffer(uint size)
	{
		// Ensure page file is the right size
		SYSTEM_INFO info;
		GetSystemInfo(&info);
		uint pageSize = info.dwAllocationGranularity;
		if(size % pageSize) // PERHAPS next multiple of instead of error?
			throw new IllegalOperationException(Format("Memory map pagefile mismatch -- "
					"requested {0}, but page size is {1}", size, pageSize));
		
		// Try to allocate the buffer
		HANDLE mmap = CreateFileMappingA(INVALID_HANDLE_VALUE, null, PAGE_READWRITE, 0, size, null);
		if(!mmap) throw new UnrecoverableException("Could not create memory mapping for buffer");
		void* buf = MapViewOfFile(mmap, FILE_MAP_ALL_ACCESS, 0, 0, size);
		if(!buf) throw new UnrecoverableException("Could not map view of buffer");
		void* buf2 = MapViewOfFileEx(mmap, FILE_MAP_ALL_ACCESS, 0, 0, size, buf + size);
		if(!buf2) throw new UnrecoverableException("Could not map second view of buffer");
		if(buf2 != buf + size) throw new UnrecoverableException("Memory mapped buffers are not contiguous");
		return buf;
	}
	
	private void _freeBuffer(void* buffer, uint size)
	{
		UnmapViewOfFile(buffer);
		UnmapViewOfFile(buffer + size);
	}
}
else
{
	static assert(false, "Unsupported operating system");
}

// IMPORTANT synchronization!!!!
public struct RingBuffer
{
	private const double WAIT_TIME = 0.01; // 10 ms
	private static Condition NULL_CONDITION;
	
	private uint size;
	private void* addr;
	
	private uint readOfs;
	private uint writeOfs;
	
	/**
	 * The callback function to be called upon an overflow or underflow.
	 * 
	 * Params:
	 *     buffer = The RIngBuffer instance with the issue
	 *     sizeRequested = the size the operation requested
	 *     sizeAvailable = how much is actually there
	 * Returns: true if the function should return null, false if the buffer should still
	 *          return the requested pointer (i.e. if the function waited until something
	 *          was available)
	 */
	public alias bool delegate(RingBuffer* buffer, uint sizeRequested, uint sizeAvialbale) BufferCallback;
	public BufferCallback onUnderflow;
	public BufferCallback onOverflow;
	
	debug(AVBuffer)
	{
		private uint readRequested = 0;
		private uint writeRequested = 0;
	}
	
	public static RingBuffer opCall(uint size)
	{
		// Not really a singleton; who cares if more than one is constructed?
		if(!NULL_CONDITION)
			NULL_CONDITION = new Condition(new Mutex());
		
		RingBuffer r;
		assert(size > 0);
		r.size = size;
		r.addr = _allocBuffer(size);
		return r;
	}
	
	public void free()
	{
		_freeBuffer(addr, size);
		addr = null;
	}
	
	public void* beginRead(uint requested)
	{
		debug(AVBuffer)
		{
			assert(readRequested == 0);
			readRequested = requested;
		}
		
		if(requested > available)
		{
			if(!onUnderflow || onUnderflow(this, requested, available))
			{
				debug(AVBuffer) readRequested = 0;
				return null;
			}
		}
		
		return addr + readOfs;
	}
	
	public void endRead(uint bytes)
	{
		debug(AVBuffer)
		{
			assert(bytes == readRequested);
			readRequested = 0;
		}
		
		readOfs += bytes;
		if(readOfs > size)
		{
			readOfs -= bytes;
			writeOfs -= bytes;
		}
	}
	
	public void* beginWrite(uint requested)
	{
		debug(AVBuffer)
		{
			assert(writeRequested == 0);
			writeRequested = requested;
		}
		
		if(requested > freeSpace)
		{
			if(!onOverflow || onOverflow(this, requested, available))
			{
				return null;
			}
		}
		
		return addr + writeOfs;
	}

	public void endWrite(uint bytes)
	{
		debug(AVBuffer)
		{
			assert(bytes == writeRequested);
			writeRequested = 0;
		}
		
		writeOfs += bytes;
	}
	
	public uint available()
	{
		return writeOfs - readOfs;
	}
	
	public uint freeSpace()
	{
		return size - available();
	}
	
	public uint totalSize()
	{
		return size;
	}
	
	public void clear()
	{
		readOfs = 0;
		writeOfs = 0;
	}
	
	public void isEmpty()
	{
		return readOfs == writeOfs;
	}
	
	private bool _waitFor(char[] exp)(uint bytes, uint timeoutMs, Condition cond = null)
	{
		if(!cond)
			cond = NULL_CONDITION;
		
		ulong microsecs = timeoutMs * 1000;
		StopWatch sw;
		sw.start();
		do
		{
			if(mixin(exp) >= bytes)
				return true;
			if(cond.wait(WAIT_TIME))
				return false;
		} while(sw.microsec() < microsecs);
		return false;
	}
	
	public alias _waitFor!("available") waitAvailable;
	public alias _waitFor!("freeSpace") waitFreeSpace;
}