4
|
1 /**
|
|
2 * Hoofbaby -- http://www.dsource.org/projects/hoofbaby
|
|
3 * Copyright (C) 2009 Robert Fraser
|
|
4 *
|
|
5 * This program is free software; you can redistribute it andor
|
|
6 * modify it under the terms of the GNU General Public License
|
|
7 * as published by the Free Software Foundation; either version 2
|
|
8 * of the License, or (at your option) any later version.
|
|
9 *
|
|
10 * This program is distributed in the hope that it will be useful,
|
|
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13 * GNU General Public License for more details.
|
|
14 */
|
|
15
|
|
16 module hoofbaby.util.buffer;
|
|
17
|
|
18 import tango.time.StopWatch;
|
|
19 import tango.core.sync.Condition;
|
|
20 import tango.core.sync.Mutex;
|
|
21
|
|
22 version(Windows)
|
|
23 {
|
|
24 import tango.stdc.stringz;
|
|
25 import tango.text.convert.Format;
|
|
26 import tango.sys.win32.UserGdi;
|
|
27
|
|
28 import hoofbaby.util.exception;
|
|
29
|
|
30 private extern(Windows)
|
|
31 {
|
|
32 HANDLE CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCTSTR);
|
|
33 LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD);
|
|
34 LPVOID MapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, DWORD dwNumberOfBytesToMap, LPVOID lpBaseAddress);
|
|
35 BOOL UnmapViewOfFile(LPCVOID);
|
|
36 }
|
|
37
|
|
38 private uint bufNum = 0;
|
|
39
|
|
40 /**
|
|
41 * Allocates a buffer of the given size. ALl accesses up to buf + (size * 2)
|
|
42 * are valid; those over buf + size will reach around
|
|
43 *
|
|
44 * Params:
|
|
45 * size = The size of the buffer (must be multiple of page size; use large powers of two)
|
|
46 * Returns: A pointer to the beginning of that memory
|
|
47 */
|
|
48 private void* _allocBuffer(uint size)
|
|
49 {
|
|
50 // Ensure page file is the right size
|
|
51 SYSTEM_INFO info;
|
|
52 GetSystemInfo(&info);
|
|
53 uint pageSize = info.dwAllocationGranularity;
|
|
54 if(size % pageSize) // PERHAPS next multiple of instead of error?
|
|
55 throw new IllegalOperationException(Format("Memory map pagefile mismatch -- "
|
|
56 "requested {0}, but page size is {1}", size, pageSize));
|
|
57
|
|
58 // Try to allocate the buffer
|
|
59 HANDLE mmap = CreateFileMappingA(INVALID_HANDLE_VALUE, null, PAGE_READWRITE, 0, size, null);
|
|
60 if(!mmap) throw new UnrecoverableException("Could not create memory mapping for buffer");
|
|
61 void* buf = MapViewOfFile(mmap, FILE_MAP_ALL_ACCESS, 0, 0, size);
|
|
62 if(!buf) throw new UnrecoverableException("Could not map view of buffer");
|
|
63 void* buf2 = MapViewOfFileEx(mmap, FILE_MAP_ALL_ACCESS, 0, 0, size, buf + size);
|
|
64 if(!buf2) throw new UnrecoverableException("Could not map second view of buffer");
|
|
65 if(buf2 != buf + size) throw new UnrecoverableException("Memory mapped buffers are not contiguous");
|
|
66 return buf;
|
|
67 }
|
|
68
|
|
69 private void _freeBuffer(void* buffer, uint size)
|
|
70 {
|
|
71 UnmapViewOfFile(buffer);
|
|
72 UnmapViewOfFile(buffer + size);
|
|
73 }
|
|
74 }
|
|
75 else
|
|
76 {
|
|
77 static assert(false, "Unsupported operating system");
|
|
78 }
|
|
79
|
|
80 // IMPORTANT synchronization!!!!
|
|
81 public struct RingBuffer
|
|
82 {
|
|
83 private const double WAIT_TIME = 0.01; // 10 ms
|
|
84 private static Condition NULL_CONDITION;
|
|
85
|
|
86 private uint size;
|
|
87 private void* addr;
|
|
88
|
|
89 private uint readOfs;
|
|
90 private uint writeOfs;
|
|
91
|
|
92 /**
|
|
93 * The callback function to be called upon an overflow or underflow.
|
|
94 *
|
|
95 * Params:
|
|
96 * buffer = The RIngBuffer instance with the issue
|
|
97 * sizeRequested = the size the operation requested
|
|
98 * sizeAvailable = how much is actually there
|
|
99 * Returns: true if the function should return null, false if the buffer should still
|
|
100 * return the requested pointer (i.e. if the function waited until something
|
|
101 * was available)
|
|
102 */
|
|
103 public alias bool delegate(RingBuffer* buffer, uint sizeRequested, uint sizeAvialbale) BufferCallback;
|
|
104 public BufferCallback onUnderflow;
|
|
105 public BufferCallback onOverflow;
|
|
106
|
|
107 debug(AVBuffer)
|
|
108 {
|
|
109 private uint readRequested = 0;
|
|
110 private uint writeRequested = 0;
|
|
111 }
|
|
112
|
|
113 public static RingBuffer opCall(uint size)
|
|
114 {
|
|
115 // Not really a singleton; who cares if more than one is constructed?
|
|
116 if(!NULL_CONDITION)
|
|
117 NULL_CONDITION = new Condition(new Mutex());
|
|
118
|
|
119 RingBuffer r;
|
|
120 assert(size > 0);
|
|
121 r.size = size;
|
|
122 r.addr = _allocBuffer(size);
|
|
123 return r;
|
|
124 }
|
|
125
|
|
126 public void free()
|
|
127 {
|
|
128 _freeBuffer(addr, size);
|
|
129 addr = null;
|
|
130 }
|
|
131
|
|
132 public void* beginRead(uint requested)
|
|
133 {
|
|
134 debug(AVBuffer)
|
|
135 {
|
|
136 assert(readRequested == 0);
|
|
137 readRequested = requested;
|
|
138 }
|
|
139
|
|
140 if(requested > available)
|
|
141 {
|
|
142 if(!onUnderflow || onUnderflow(this, requested, available))
|
|
143 {
|
|
144 debug(AVBuffer) readRequested = 0;
|
|
145 return null;
|
|
146 }
|
|
147 }
|
|
148
|
|
149 return addr + readOfs;
|
|
150 }
|
|
151
|
|
152 public void endRead(uint bytes)
|
|
153 {
|
|
154 debug(AVBuffer)
|
|
155 {
|
|
156 assert(bytes == readRequested);
|
|
157 readRequested = 0;
|
|
158 }
|
|
159
|
|
160 readOfs += bytes;
|
|
161 if(readOfs > size)
|
|
162 {
|
|
163 readOfs -= bytes;
|
|
164 writeOfs -= bytes;
|
|
165 }
|
|
166 }
|
|
167
|
|
168 public void* beginWrite(uint requested)
|
|
169 {
|
|
170 debug(AVBuffer)
|
|
171 {
|
|
172 assert(writeRequested == 0);
|
|
173 writeRequested = requested;
|
|
174 }
|
|
175
|
|
176 if(requested > freeSpace)
|
|
177 {
|
|
178 if(!onOverflow || onOverflow(this, requested, available))
|
|
179 {
|
|
180 return null;
|
|
181 }
|
|
182 }
|
|
183
|
|
184 return addr + writeOfs;
|
|
185 }
|
|
186
|
|
187 public void endWrite(uint bytes)
|
|
188 {
|
|
189 debug(AVBuffer)
|
|
190 {
|
|
191 assert(bytes == writeRequested);
|
|
192 writeRequested = 0;
|
|
193 }
|
|
194
|
|
195 writeOfs += bytes;
|
|
196 }
|
|
197
|
|
198 public uint available()
|
|
199 {
|
|
200 return writeOfs - readOfs;
|
|
201 }
|
|
202
|
|
203 public uint freeSpace()
|
|
204 {
|
|
205 return size - available();
|
|
206 }
|
|
207
|
|
208 public uint totalSize()
|
|
209 {
|
|
210 return size;
|
|
211 }
|
|
212
|
|
213 public void clear()
|
|
214 {
|
|
215 readOfs = 0;
|
|
216 writeOfs = 0;
|
|
217 }
|
|
218
|
|
219 public void isEmpty()
|
|
220 {
|
|
221 return readOfs == writeOfs;
|
|
222 }
|
|
223
|
|
224 private bool _waitFor(char[] exp)(uint bytes, uint timeoutMs, Condition cond = null)
|
|
225 {
|
|
226 if(!cond)
|
|
227 cond = NULL_CONDITION;
|
|
228
|
|
229 ulong microsecs = timeoutMs * 1000;
|
|
230 StopWatch sw;
|
|
231 sw.start();
|
|
232 do
|
|
233 {
|
|
234 if(mixin(exp) >= bytes)
|
|
235 return true;
|
|
236 if(cond.wait(WAIT_TIME))
|
|
237 return false;
|
|
238 } while(sw.microsec() < microsecs);
|
|
239 return false;
|
|
240 }
|
|
241
|
|
242 public alias _waitFor!("available") waitAvailable;
|
|
243 public alias _waitFor!("freeSpace") waitFreeSpace;
|
|
244 } |