file_iterator.ipp 12 KB

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