/*
**      PJL C++ Library
**      mmap_file.h
**
**      Copyright (C) 1998  Paul J. Lucas
**
**      This program is free software; you can redistribute it and/or modify
**      it under the terms of the GNU General Public License as published by
**      the Free Software Foundation; either version 2 of the License, or
**      (at your option) any later version.
**
**      This program is distributed in the hope that it will be useful,
**      but WITHOUT ANY WARRANTY; without even the implied warranty of
**      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**      GNU General Public License for more details.
**
**      You should have received a copy of the GNU General Public License
**      along with this program; if not, write to the Free Software
**      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef mmap_file_H
#define mmap_file_H

// standard
#include <cstddef>                              /* for ptrdiff_t, size_t */
#include <fstream>                              /* for openmode */
#include <iterator>
#include <sys/types.h>                          /* needed by FreeBSD systems */
#include <sys/mman.h>                           /* for mmap(2) */

//
// Some C headers on some implementations define open/close as macros to remap
// them to functions that handle files larger than 2 GB.  This works fine in C,
// but can break C++ where classes have open/close member functions.  Undefine
// them and live with the consequence of not being able to deal with large
// files on such (brain-damaged) implementations.
//
#ifdef  open
#undef  open
#endif
#ifdef  close
#undef  close
#endif

// local
#include "platform.h"

namespace PJL {

//*****************************************************************************
//
// SYNOPSYS
//
        class mmap_file
//
// DESCRIPTION
//
//      An mmap_file is an object that maps a file into memory (via the Unix
//      system call mmap(2)) allowing it to be accessed via iterators.
//      Processing a file, especially files accessed randomly, is MUCH faster
//      than standard I/O.
//
// SEE ALSO
//
//      mmap(2)
//
//      W. Richard Stevens.  "Advanced Programming in the Unix Environment,"
//      Addison-Wesley, Reading, MA, 1993.  pp. 407-413
//
//*****************************************************************************
{
public:
    ////////// typedefs ///////////////////////////////////////////////////////

    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef char value_type;
    typedef value_type* pointer;
    typedef value_type const* const_pointer;
    typedef value_type& reference;
    typedef value_type const& const_reference;

    enum behavior_type {
#ifdef  PJL_NO_MADVISE
        bt_normal,
        bt_random,
        bt_sequential
#else
        bt_normal   = MADV_NORMAL,
        bt_random   = MADV_RANDOM,
        bt_sequential   = MADV_SEQUENTIAL
#endif
    };

    ////////// constructors & destructor //////////////////////////////////////

    mmap_file()                             { init(); }
    mmap_file( char const *path, std::ios::openmode mode = std::ios::in ) {
        init();
        open( path, mode );
    }
    ~mmap_file()                            { close(); }

    ////////// iterators //////////////////////////////////////////////////////

    typedef pointer iterator;
    typedef const_pointer const_iterator;
#ifndef __SUNPRO_CC
    //
    // There's a known bug with the reverse_iterator definition in the STL
    // headers that Sun's CC compiler uses.  Since we don't actually use
    // reverse_iterator in SWISH++, just leave it out.
    //
    typedef std::reverse_iterator< iterator > reverse_iterator;
    typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
#endif

    iterator        begin()                 { return (iterator)addr_; }
    const_iterator  begin() const           { return (const_iterator)addr_; }
    iterator        end()                   { return begin() + size_; }
    const_iterator  end() const             { return begin() + size_; }

#ifndef __SUNPRO_CC
    reverse_iterator rbegin() {
        return reverse_iterator( end() );
    }
    reverse_iterator rend() {
        return reverse_iterator( begin() );
    }
    const_reverse_iterator rbegin() const {
        return const_reverse_iterator( end() );
    }
    const_reverse_iterator rend() const {
        return const_reverse_iterator( begin() );
    }
#endif  /* __SUNPRO_CC */

    ////////// member functions ///////////////////////////////////////////////

    operator    bool() const                { return !errno_; }

    reference       back()                  { return *( end() - 1 ); }
    const_reference back() const            { return *( end() - 1 ); }
    int             behavior( behavior_type ) const;
    reference       front()                 { return *begin(); }
    const_reference front() const           { return *begin(); }
    bool            open( char const *path, std::ios::openmode = std::ios::in );
    void            close();
    bool            empty() const           { return !size_; }
    int             error() const           { return errno_; }
    size_type       max_size() const        { return size_; }
    size_type       size() const            { return size_; }

    reference operator[]( size_type i ) {
        return *( begin() + i );
    }
    const_reference operator[]( size_type i ) const {
        return *( begin() + i );
    }
private:
    size_type   size_;
    int         fd_;                            // Unix file descriptor
    void        *addr_;
    mutable int errno_;
    void        init();
};

} // namespace PJL

#endif  /* mmap_file_H */
/* vim:set et sw=4 ts=4: */
