diff src/impl/hoofbaby/util/buffer.d @ 0:3425707ddbf6

Initial import (hopefully this mercurial stuff works...)
author fraserofthenight
date Mon, 06 Jul 2009 08:06:28 -0700
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/impl/hoofbaby/util/buffer.d	Mon Jul 06 08:06:28 2009 -0700
@@ -0,0 +1,244 @@
+/**
+ * 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;
+}
\ No newline at end of file