132
|
1 /*******************************************************************************
|
|
2
|
|
3 copyright: Copyright (c) 2004 Kris Bell. All rights reserved
|
|
4
|
|
5 license: BSD style: $(LICENSE)
|
|
6
|
|
7 version: Initial release: March 2004
|
|
8
|
|
9 author: Kris
|
|
10
|
|
11 *******************************************************************************/
|
|
12
|
|
13 module tango.io.MappedBuffer;
|
|
14
|
|
15 private import tango.sys.Common;
|
|
16
|
|
17 private import tango.io.Buffer;
|
|
18
|
|
19 private import tango.core.Exception;
|
|
20
|
|
21 public import tango.io.FileConduit;
|
|
22
|
|
23 /*******************************************************************************
|
|
24
|
|
25 Win32 declarations
|
|
26
|
|
27 *******************************************************************************/
|
|
28
|
|
29 version (Win32)
|
|
30 private extern (Windows)
|
|
31 {
|
|
32 BOOL UnmapViewOfFile (LPCVOID);
|
|
33 BOOL FlushViewOfFile (LPCVOID, DWORD);
|
|
34 LPVOID MapViewOfFile (HANDLE, DWORD, DWORD, DWORD, DWORD);
|
|
35 HANDLE CreateFileMappingA (HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCTSTR);
|
|
36 }
|
|
37
|
|
38 version (Posix)
|
|
39 {
|
|
40 private import tango.stdc.posix.sys.mman;
|
|
41 }
|
|
42
|
|
43
|
|
44 /*******************************************************************************
|
|
45
|
|
46 Subclass to treat the buffer as a seekable entity, where all
|
|
47 capacity is available for reading and/or writing. To achieve
|
|
48 this we must effectively disable the 'limit' watermark, and
|
|
49 locate write operations around 'position' instead.
|
|
50
|
|
51 *******************************************************************************/
|
|
52
|
|
53 class MappedBuffer : Buffer, IConduit.Seek
|
|
54 {
|
|
55 private FileConduit host; // the hosting file
|
|
56
|
|
57 version (Win32)
|
|
58 {
|
|
59 private void* base; // array pointer
|
|
60 private HANDLE mmFile; // mapped file
|
|
61
|
|
62 /***************************************************************
|
|
63
|
|
64 Construct a MappedBuffer upon the given FileConduit.
|
|
65 One should set the file size using seek() & truncate()
|
|
66 to setup the available working space.
|
|
67
|
|
68 ***************************************************************/
|
|
69
|
|
70 this (FileConduit host)
|
|
71 {
|
|
72 super (0);
|
|
73
|
|
74 this.host = host;
|
|
75
|
|
76 // can only do 32bit mapping on 32bit platform
|
|
77 auto size = host.length;
|
|
78 assert (size <= uint.max);
|
|
79
|
|
80 auto access = host.style.access;
|
|
81
|
|
82 DWORD flags = PAGE_READONLY;
|
|
83 if (access & host.Access.Write)
|
|
84 flags = PAGE_READWRITE;
|
|
85
|
|
86 auto handle = cast(HANDLE) host.fileHandle;
|
|
87 mmFile = CreateFileMappingA (handle, null, flags, 0, 0, null);
|
|
88 if (mmFile is null)
|
|
89 host.error ();
|
|
90
|
|
91 flags = FILE_MAP_READ;
|
|
92 if (access & host.Access.Write)
|
|
93 flags |= FILE_MAP_WRITE;
|
|
94
|
|
95 base = MapViewOfFile (mmFile, flags, 0, 0, 0);
|
|
96 if (base is null)
|
|
97 host.error;
|
|
98
|
|
99 void[] mem = base [0 .. cast(int) size];
|
|
100 setContent (mem);
|
|
101 }
|
|
102
|
|
103 /***************************************************************
|
|
104
|
|
105 Release this mapped buffer without flushing
|
|
106
|
|
107 ***************************************************************/
|
|
108
|
|
109 override void close ()
|
|
110 {
|
|
111 if (base)
|
|
112 UnmapViewOfFile (base);
|
|
113
|
|
114 if (mmFile)
|
|
115 CloseHandle (mmFile);
|
|
116
|
|
117 mmFile = null;
|
|
118 base = null;
|
|
119 }
|
|
120
|
|
121 /***************************************************************
|
|
122
|
|
123 Flush dirty content out to the drive. This
|
|
124 fails with error 33 if the file content is
|
|
125 virgin. Opening a file for ReadWriteExists
|
|
126 followed by a flush() will cause this.
|
|
127
|
|
128 ***************************************************************/
|
|
129
|
|
130 override OutputStream flush ()
|
|
131 {
|
|
132 // flush all dirty pages
|
|
133 if (! FlushViewOfFile (base, 0))
|
|
134 host.error ();
|
|
135 return this;
|
|
136 }
|
|
137 }
|
|
138
|
|
139 /***********************************************************************
|
|
140
|
|
141 ***********************************************************************/
|
|
142
|
|
143 version (Posix)
|
|
144 {
|
|
145 // Linux code: not yet tested on other POSIX systems.
|
|
146 private void* base; // array pointer
|
|
147 private ulong size; // length of file
|
|
148
|
|
149 this (FileConduit host)
|
|
150 {
|
|
151 super(0);
|
|
152
|
|
153 this.host = host;
|
|
154 size = host.length;
|
|
155
|
|
156 // Make sure the mapping attributes are consistant with
|
|
157 // the FileConduit attributes.
|
|
158
|
|
159 auto access = host.style.access;
|
|
160
|
|
161 int flags = MAP_SHARED;
|
|
162 int protection = PROT_READ;
|
|
163
|
|
164 if (access & host.Access.Write)
|
|
165 protection |= PROT_WRITE;
|
|
166
|
|
167 base = mmap (null, size, protection, flags, host.fileHandle(), 0);
|
|
168 if (base is null)
|
|
169 host.error();
|
|
170
|
|
171 void[] mem = base [0 .. cast(int) size];
|
|
172 setContent (mem);
|
|
173 }
|
|
174
|
|
175 /***************************************************************
|
|
176
|
|
177 Release this mapped buffer without flushing
|
|
178
|
|
179 ***************************************************************/
|
|
180
|
|
181 override void close ()
|
|
182 {
|
|
183 // NOTE: When a process ends, all mmaps belonging to that process
|
|
184 // are automatically unmapped by system (Linux).
|
|
185 // On the other hand, this is NOT the case when the related
|
|
186 // file descriptor is closed. This function unmaps explicitly.
|
|
187
|
|
188 if (base)
|
|
189 if (munmap (base, size))
|
|
190 host.error();
|
|
191 base = null;
|
|
192 }
|
|
193
|
|
194 /***************************************************************
|
|
195
|
|
196 Flush dirty content out to the drive.
|
|
197
|
|
198 ***************************************************************/
|
|
199
|
|
200 override OutputStream flush ()
|
|
201 {
|
|
202 // MS_ASYNC: delayed flush; equivalent to "add-to-queue"
|
|
203 // MS_SYNC: function flushes file immediately; no return until flush complete
|
|
204 // MS_INVALIDATE: invalidate all mappings of the same file (shared)
|
|
205
|
|
206 if (msync (base, size, MS_SYNC | MS_INVALIDATE))
|
|
207 host.error();
|
|
208 return this;
|
|
209 }
|
|
210 }
|
|
211
|
|
212 /***********************************************************************
|
|
213
|
|
214 Seek to the specified position within the buffer, and return
|
|
215 the byte offset of the new location (relative to zero).
|
|
216
|
|
217 ***********************************************************************/
|
|
218
|
|
219 long seek (long offset, Anchor anchor = Anchor.Begin)
|
|
220 {
|
|
221 uint pos = dimension;
|
|
222
|
|
223 if (anchor is Anchor.Begin)
|
|
224 pos = cast(uint) offset;
|
|
225 else
|
|
226 if (anchor is Anchor.End)
|
|
227 pos -= cast(uint) offset;
|
|
228 else
|
|
229 pos = index + cast(uint) offset;
|
|
230
|
|
231 return index = pos;
|
|
232 }
|
|
233
|
|
234 /***********************************************************************
|
|
235
|
|
236 Return count of writable bytes available in buffer. This is
|
|
237 calculated simply as capacity() - limit()
|
|
238
|
|
239 ***********************************************************************/
|
|
240
|
|
241 override uint writable ()
|
|
242 {
|
|
243 return dimension - index;
|
|
244 }
|
|
245
|
|
246 /***********************************************************************
|
|
247
|
|
248 Bulk copy of data from 'src'. Position is adjusted by 'size'
|
|
249 bytes.
|
|
250
|
|
251 ***********************************************************************/
|
|
252
|
|
253 override protected void copy (void *src, uint size)
|
|
254 {
|
|
255 // avoid "out of bounds" test on zero size
|
|
256 if (size)
|
|
257 {
|
|
258 // content may overlap ...
|
|
259 memcpy (&data[index], src, size);
|
|
260 index += size;
|
|
261 }
|
|
262 }
|
|
263
|
|
264 /***********************************************************************
|
|
265
|
|
266 Exposes the raw data buffer at the current write position,
|
|
267 The delegate is provided with a void[] representing space
|
|
268 available within the buffer at the current write position.
|
|
269
|
|
270 The delegate should return the appropriate number of bytes
|
|
271 if it writes valid content, or IConduit.Eof on error.
|
|
272
|
|
273 Returns whatever the delegate returns.
|
|
274
|
|
275 ***********************************************************************/
|
|
276
|
|
277 override uint write (uint delegate (void[]) dg)
|
|
278 {
|
|
279 int count = dg (data [index .. dimension]);
|
|
280
|
|
281 if (count != IConduit.Eof)
|
|
282 {
|
|
283 index += count;
|
|
284 assert (index <= dimension);
|
|
285 }
|
|
286 return count;
|
|
287 }
|
|
288
|
|
289 /***********************************************************************
|
|
290
|
|
291 Prohibit compress() from doing anything at all.
|
|
292
|
|
293 ***********************************************************************/
|
|
294
|
|
295 override IBuffer compress ()
|
|
296 {
|
|
297 return this;
|
|
298 }
|
|
299
|
|
300 /***********************************************************************
|
|
301
|
|
302 Prohibit clear() from doing anything at all.
|
|
303
|
|
304 ***********************************************************************/
|
|
305
|
|
306 override InputStream clear ()
|
|
307 {
|
|
308 return this;
|
|
309 }
|
|
310
|
|
311 /***********************************************************************
|
|
312
|
|
313 Prohibit the setting of another IConduit
|
|
314
|
|
315 ***********************************************************************/
|
|
316
|
|
317 override IBuffer setConduit (IConduit conduit)
|
|
318 {
|
|
319 error ("cannot setConduit on memory-mapped buffer");
|
|
320 return null;
|
|
321 }
|
|
322 }
|
|
323
|
|
324
|
|
325 debug (MappedBuffer)
|
|
326 {
|
|
327 void main()
|
|
328 {
|
|
329 auto x = new MappedBuffer(null);
|
|
330 }
|
|
331 }
|