blob: 14709a51b47306b58dd0b3f52542e6ae9217ddc3 [file] [log] [blame]
/*=============================================================================
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 */