blob: 2c04affdee8d1e6341e92ce5d296c5e7992cb859 [file] [log] [blame]
//===- MemoryArea.h -------------------------------------------------------===//
//
// The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_MEMORY_AREA_H
#define MCLD_MEMORY_AREA_H
#ifdef ENABLE_UNITTEST
#include <gtest.h>
#endif
#include "mcld/ADT/Uncopyable.h"
#include "mcld/Support/FileSystem.h"
#include "mcld/Support/Path.h"
#include <llvm/ADT/ilist.h>
#include <llvm/ADT/ilist_node.h>
#include <fcntl.h>
#include <string>
#include <list>
#if defined(ENABLE_UNITTEST)
namespace mcldtest
{
class MemoryAreaTest;
} // namespace of mcldtest
#endif
namespace mcld
{
class MemoryRegion;
class RegionFactory;
/** \class MemoryArea
* \brief MemoryArea is used to manage distinct MemoryRegions of address space.
*
* Good linkers must well manipulate memory mapped I/O and dynamic memory.
* In MCLinker, MemoryArea is the decision-maker to use memory mapped I/O or
* dynamic memory. When a client requests MemoryArea for a piece of memory
* to hold a part of a file, MemoryArea is going to see whether the requested
* part of the file is already in any existing memory which is requested
* before. If it is, MemoryArea creates a new MemoryRegion within the memory
* requested before. Otherwise, MemoryArea uses memory mapped I/O or dynamic
* memory to load the file.
*
* If the part a file being loaded is larger than 3/4 pages, MemoryArea uses
* memory mapped I/O to load the file. Otherwise, MemoryArea uses dynamic
* memory to read the content of file into the memory space.
*/
class MemoryArea : private Uncopyable
{
#if defined(ENABLE_UNITTEST)
friend class mcldtest::MemoryAreaTest;
#endif
public:
enum IOState
{
GoodBit = 0,
BadBit = 1L << 0,
EOFBit = 1L << 1,
FailBit = 1L << 2,
IOStateEnd = 1L << 16
};
enum AccessMode
{
ReadOnly = O_RDONLY,
WriteOnly = O_WRONLY,
ReadWrite = O_RDWR,
AccessMask = O_ACCMODE
};
private:
typedef sys::fs::detail::Address Address;
friend class MemoryRegion;
friend class RegionFactory;
struct Space : public llvm::ilist_node<Space>
{
public:
enum Type
{
ALLOCATED_ARRAY,
MMAPED,
UNALLOCATED
};
public:
Space()
: m_pParent(NULL),
type(UNALLOCATED),
file_offset(0),
size(0),
data(0),
region_num(0)
{ }
Space(MemoryArea* pParent, size_t pOffset, size_t pLength)
: m_pParent(pParent),
type(UNALLOCATED),
file_offset(pOffset),
size(pLength),
data(0),
region_num(0)
{ }
~Space()
{ }
void sync()
{ m_pParent->write(*this); }
private:
MemoryArea* m_pParent;
public:
Type type;
size_t file_offset;
size_t size;
sys::fs::detail::Address data;
size_t region_num;
};
friend class Space;
typedef llvm::iplist<Space> SpaceList;
public:
// constructor
// @param pRegionFactory the factory to manage MemoryRegions
MemoryArea(RegionFactory& pRegionFactory);
// destructor
~MemoryArea();
// request - create a MemoryRegion within a sufficient space
// find an existing space to hold the MemoryRegion.
// if MemoryArea does not find such space, then it creates a new space and
// assign a MemoryRegion into the space.
MemoryRegion* request(size_t pOffset, size_t pLength);
// release - release a MemoryRegion.
// release a MemoryRegion does not cause
void release(MemoryRegion* pRegion);
// clean - release all MemoryRegion and unmap all spaces.
void clean();
// sync - sync all MemoryRegion
void sync();
// map - open the file pPath and mapped it onto MemoryArea
// @param flags see man 2 open
void map(const sys::fs::Path& pPath, int flags);
// map - open the file pPath and mapped it onto MemoryArea
// @param flags see man 2 open
// @param mode see man 2 open
void map(const sys::fs::Path& pPath, int flags, int mode);
// unmap - close the opened file and unmap the MemoryArea
void unmap();
// path - the path of the mapped file.
const sys::fs::Path& path() const
{ return m_FilePath; }
// size - the real size of the mapped file.
size_t size() const
{ return m_FileSize; }
// isMapped - check if MemoryArea is mapped to a file
bool isMapped() const;
// isGood - check if the state of the opened area is good for read/write
// operations
bool isGood() const;
// isBad - check if an error causes the loss of integrity of the memory space
bool isBad() const;
// isFailed - check if an error related to the internal logic of the operation
// itself occurs
bool isFailed() const;
// isEOF - check if we reach the end of the file
bool isEOF() const;
// isReadable - check if the memory area is readable
bool isReadable() const;
// isWriteable - check if the memory area is writable
bool isWritable() const;
// rdstate - get error state flags
// Returns the current internal error state flags of the stream
int rdstate() const;
// setState - set error state flag
void setState(IOState pState);
// clear - set error state flag
void clear(IOState pState = GoodBit);
private:
// readToBuffer - read data from the file behind this MemorySpace and store
// those bytes in pBuf. Return the number of byte read or -1 on error.
ssize_t readToBuffer(sys::fs::detail::Address pBuf,
size_t pSize, size_t pOffset);
private:
// find - first fit search
Space* find(size_t pOffset, size_t pLength);
// release a Space, but does not remove it from space list
void release(Space* pSpace);
// read - read data from mapped file into virtual memroy of pSpace. Return
// false on error.
bool read(Space& pSpace);
// write - write back the virtual memory of pSpace into mapped file.
void write(const Space& pSpace);
// truncate - truncate the file size to length.
void truncate(size_t pLength);
// policy - decide whehter to use dynamic memory or memory mapped I/O
Space::Type policy(off_t pOffset, size_t pLength);
// the size of one page
static const off_t PageSize = 4096;
// page_boundary - Given a file size, return the size to read integral pages.
// return the first page boundary after pFileOffset
static off_t page_boundary(off_t pFileOffset)
{ return (pFileOffset + (PageSize - 1)) & ~ (PageSize - 1); }
// Given a file offset, return the page offset.
// return the first page boundary before pFileOffset
static off_t page_offset(off_t pFileOffset)
{ return pFileOffset & ~ (PageSize - 1); }
private:
RegionFactory& m_RegionFactory;
sys::fs::Path m_FilePath;
int m_FileDescriptor;
size_t m_FileSize;
int m_AccessFlags;
int m_State;
SpaceList m_SpaceList;
};
} // namespace of mcld
#endif