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