1 /*******************************************************************************
2 
3         Copyright:
4             Copyright (c) 2004 Kris Bell.
5             Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
6             All rights reserved.
7 
8         License:
9             Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
10             See LICENSE_TANGO.txt for details.
11 
12         Version: Initial release: March 2004
13 
14         Authors: Kris
15 
16 *******************************************************************************/
17 
18 module ocean.io.device.FileMap;
19 
20 import ocean.io.device.Array;
21 import ocean.io.device.File;
22 import ocean.meta.types.Qualifiers;
23 import ocean.sys.Common;
24 
25 import core.sys.posix.sys.mman;
26 
27 
28 /*******************************************************************************
29 
30 *******************************************************************************/
31 
32 class FileMap : Array
33 {
34         private MappedFile file;
35 
36         /***********************************************************************
37 
38                 Construct a FileMap upon the given path.
39 
40                 You should use resize() to setup the available
41                 working space.
42 
43         ***********************************************************************/
44 
45         this (cstring path, File.Style style = File.ReadWriteOpen)
46         {
47                 file = new MappedFile (path, style);
48                 super (file.map);
49         }
50 
51         /***********************************************************************
52 
53                 Resize the file and return the remapped content. Usage of
54                 map() is not required following this call.
55 
56         ***********************************************************************/
57 
58         final ubyte[] resize (long size)
59         {
60                 auto ret = file.resize (size);
61                 super.assign (ret);
62                 return ret;
63         }
64 
65         /***********************************************************************
66 
67                 Release external resources.
68 
69         ***********************************************************************/
70 
71         override void close ()
72         {
73                 super.close;
74                 if (file)
75                     file.close;
76                 file = null;
77         }
78 }
79 
80 
81 /*******************************************************************************
82 
83 *******************************************************************************/
84 
85 class MappedFile
86 {
87         private File host;
88 
89         /***********************************************************************
90 
91                 Construct a FileMap upon the given path.
92 
93                 You should use resize() to setup the available
94                 working space.
95 
96         ***********************************************************************/
97 
98         this (cstring path, File.Style style = File.ReadWriteOpen)
99         {
100                 host = new File (path, style);
101         }
102 
103         /***********************************************************************
104 
105         ***********************************************************************/
106 
107         final long length ()
108         {
109                 return host.length;
110         }
111 
112         /***********************************************************************
113 
114         ***********************************************************************/
115 
116         final istring path ()
117         {
118                 return host.toString;
119         }
120 
121         /***********************************************************************
122 
123                 Resize the file and return the remapped content. Usage of
124                 map() is not required following this call.
125 
126         ***********************************************************************/
127 
128         final ubyte[] resize (long size)
129         {
130                 host.truncate (size);
131                 return map;
132         }
133 
134     private void*   base;           // array pointer
135     private size_t  size;           // length of file
136 
137     /***************************************************************************
138 
139         Return a slice representing file content as a memory-mapped array
140 
141         Use this to remap content each time the file size is changed.
142 
143     ***************************************************************************/
144 
145     final ubyte[] map ()
146     {
147         // be wary of redundant references
148         if (base)
149             reset;
150 
151         // can only do 32bit mapping on 32bit platform
152         size = cast (size_t) host.length;
153 
154         // Make sure the mapping attributes are consistant with
155         // the File attributes.
156         int flags = MAP_SHARED;
157         int protection = PROT_READ;
158         auto access = host.style.access;
159         if (access & host.Access.Write)
160             protection |= PROT_WRITE;
161 
162         base = mmap (null, size, protection, flags, host.fileHandle, 0);
163         if (base is MAP_FAILED)
164         {
165             base = null;
166             host.error;
167         }
168 
169         return (cast(ubyte*) base) [0 .. size];
170     }
171 
172     /***************************************************************************
173 
174         Release this mapped buffer without flushing.
175 
176     ***************************************************************************/
177 
178     final void close ()
179     {
180         reset;
181         if (host)
182             host.close;
183         host = null;
184     }
185 
186     ///
187     private void reset ()
188     {
189         // NOTE: When a process ends, all mmaps belonging to that process
190         //       are automatically unmapped by system (Linux).
191         //       On the other hand, this is NOT the case when the related
192         //       file descriptor is closed.  This function unmaps explicitly.
193         if (base)
194             if (munmap (base, size))
195                 host.error;
196 
197         base = null;
198     }
199 
200     /***************************************************************************
201 
202         Flush dirty content out to the drive.
203 
204     ***************************************************************************/
205 
206     final MappedFile flush ()
207     {
208         // MS_ASYNC: delayed flush; equivalent to "add-to-queue"
209         // MS_SYNC: function flushes file immediately; no return until flush complete
210         // MS_INVALIDATE: invalidate all mappings of the same file (shared)
211 
212         if (msync (base, size, MS_SYNC | MS_INVALIDATE))
213             host.error;
214         return this;
215     }
216 }
217 
218 ///
219 unittest
220 {
221     void example ( )
222     {
223         auto file = new MappedFile ("foo.map");
224         auto heap = file.resize (1_000_000);
225         file.close();
226 
227         auto file1 = new MappedFile ("foo1.map");
228         auto heap1 = file1.resize (1_000_000);
229         file1.close();
230     }
231 }