1/*============================================================================= 2 Copyright (c) 2003 Giovanni Bajo 3 Copyright (c) 2003 Martin Wille 4 Copyright (c) 2003 Hartmut Kaiser 5 http://spirit.sourceforge.net/ 6 7 Use, modification and distribution is subject to the Boost Software 8 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 9 http://www.boost.org/LICENSE_1_0.txt) 10=============================================================================*/ 11 12#ifndef BOOST_SPIRIT_FILE_ITERATOR_IPP 13#define BOOST_SPIRIT_FILE_ITERATOR_IPP 14 15#ifdef BOOST_SPIRIT_FILEITERATOR_WINDOWS 16# include <windows.h> 17#endif 18 19#include <cstdio> 20#include <boost/shared_ptr.hpp> 21 22#ifdef BOOST_SPIRIT_FILEITERATOR_WINDOWS 23# include <boost/type_traits/remove_pointer.hpp> 24#endif 25 26#ifdef BOOST_SPIRIT_FILEITERATOR_POSIX 27# include <sys/types.h> // open, stat, mmap, munmap 28# include <sys/stat.h> // stat 29# include <fcntl.h> // open 30# include <unistd.h> // stat, mmap, munmap 31# include <sys/mman.h> // mmap, mmunmap 32#endif 33 34/////////////////////////////////////////////////////////////////////////////// 35namespace boost { namespace spirit { 36 37BOOST_SPIRIT_CLASSIC_NAMESPACE_BEGIN 38 39/////////////////////////////////////////////////////////////////////////////// 40namespace fileiter_impl { 41 42/////////////////////////////////////////////////////////////////////////////// 43// 44// std_file_iterator 45// 46// Base class that implements iteration through a file using standard C 47// stream library (fopen and friends). This class and the following are 48// the base components on which the iterator is built (through the 49// iterator adaptor library). 50// 51// The opened file stream (FILE) is held with a shared_ptr<>, whose 52// custom deleter invokes fcose(). This makes the syntax of the class 53// very easy, especially everything related to copying. 54// 55/////////////////////////////////////////////////////////////////////////////// 56 57template <typename CharT> 58class std_file_iterator 59{ 60public: 61 typedef CharT value_type; 62 63 std_file_iterator() 64 {} 65 66 explicit std_file_iterator(std::string const& fileName) 67 { 68 using namespace std; 69 FILE* f = fopen(fileName.c_str(), "rb"); 70 71 // If the file was opened, store it into 72 // the smart pointer. 73 if (f) 74 { 75 m_file.reset(f, fclose); 76 m_pos = 0; 77 m_eof = false; 78 update_char(); 79 } 80 } 81 82 std_file_iterator(const std_file_iterator& iter) 83 { *this = iter; } 84 85 std_file_iterator& operator=(const std_file_iterator& iter) 86 { 87 m_file = iter.m_file; 88 m_curChar = iter.m_curChar; 89 m_eof = iter.m_eof; 90 m_pos = iter.m_pos; 91 92 return *this; 93 } 94 95 // Nasty bug in Comeau up to 4.3.0.1, we need explicit boolean context 96 // for shared_ptr to evaluate correctly 97 operator bool() const 98 { return m_file ? true : false; } 99 100 bool operator==(const std_file_iterator& iter) const 101 { 102 return (m_file == iter.m_file) && (m_eof == iter.m_eof) && 103 (m_pos == iter.m_pos); 104 } 105 106 const CharT& get_cur_char(void) const 107 { 108 return m_curChar; 109 } 110 111 void prev_char(void) 112 { 113 m_pos -= sizeof(CharT); 114 update_char(); 115 } 116 117 void next_char(void) 118 { 119 m_pos += sizeof(CharT); 120 update_char(); 121 } 122 123 void seek_end(void) 124 { 125 using namespace std; 126 fseek(m_file.get(), 0, SEEK_END); 127 m_pos = ftell(m_file.get()) / sizeof(CharT); 128 m_eof = true; 129 } 130 131 void advance(std::ptrdiff_t n) 132 { 133 m_pos += static_cast<long>(n) * sizeof(CharT); 134 update_char(); 135 } 136 137 std::ptrdiff_t distance(const std_file_iterator& iter) const 138 { 139 return (std::ptrdiff_t)(m_pos - iter.m_pos) / sizeof(CharT); 140 } 141 142private: 143 boost::shared_ptr<std::FILE> m_file; 144 long m_pos; 145 CharT m_curChar; 146 bool m_eof; 147 148 void update_char(void) 149 { 150 using namespace std; 151 if (ftell(m_file.get()) != m_pos) 152 fseek(m_file.get(), m_pos, SEEK_SET); 153 154 m_eof = (fread(&m_curChar, sizeof(CharT), 1, m_file.get()) < 1); 155 } 156}; 157 158 159/////////////////////////////////////////////////////////////////////////////// 160// 161// mmap_file_iterator 162// 163// File iterator for memory mapped files, for now implemented on Windows and 164// POSIX platforms. This class has the same interface of std_file_iterator, 165// and can be used in its place (in fact, it's the default for Windows and 166// POSIX). 167// 168/////////////////////////////////////////////////////////////////////////////// 169 170/////////////////////////////////////////////////////////////////////////////// 171// mmap_file_iterator, Windows version 172#ifdef BOOST_SPIRIT_FILEITERATOR_WINDOWS 173template <typename CharT> 174class mmap_file_iterator 175{ 176public: 177 typedef CharT value_type; 178 179 mmap_file_iterator() 180 : m_filesize(0), m_curChar(0) 181 {} 182 183 explicit mmap_file_iterator(std::string const& fileName) 184 : m_filesize(0), m_curChar(0) 185 { 186 HANDLE hFile = ::CreateFileA( 187 fileName.c_str(), 188 GENERIC_READ, 189 FILE_SHARE_READ, 190 NULL, 191 OPEN_EXISTING, 192 FILE_FLAG_SEQUENTIAL_SCAN, 193 NULL 194 ); 195 196 if (hFile == INVALID_HANDLE_VALUE) 197 return; 198 199 // Store the size of the file, it's used to construct 200 // the end iterator 201 m_filesize = ::GetFileSize(hFile, NULL); 202 203 HANDLE hMap = ::CreateFileMapping( 204 hFile, 205 NULL, 206 PAGE_READONLY, 207 0, 0, 208 NULL 209 ); 210 211 if (hMap == NULL) 212 { 213 ::CloseHandle(hFile); 214 return; 215 } 216 217 LPVOID pMem = ::MapViewOfFile( 218 hMap, 219 FILE_MAP_READ, 220 0, 0, 0 221 ); 222 223 if (pMem == NULL) 224 { 225 ::CloseHandle(hMap); 226 ::CloseHandle(hFile); 227 return; 228 } 229 230 // We hold both the file handle and the memory pointer. 231 // We can close the hMap handle now because Windows holds internally 232 // a reference to it since there is a view mapped. 233 ::CloseHandle(hMap); 234 235 // It seems like we can close the file handle as well (because 236 // a reference is hold by the filemap object). 237 ::CloseHandle(hFile); 238 239 // Store the handles inside the shared_ptr (with the custom destructors) 240 m_mem.reset(static_cast<CharT*>(pMem), ::UnmapViewOfFile); 241 242 // Start of the file 243 m_curChar = m_mem.get(); 244 } 245 246 mmap_file_iterator(const mmap_file_iterator& iter) 247 { *this = iter; } 248 249 mmap_file_iterator& operator=(const mmap_file_iterator& iter) 250 { 251 m_curChar = iter.m_curChar; 252 m_mem = iter.m_mem; 253 m_filesize = iter.m_filesize; 254 255 return *this; 256 } 257 258 // Nasty bug in Comeau up to 4.3.0.1, we need explicit boolean context 259 // for shared_ptr to evaluate correctly 260 operator bool() const 261 { return m_mem ? true : false; } 262 263 bool operator==(const mmap_file_iterator& iter) const 264 { return m_curChar == iter.m_curChar; } 265 266 const CharT& get_cur_char(void) const 267 { return *m_curChar; } 268 269 void next_char(void) 270 { m_curChar++; } 271 272 void prev_char(void) 273 { m_curChar--; } 274 275 void advance(std::ptrdiff_t n) 276 { m_curChar += n; } 277 278 std::ptrdiff_t distance(const mmap_file_iterator& iter) const 279 { return m_curChar - iter.m_curChar; } 280 281 void seek_end(void) 282 { 283 m_curChar = m_mem.get() + 284 (m_filesize / sizeof(CharT)); 285 } 286 287private: 288 typedef boost::remove_pointer<HANDLE>::type handle_t; 289 290 boost::shared_ptr<CharT> m_mem; 291 std::size_t m_filesize; 292 CharT* m_curChar; 293}; 294 295#endif // BOOST_SPIRIT_FILEITERATOR_WINDOWS 296 297/////////////////////////////////////////////////////////////////////////////// 298// mmap_file_iterator, POSIX version 299#ifdef BOOST_SPIRIT_FILEITERATOR_POSIX 300template <typename CharT> 301class mmap_file_iterator 302{ 303private: 304 struct mapping 305 { 306 mapping(void *p, off_t len) 307 : data(p) 308 , size(len) 309 { } 310 311 CharT const *begin() const 312 { 313 return static_cast<CharT *>(data); 314 } 315 316 CharT const *end() const 317 { 318 return static_cast<CharT *>(data) + size/sizeof(CharT); 319 } 320 321 ~mapping() 322 { 323 munmap(static_cast<char*>(data), size); 324 } 325 326 private: 327 void *data; 328 off_t size; 329 }; 330 331public: 332 typedef CharT value_type; 333 334 mmap_file_iterator() 335 : m_curChar(0) 336 {} 337 338 explicit mmap_file_iterator(std::string const& file_name) 339 : m_curChar(0) 340 { 341 // open the file 342 int fd = open(file_name.c_str(), 343#ifdef O_NOCTTY 344 O_NOCTTY | // if stdin was closed then opening a file 345 // would cause the file to become the controlling 346 // terminal if the filename refers to a tty. Setting 347 // O_NOCTTY inhibits this. 348#endif 349 O_RDONLY); 350 351 if (fd == -1) 352 return; 353 354 // call fstat to find get information about the file just 355 // opened (size and file type) 356 struct stat stat_buf; 357 if ((fstat(fd, &stat_buf) != 0) || !S_ISREG(stat_buf.st_mode)) 358 { // if fstat returns an error or if the file isn't a 359 // regular file we give up. 360 close(fd); 361 return; 362 } 363 364 // perform the actual mapping 365 void *p = mmap(0, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0); 366 // it is safe to close() here. POSIX requires that the OS keeps a 367 // second handle to the file while the file is mmapped. 368 close(fd); 369 370 if (p == MAP_FAILED) 371 return; 372 373 mapping *m = 0; 374 try 375 { 376 m = new mapping(p, stat_buf.st_size); 377 } 378 catch(...) 379 { 380 munmap(static_cast<char*>(p), stat_buf.st_size); 381 throw; 382 } 383 384 m_mem.reset(m); 385 386 // Start of the file 387 m_curChar = m_mem->begin(); 388 } 389 390 mmap_file_iterator(const mmap_file_iterator& iter) 391 { *this = iter; } 392 393 mmap_file_iterator& operator=(const mmap_file_iterator& iter) 394 { 395 m_curChar = iter.m_curChar; 396 m_mem = iter.m_mem; 397 398 return *this; 399 } 400 401 // Nasty bug in Comeau up to 4.3.0.1, we need explicit boolean context 402 // for shared_ptr to evaluate correctly 403 operator bool() const 404 { return m_mem ? true : false; } 405 406 bool operator==(const mmap_file_iterator& iter) const 407 { return m_curChar == iter.m_curChar; } 408 409 const CharT& get_cur_char(void) const 410 { return *m_curChar; } 411 412 void next_char(void) 413 { m_curChar++; } 414 415 void prev_char(void) 416 { m_curChar--; } 417 418 void advance(signed long n) 419 { m_curChar += n; } 420 421 long distance(const mmap_file_iterator& iter) const 422 { return m_curChar - iter.m_curChar; } 423 424 void seek_end(void) 425 { 426 m_curChar = m_mem->end(); 427 } 428 429private: 430 431 boost::shared_ptr<mapping> m_mem; 432 CharT const* m_curChar; 433}; 434 435#endif // BOOST_SPIRIT_FILEITERATOR_POSIX 436 437/////////////////////////////////////////////////////////////////////////////// 438} /* namespace boost::spirit::fileiter_impl */ 439 440template <typename CharT, typename BaseIteratorT> 441file_iterator<CharT,BaseIteratorT> 442file_iterator<CharT,BaseIteratorT>::make_end(void) 443{ 444 file_iterator iter(*this); 445 iter.base_reference().seek_end(); 446 return iter; 447} 448 449template <typename CharT, typename BaseIteratorT> 450file_iterator<CharT,BaseIteratorT>& 451file_iterator<CharT,BaseIteratorT>::operator=(const base_t& iter) 452{ 453 base_t::operator=(iter); 454 return *this; 455} 456 457/////////////////////////////////////////////////////////////////////////////// 458BOOST_SPIRIT_CLASSIC_NAMESPACE_END 459 460}} /* namespace boost::spirit */ 461 462 463#endif /* BOOST_SPIRIT_FILE_ITERATOR_IPP */ 464