CASToR  2.0
Tomographic Reconstruction (PET/SPECT/CT)
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
oMemoryMapped.cc
Go to the documentation of this file.
1 /*
2 This file is part of CASToR.
3 
4  CASToR is free software: you can redistribute it and/or modify it under the
5  terms of the GNU General Public License as published by the Free Software
6  Foundation, either version 3 of the License, or (at your option) any later
7  version.
8 
9  CASToR is distributed in the hope that it will be useful, but WITHOUT ANY
10  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12  details.
13 
14  You should have received a copy of the GNU General Public License along with
15  CASToR (in file GNU_GPL.TXT). If not, see <http://www.gnu.org/licenses/>.
16 
17 Copyright 2017-2018 all CASToR contributors listed below:
18 
19  --> current contributors: Thibaut MERLIN, Simon STUTE, Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Mael MILLARDET
20  --> past contributors: Valentin VIELZEUF
21 
22 This is CASToR version 2.0.
23 */
24 
36 #include "oMemoryMapped.hh"
37 
38 #include <stdexcept>
39 #include <cstdio>
40 #include <iostream>
41 
42 // OS-specific
43 #if defined(_WIN32) || defined(CASTOR_USE_MINGW)
44 // Windows
45 #include <windows.h>
46 #else
47 // Linux
48 // enable large file support on 32 bit systems
49 #ifndef _LARGEFILE64_SOURCE
50 #define _LARGEFILE64_SOURCE
51 #endif
52 #ifdef _FILE_OFFSET_BITS
53 #undef _FILE_OFFSET_BITS
54 #endif
55 #define _FILE_OFFSET_BITS 64
56 // and include needed headers
57 #include <sys/stat.h>
58 #include <sys/mman.h>
59 #include <fcntl.h>
60 #include <errno.h>
61 #include <unistd.h>
62 #endif
63 
64 // =====================================================================
65 // ---------------------------------------------------------------------
66 // ---------------------------------------------------------------------
67 // =====================================================================
68 
71 : _filename (),
72  _filesize (0),
73  _hint (Normal),
74  _mappedBytes(0),
75 #if defined(_WIN32) || defined(CASTOR_USE_MINGW)
76  _mappedFile (NULL),
77 #endif
78  _file (0),
79  _mappedView (NULL)
80 {
81 }
82 
83 // =====================================================================
84 // ---------------------------------------------------------------------
85 // ---------------------------------------------------------------------
86 // =====================================================================
87 
89 oMemoryMapped::oMemoryMapped(const std::string& filename, size_t mappedBytes, CacheHint hint)
90 : _filename (filename),
91  _filesize (0),
92  _hint (hint),
93  _mappedBytes(mappedBytes),
94 #if defined(_WIN32) || defined(CASTOR_USE_MINGW)
95  _mappedFile (NULL),
96 #endif
97  _file (0),
98  _mappedView (NULL)
99 {
100  Open(filename, mappedBytes, hint);
101 }
102 
103 // =====================================================================
104 // ---------------------------------------------------------------------
105 // ---------------------------------------------------------------------
106 // =====================================================================
107 
110 {
111  Close();
112 }
113 
114 // =====================================================================
115 // ---------------------------------------------------------------------
116 // ---------------------------------------------------------------------
117 // =====================================================================
118 
120 int oMemoryMapped::Open(const std::string& filename, size_t mappedBytes, CacheHint hint)
121 {
122  // already open ?
123  if (IsValid())
124  {
125  Cerr("***** oMemoryMapped::Open() -> File is already open !" << endl);
126  return 1;
127  }
128 
129  _file = 0;
130  _filesize = 0;
131  _hint = hint;
132 #if defined(_WIN32) || defined(CASTOR_USE_MINGW)
133  _mappedFile = NULL;
134 #endif
135  _mappedView = NULL;
136 
137 #if defined(_WIN32) || defined(CASTOR_USE_MINGW)
138 
139  // ===================================================================
140  // Windows
141  // ===================================================================
142 
143  DWORD winHint = 0;
144  switch (_hint)
145  {
146  case Normal: winHint = FILE_ATTRIBUTE_NORMAL; break;
147  case SequentialScan: winHint = FILE_FLAG_SEQUENTIAL_SCAN; break;
148  case RandomAccess: winHint = FILE_FLAG_RANDOM_ACCESS; break;
149  default: break;
150  }
151 
152  // open file
153  _file = ::CreateFileA(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, winHint, NULL);
154  if (!_file)
155  {
156  Cerr("***** oMemoryMapped::Open() -> Failed to create windows file from function CreateFileA() !" << endl);
157  return 1;
158  }
159 
160  // file size
161  LARGE_INTEGER result;
162  if (!GetFileSizeEx(_file, &result))
163  {
164  Cerr("***** oMemoryMapped::Open() -> Failed to get file size from windows function GetFileSizeEx() !" << endl);
165  return 1;
166  }
167  _filesize = static_cast<uint64_t>(result.QuadPart);
168 
169  // convert to mapped mode
170  _mappedFile = ::CreateFileMapping(_file, NULL, PAGE_READONLY, 0, 0, NULL);
171  if (!_mappedFile)
172  {
173  Cerr("***** oMemoryMapped::Open() -> Failed to convert file to mapped mode from windows function CreateFileMapping() !" << endl);
174  return 1;
175  }
176 
177 #else
178 
179  // ===================================================================
180  // Linux
181  // ===================================================================
182 
183  // open file
184  _file = ::open(filename.c_str(), O_RDONLY | O_LARGEFILE);
185  if (_file == -1)
186  {
187  _file = 0;
188  Cerr("***** oMemoryMapped::Open() -> Failed to open file from unix function open() !" << endl);
189  return 1;
190  }
191 
192  // file size
193  struct stat64 statInfo;
194  if (fstat64(_file, &statInfo) < 0)
195  {
196  Cerr("***** oMemoryMapped::Open() -> Failed to get correct file size from unix function fstat64() !" << endl);
197  return 1;
198  }
199 
200  _filesize = statInfo.st_size;
201 #endif
202 
203  // initial mapping
204  if (Remap(0, mappedBytes))
205  {
206  Cerr("***** oMemoryMapped::Open() -> A problem occured while calling the oMemoryMapped::Remap() function !" << endl);
207  return 1;
208  }
209 
210  // check
211  if (!_mappedView)
212  {
213  Cerr("***** oMemoryMapped::Open() -> Failed to get a correct mapped view after calling the oMemoryMapped::Remap() function !" << endl);
214  return 1;
215  }
216 
217  // everything's fine
218  return 0;
219 }
220 
221 // =====================================================================
222 // ---------------------------------------------------------------------
223 // ---------------------------------------------------------------------
224 // =====================================================================
225 
228 {
229  // kill pointer
230  if (_mappedView)
231  {
232 #if defined(_WIN32) || defined(CASTOR_USE_MINGW)
233  ::UnmapViewOfFile(_mappedView);
234 #else
235  ::munmap(_mappedView, _filesize);
236 #endif
237  _mappedView = NULL;
238  }
239 
240 #if defined(_WIN32) || defined(CASTOR_USE_MINGW)
241  if (_mappedFile)
242  {
243  ::CloseHandle(_mappedFile);
244  _mappedFile = NULL;
245  }
246 #endif
247 
248  // close underlying file
249  if (_file)
250  {
251 #if defined(_WIN32) || defined(CASTOR_USE_MINGW)
252  ::CloseHandle(_file);
253 #else
254  ::close(_file);
255 #endif
256  _file = 0;
257  }
258 
259  _filesize = 0;
260 }
261 
262 // =====================================================================
263 // ---------------------------------------------------------------------
264 // ---------------------------------------------------------------------
265 // =====================================================================
266 
268 unsigned char oMemoryMapped::operator[](size_t offset) const
269 {
270  return ((unsigned char*)_mappedView)[offset];
271 }
272 
273 // =====================================================================
274 // ---------------------------------------------------------------------
275 // ---------------------------------------------------------------------
276 // =====================================================================
277 
279 unsigned char oMemoryMapped::at(size_t offset) const
280 {
281  // checks
282  if (!_mappedView)
283  throw std::invalid_argument("No view mapped");
284  if (offset >= _filesize)
285  throw std::out_of_range("View is not large enough");
286 
287  return operator[](offset);
288 }
289 
290 // =====================================================================
291 // ---------------------------------------------------------------------
292 // ---------------------------------------------------------------------
293 // =====================================================================
294 
296 const unsigned char* oMemoryMapped::GetData() const
297 {
298  return (const unsigned char*)_mappedView;
299 }
300 
301 // =====================================================================
302 // ---------------------------------------------------------------------
303 // ---------------------------------------------------------------------
304 // =====================================================================
305 
308 {
309  return _mappedView != NULL;
310 }
311 
312 // =====================================================================
313 // ---------------------------------------------------------------------
314 // ---------------------------------------------------------------------
315 // =====================================================================
316 
318 uint64_t oMemoryMapped::size() const
319 {
320  return _filesize;
321 }
322 
323 // =====================================================================
324 // ---------------------------------------------------------------------
325 // ---------------------------------------------------------------------
326 // =====================================================================
327 
330 {
331  return _mappedBytes;
332 }
333 
334 // =====================================================================
335 // ---------------------------------------------------------------------
336 // ---------------------------------------------------------------------
337 // =====================================================================
338 
340 int oMemoryMapped::Remap(uint64_t offset, size_t mappedBytes)
341 {
342  if (!_file)
343  {
344  Cerr("***** oMemoryMapped::Remap() -> Cannot remap a file that has not been created !" << endl);
345  return 1;
346  }
347 
348  if (mappedBytes == WholeFile)
349  mappedBytes = _filesize;
350 
351  // close old mapping
352  if (_mappedView)
353  {
354 #if defined(_WIN32) || defined(CASTOR_USE_MINGW)
355  ::UnmapViewOfFile(_mappedView);
356 #else
357  ::munmap(_mappedView, _mappedBytes);
358 #endif
359  _mappedView = NULL;
360  }
361 
362  // don't go further than end of file
363  if (offset > _filesize)
364  {
365  Cerr("***** oMemoryMapped::Remap() -> Provided offset is after the end of file !" << endl);
366  return 1;
367  }
368  if (offset + mappedBytes > _filesize)
369  mappedBytes = size_t(_filesize - offset);
370 
371 #if defined(_WIN32) || defined(CASTOR_USE_MINGW)
372 
373  // ===================================================================
374  // Windows
375  // ===================================================================
376 
377  DWORD offsetLow = DWORD(offset & 0xFFFFFFFF);
378  DWORD offsetHigh = DWORD(offset >> 32);
379  _mappedBytes = mappedBytes;
380 
381  // get memory address
382  _mappedView = ::MapViewOfFile(_mappedFile, FILE_MAP_READ, offsetHigh, offsetLow, mappedBytes);
383 
384  if (_mappedView == NULL)
385  {
386  _mappedBytes = 0;
387  _mappedView = NULL;
388  Cerr("***** oMemoryMapped::Remap() -> Mapped view is null after calling windows function MapViewOfFile() !" << endl);
389  return 1;
390  }
391 
392 #else
393 
394  // ===================================================================
395  // Linux
396  // ===================================================================
397 
398  // new mapping
399  _mappedView = ::mmap64(NULL, mappedBytes, PROT_READ, MAP_SHARED, _file, offset);
400  if (_mappedView == MAP_FAILED)
401  {
402  _mappedBytes = 0;
403  _mappedView = NULL;
404  Cerr("***** oMemoryMapped::Remap() -> Mapping failed after calling the unix function mmap64() !" << endl);
405  return 1;
406  }
407 
408  _mappedBytes = mappedBytes;
409 
410  // tweak performance
411  int linuxHint = 0;
412  switch (_hint)
413  {
414  case Normal: linuxHint = MADV_NORMAL; break;
415  case SequentialScan: linuxHint = MADV_SEQUENTIAL; break;
416  case RandomAccess: linuxHint = MADV_RANDOM; break;
417  default: break;
418  }
419  // assume that file will be accessed soon
420  //linuxHint |= MADV_WILLNEED;
421  // assume that file will be large
422  //linuxHint |= MADV_HUGEPAGE;
423 // linuxHint [= MADV_NOHUGEPAGE;
424 
425  ::madvise(_mappedView, _mappedBytes, linuxHint);
426 
427 #endif
428 
429  // end
430  return 0;
431 }
432 
433 // =====================================================================
434 // ---------------------------------------------------------------------
435 // ---------------------------------------------------------------------
436 // =====================================================================
437 
440 {
441 #if defined(_WIN32) || defined(CASTOR_USE_MINGW)
442  SYSTEM_INFO sysInfo;
443  GetSystemInfo(&sysInfo);
444  return sysInfo.dwAllocationGranularity;
445 #else
446  return sysconf(_SC_PAGESIZE); //::getpagesize();
447 #endif
448 }
449 
450 // =====================================================================
451 // ---------------------------------------------------------------------
452 // ---------------------------------------------------------------------
453 // =====================================================================
int Remap(uint64_t offset, size_t mappedBytes)
replace mapping by a new one of the same file, offset MUST be a multiple of the page size ...
bool IsValid() const
true, if file successfully opened
unsigned char at(size_t offset) const
access position, including range checking
void * _mappedView
pointer to the file contents mapped into memory
int Open(const std::string &filename, size_t mappedBytes=WholeFile, CacheHint hint=Normal)
open file, mappedBytes = 0 maps the whole file
oMemoryMapped()
do nothing, must use open()
#define Cerr(MESSAGE)
CacheHint
tweak performance
unsigned char operator[](size_t offset) const
access position, no range checking (faster)
everything ... be careful when file is larger than memory
read file only once with few seeks
uint64_t _filesize
file size
size_t mappedSize() const
get number of actually mapped bytes
void Close()
close file
const unsigned char * GetData() const
raw access
static int GetPageSize()
get OS page size (for remap)
Implementation of file to memory mapping.
~oMemoryMapped()
close file (see close() )
size_t _mappedBytes
mapped size
uint64_t size() const
get file size
FileHandle _file
file handle
CacheHint _hint
caching strategy
good overall performance