/*============================================================================= | |
Copyright (c) 2003 Giovanni Bajo | |
Copyright (c) 2003 Martin Wille | |
Copyright (c) 2003 Hartmut Kaiser | |
http://spirit.sourceforge.net/ | |
Use, modification and distribution is subject to the Boost Software | |
License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at | |
http://www.boost.org/LICENSE_1_0.txt) | |
=============================================================================*/ | |
#ifndef BOOST_SPIRIT_FILE_ITERATOR_IPP | |
#define BOOST_SPIRIT_FILE_ITERATOR_IPP | |
#ifdef BOOST_SPIRIT_FILEITERATOR_WINDOWS | |
# include <windows.h> | |
#endif | |
#include <cstdio> | |
#include <boost/shared_ptr.hpp> | |
#ifdef BOOST_SPIRIT_FILEITERATOR_WINDOWS | |
# include <boost/type_traits/remove_pointer.hpp> | |
#endif | |
#ifdef BOOST_SPIRIT_FILEITERATOR_POSIX | |
# include <sys/types.h> // open, stat, mmap, munmap | |
# include <sys/stat.h> // stat | |
# include <fcntl.h> // open | |
# include <unistd.h> // stat, mmap, munmap | |
# include <sys/mman.h> // mmap, mmunmap | |
#endif | |
/////////////////////////////////////////////////////////////////////////////// | |
namespace boost { namespace spirit { | |
BOOST_SPIRIT_CLASSIC_NAMESPACE_BEGIN | |
/////////////////////////////////////////////////////////////////////////////// | |
namespace fileiter_impl { | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// std_file_iterator | |
// | |
// Base class that implements iteration through a file using standard C | |
// stream library (fopen and friends). This class and the following are | |
// the base components on which the iterator is built (through the | |
// iterator adaptor library). | |
// | |
// The opened file stream (FILE) is held with a shared_ptr<>, whose | |
// custom deleter invokes fcose(). This makes the syntax of the class | |
// very easy, especially everything related to copying. | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
template <typename CharT> | |
class std_file_iterator | |
{ | |
public: | |
typedef CharT value_type; | |
std_file_iterator() | |
{} | |
explicit std_file_iterator(std::string fileName) | |
{ | |
using namespace std; | |
FILE* f = fopen(fileName.c_str(), "rb"); | |
// If the file was opened, store it into | |
// the smart pointer. | |
if (f) | |
{ | |
m_file.reset(f, fclose); | |
m_pos = 0; | |
m_eof = false; | |
update_char(); | |
} | |
} | |
std_file_iterator(const std_file_iterator& iter) | |
{ *this = iter; } | |
std_file_iterator& operator=(const std_file_iterator& iter) | |
{ | |
m_file = iter.m_file; | |
m_curChar = iter.m_curChar; | |
m_eof = iter.m_eof; | |
m_pos = iter.m_pos; | |
return *this; | |
} | |
// Nasty bug in Comeau up to 4.3.0.1, we need explicit boolean context | |
// for shared_ptr to evaluate correctly | |
operator bool() const | |
{ return m_file ? true : false; } | |
bool operator==(const std_file_iterator& iter) const | |
{ | |
return (m_file == iter.m_file) && (m_eof == iter.m_eof) && | |
(m_pos == iter.m_pos); | |
} | |
const CharT& get_cur_char(void) const | |
{ | |
return m_curChar; | |
} | |
void prev_char(void) | |
{ | |
m_pos -= sizeof(CharT); | |
update_char(); | |
} | |
void next_char(void) | |
{ | |
m_pos += sizeof(CharT); | |
update_char(); | |
} | |
void seek_end(void) | |
{ | |
using namespace std; | |
fseek(m_file.get(), 0, SEEK_END); | |
m_pos = ftell(m_file.get()) / sizeof(CharT); | |
m_eof = true; | |
} | |
void advance(std::ptrdiff_t n) | |
{ | |
m_pos += n * sizeof(CharT); | |
update_char(); | |
} | |
std::ptrdiff_t distance(const std_file_iterator& iter) const | |
{ | |
return (std::ptrdiff_t)(m_pos - iter.m_pos) / sizeof(CharT); | |
} | |
private: | |
boost::shared_ptr<std::FILE> m_file; | |
std::size_t m_pos; | |
CharT m_curChar; | |
bool m_eof; | |
void update_char(void) | |
{ | |
using namespace std; | |
if ((std::size_t)ftell(m_file.get()) != m_pos) | |
fseek(m_file.get(), m_pos, SEEK_SET); | |
m_eof = (fread(&m_curChar, sizeof(CharT), 1, m_file.get()) < 1); | |
} | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// mmap_file_iterator | |
// | |
// File iterator for memory mapped files, for now implemented on Windows and | |
// POSIX platforms. This class has the same interface of std_file_iterator, | |
// and can be used in its place (in fact, it's the default for Windows and | |
// POSIX). | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
/////////////////////////////////////////////////////////////////////////////// | |
// mmap_file_iterator, Windows version | |
#ifdef BOOST_SPIRIT_FILEITERATOR_WINDOWS | |
template <typename CharT> | |
class mmap_file_iterator | |
{ | |
public: | |
typedef CharT value_type; | |
mmap_file_iterator() | |
{} | |
explicit mmap_file_iterator(std::string fileName) | |
{ | |
HANDLE hFile = ::CreateFileA( | |
fileName.c_str(), | |
GENERIC_READ, | |
FILE_SHARE_READ, | |
NULL, | |
OPEN_EXISTING, | |
FILE_FLAG_SEQUENTIAL_SCAN, | |
NULL | |
); | |
if (hFile == INVALID_HANDLE_VALUE) | |
return; | |
// Store the size of the file, it's used to construct | |
// the end iterator | |
m_filesize = ::GetFileSize(hFile, NULL); | |
HANDLE hMap = ::CreateFileMapping( | |
hFile, | |
NULL, | |
PAGE_READONLY, | |
0, 0, | |
NULL | |
); | |
if (hMap == NULL) | |
{ | |
::CloseHandle(hFile); | |
return; | |
} | |
LPVOID pMem = ::MapViewOfFile( | |
hMap, | |
FILE_MAP_READ, | |
0, 0, 0 | |
); | |
if (pMem == NULL) | |
{ | |
::CloseHandle(hMap); | |
::CloseHandle(hFile); | |
return; | |
} | |
// We hold both the file handle and the memory pointer. | |
// We can close the hMap handle now because Windows holds internally | |
// a reference to it since there is a view mapped. | |
::CloseHandle(hMap); | |
// It seems like we can close the file handle as well (because | |
// a reference is hold by the filemap object). | |
::CloseHandle(hFile); | |
// Store the handles inside the shared_ptr (with the custom destructors) | |
m_mem.reset(static_cast<CharT*>(pMem), ::UnmapViewOfFile); | |
// Start of the file | |
m_curChar = m_mem.get(); | |
} | |
mmap_file_iterator(const mmap_file_iterator& iter) | |
{ *this = iter; } | |
mmap_file_iterator& operator=(const mmap_file_iterator& iter) | |
{ | |
m_curChar = iter.m_curChar; | |
m_mem = iter.m_mem; | |
m_filesize = iter.m_filesize; | |
return *this; | |
} | |
// Nasty bug in Comeau up to 4.3.0.1, we need explicit boolean context | |
// for shared_ptr to evaluate correctly | |
operator bool() const | |
{ return m_mem ? true : false; } | |
bool operator==(const mmap_file_iterator& iter) const | |
{ return m_curChar == iter.m_curChar; } | |
const CharT& get_cur_char(void) const | |
{ return *m_curChar; } | |
void next_char(void) | |
{ m_curChar++; } | |
void prev_char(void) | |
{ m_curChar--; } | |
void advance(std::ptrdiff_t n) | |
{ m_curChar += n; } | |
std::ptrdiff_t distance(const mmap_file_iterator& iter) const | |
{ return m_curChar - iter.m_curChar; } | |
void seek_end(void) | |
{ | |
m_curChar = m_mem.get() + | |
(m_filesize / sizeof(CharT)); | |
} | |
private: | |
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION | |
typedef boost::remove_pointer<HANDLE>::type handle_t; | |
#else | |
typedef void handle_t; | |
#endif | |
boost::shared_ptr<CharT> m_mem; | |
std::size_t m_filesize; | |
CharT* m_curChar; | |
}; | |
#endif // BOOST_SPIRIT_FILEITERATOR_WINDOWS | |
/////////////////////////////////////////////////////////////////////////////// | |
// mmap_file_iterator, POSIX version | |
#ifdef BOOST_SPIRIT_FILEITERATOR_POSIX | |
template <typename CharT> | |
class mmap_file_iterator | |
{ | |
private: | |
struct mapping | |
{ | |
mapping(void *p, off_t len) | |
: data(p) | |
, size(len) | |
{ } | |
CharT const *begin() const | |
{ | |
return static_cast<CharT *>(data); | |
} | |
CharT const *end() const | |
{ | |
return static_cast<CharT *>(data) + size/sizeof(CharT); | |
} | |
~mapping() | |
{ | |
munmap(static_cast<char*>(data), size); | |
} | |
private: | |
void *data; | |
off_t size; | |
}; | |
public: | |
typedef CharT value_type; | |
mmap_file_iterator() | |
{} | |
explicit mmap_file_iterator(std::string file_name) | |
{ | |
// open the file | |
int fd = open(file_name.c_str(), | |
#ifdef O_NOCTTY | |
O_NOCTTY | // if stdin was closed then opening a file | |
// would cause the file to become the controlling | |
// terminal if the filename refers to a tty. Setting | |
// O_NOCTTY inhibits this. | |
#endif | |
O_RDONLY); | |
if (fd == -1) | |
return; | |
// call fstat to find get information about the file just | |
// opened (size and file type) | |
struct stat stat_buf; | |
if ((fstat(fd, &stat_buf) != 0) || !S_ISREG(stat_buf.st_mode)) | |
{ // if fstat returns an error or if the file isn't a | |
// regular file we give up. | |
close(fd); | |
return; | |
} | |
// perform the actual mapping | |
void *p = mmap(0, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0); | |
// it is safe to close() here. POSIX requires that the OS keeps a | |
// second handle to the file while the file is mmapped. | |
close(fd); | |
if (p == MAP_FAILED) | |
return; | |
mapping *m = 0; | |
try | |
{ | |
m = new mapping(p, stat_buf.st_size); | |
} | |
catch(...) | |
{ | |
munmap(static_cast<char*>(p), stat_buf.st_size); | |
throw; | |
} | |
m_mem.reset(m); | |
// Start of the file | |
m_curChar = m_mem->begin(); | |
} | |
mmap_file_iterator(const mmap_file_iterator& iter) | |
{ *this = iter; } | |
mmap_file_iterator& operator=(const mmap_file_iterator& iter) | |
{ | |
m_curChar = iter.m_curChar; | |
m_mem = iter.m_mem; | |
return *this; | |
} | |
// Nasty bug in Comeau up to 4.3.0.1, we need explicit boolean context | |
// for shared_ptr to evaluate correctly | |
operator bool() const | |
{ return m_mem ? true : false; } | |
bool operator==(const mmap_file_iterator& iter) const | |
{ return m_curChar == iter.m_curChar; } | |
const CharT& get_cur_char(void) const | |
{ return *m_curChar; } | |
void next_char(void) | |
{ m_curChar++; } | |
void prev_char(void) | |
{ m_curChar--; } | |
void advance(signed long n) | |
{ m_curChar += n; } | |
long distance(const mmap_file_iterator& iter) const | |
{ return m_curChar - iter.m_curChar; } | |
void seek_end(void) | |
{ | |
m_curChar = m_mem->end(); | |
} | |
private: | |
boost::shared_ptr<mapping> m_mem; | |
CharT const* m_curChar; | |
}; | |
#endif // BOOST_SPIRIT_FILEITERATOR_POSIX | |
/////////////////////////////////////////////////////////////////////////////// | |
} /* namespace boost::spirit::fileiter_impl */ | |
template <typename CharT, typename BaseIteratorT> | |
file_iterator<CharT,BaseIteratorT> | |
file_iterator<CharT,BaseIteratorT>::make_end(void) | |
{ | |
file_iterator iter(*this); | |
iter.base_reference().seek_end(); | |
return iter; | |
} | |
template <typename CharT, typename BaseIteratorT> | |
file_iterator<CharT,BaseIteratorT>& | |
file_iterator<CharT,BaseIteratorT>::operator=(const base_t& iter) | |
{ | |
base_t::operator=(iter); | |
return *this; | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
BOOST_SPIRIT_CLASSIC_NAMESPACE_END | |
}} /* namespace boost::spirit */ | |
#endif /* BOOST_SPIRIT_FILE_ITERATOR_IPP */ |