Mercurial > projects > hoofbaby
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