Mercurial > projects > hoofbaby
view ref/buffer.d @ 9:05c88622db6b default tip
- Adhoc transcoder now doesn't rely on Platinum
- Moved the adhoc transcoder to the main source tree; it can be moved back into test once it's more stable
author | fraserofthenight |
---|---|
date | Tue, 14 Jul 2009 22:04:05 -0700 |
parents | a1202aac1124 |
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; }