// Copyright (c) 2004 David Muse
// See the COPYING file for more information

#include <rudiments/memorymap.h>
#include <rudiments/error.h>

#include <stdio.h>
#ifdef RUDIMENTS_HAVE_MMAP
	#include <sys/mman.h>
#endif

// for getpagesize()...
#ifdef RUDIMENTS_HAVE_UNISTD_H
	#include <unistd.h>
#endif
#ifdef RUDIMENTS_HAVE_WINDOWS_H
	#include <windows.h>
#endif

#ifdef RUDIMENTS_HAVE_MUNMAP_CADDR_T
	#define MUNMAP_ADDRCAST caddr_t
#else
	#define MUNMAP_ADDRCAST void *
#endif

#ifdef RUDIMENTS_HAVE_MINCORE_CADDR_T
	#define MINCORE_ADDRCAST caddr_t
#else
	#define MINCORE_ADDRCAST void *
#endif

#ifdef RUDIMENTS_HAVE_MPROTECT_CADDR_T
	#define MPROTECT_ADDRCAST caddr_t
#else
	#define MPROTECT_ADDRCAST void *
#endif

#ifdef RUDIMENTS_HAVE_MSYNC_CADDR_T
	#define MSYNC_ADDRCAST caddr_t
#else
	#define MSYNC_ADDRCAST void *
#endif

#ifdef RUDIMENTS_HAVE_MLOCK_CADDR_T
	#define MLOCK_ADDRCAST caddr_t
#else
	#define MLOCK_ADDRCAST void *
#endif

#ifdef RUDIMENTS_HAVE_MUNLOCK_CADDR_T
	#define MUNLOCK_ADDRCAST caddr_t
#else
	#define MUNLOCK_ADDRCAST void *
#endif

#ifdef RUDIMENTS_HAVE_MADVISE_CADDR_T
	#define MADVISE_ADDRCAST caddr_t
#else
	#define MADVISE_ADDRCAST void *
#endif

#ifdef RUDIMENTS_NAMESPACE
namespace rudiments {
#endif

class memorymapprivate {
	friend class memorymap;
	private:
		void	*_data;
		#ifdef RUDIMENTS_HAVE_CREATE_FILE_MAPPING
		HANDLE	_map;
		#endif
		size_t	_length;
};

memorymap::memorymap() {
	pvt=new memorymapprivate;
	pvt->_data=NULL;
	#ifdef RUDIMENTS_HAVE_CREATE_FILE_MAPPING
	pvt->_map=NULL;
	#endif
	pvt->_length=0;
}

memorymap::~memorymap() {
	detach();
	delete pvt;
}

bool memorymap::attach(int fd, off64_t offset, size_t len,
					int protection, int flags) {
	pvt->_length=len;
	#if defined(RUDIMENTS_HAVE_MMAP)
	do {
		pvt->_data=mmap(NULL,len,protection,flags,fd,offset);
	} while (pvt->_data==MAP_FAILED &&
			error::getErrorNumber()==EINTR);
	return (pvt->_data!=MAP_FAILED);
	#elif defined(RUDIMENTS_HAVE_CREATE_FILE_MAPPING)
	DWORD	mapprot=(protection|PROT_WRITE)?
				PAGE_READONLY:PAGE_READWRITE;
	pvt->_map=CreateFileMapping((HANDLE)_get_osfhandle(fd),
				NULL,mapprot,0,len,NULL);
	if (pvt->_map) {
		DWORD	viewprot=(protection|PROT_WRITE)?
					FILE_MAP_READ:FILE_MAP_WRITE;
		pvt->_data=MapViewOfFile(pvt->_map,viewprot,0,0,len);
		if (!pvt->_data) {
			CloseHandle(pvt->_map);
			return false;
		}
		return true;
	}
	return false;
	#else
	return false;
	#endif
}

bool memorymap::detach() {
	#if defined(RUDIMENTS_HAVE_MMAP) || \
		defined(RUDIMENTS_HAVE_CREATE_FILE_MAPPING)
		#if defined(RUDIMENTS_HAVE_MMAP)
		int	result;
		do {
			result=munmap(reinterpret_cast<MUNMAP_ADDRCAST>
						(pvt->_data),pvt->_length);
		} while (result==-1 && error::getErrorNumber()==EINTR);
		bool	retval=!result;
		#elif defined(RUDIMENTS_HAVE_CREATE_FILE_MAPPING)
		bool	retval=(UnmapViewOfFile(pvt->_data) &&
						CloseHandle(pvt->_map));
		#endif
		pvt->_data=NULL;
		pvt->_length=0;
		return retval;
	#else
		return false;
	#endif
}

bool memorymap::setProtection(int protection) {
	return setProtection(0,pvt->_length,protection);
}

bool memorymap::setProtection(off64_t offset, size_t len, int protection) {
	#ifdef RUDIMENTS_HAVE_MPROTECT
	unsigned char	*ptr=(static_cast<unsigned char *>(pvt->_data))+offset;
	int	result;
	do {
		result=mprotect(reinterpret_cast<MPROTECT_ADDRCAST>(ptr),
							len,protection);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
	#else
	return false;
	#endif
}

void *memorymap::getData() {
	return pvt->_data;
}

size_t memorymap::getLength() {
	return pvt->_length;
}

bool memorymap::sync(bool immediate, bool invalidate) {
	return sync(0,pvt->_length,immediate,invalidate);
}

bool memorymap::sync(off64_t offset, size_t len,
			bool immediate, bool invalidate) {
	unsigned char	*ptr=(static_cast<unsigned char *>(pvt->_data))+offset;
	#ifdef RUDIMENTS_HAVE_MSYNC
	int	result;
	do {
		result=msync(reinterpret_cast<MSYNC_ADDRCAST>(ptr),len,
				((immediate)?MS_SYNC:MS_ASYNC)|
					((invalidate)?MS_INVALIDATE:0));
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
	#elif defined(RUDIMENTS_HAVE_CREATE_FILE_MAPPING)
	return FlushViewOfFile(reinterpret_cast<void *>(ptr),len);
	#else
	return true;
	#endif
}

bool memorymap::sequentialAccess(off64_t offset, size_t len) {
	#if defined(RUDIMENTS_HAVE_MADVISE) && defined(MADV_SEQUENTIAL)
	unsigned char	*ptr=(static_cast<unsigned char *>(pvt->_data))+offset;
	return mAdvise(ptr,len,MADV_SEQUENTIAL);
	#else
	return true;
	#endif
}

bool memorymap::randomAccess(off64_t offset, size_t len) {
	#if defined(RUDIMENTS_HAVE_MADVISE) && defined(MADV_RANDOM)
	unsigned char	*ptr=(static_cast<unsigned char *>(pvt->_data))+offset;
	return mAdvise(ptr,len,MADV_RANDOM);
	#else
	return true;
	#endif
}

bool memorymap::willNeed(off64_t offset, size_t len) {
	#if defined(RUDIMENTS_HAVE_MADVISE) && defined(MADV_WILLNEED)
	unsigned char	*ptr=(static_cast<unsigned char *>(pvt->_data))+offset;
	return mAdvise(ptr,len,MADV_WILLNEED);
	#else
	return true;
	#endif
}

bool memorymap::wontNeed(off64_t offset, size_t len) {
	#if defined(RUDIMENTS_HAVE_MADVISE) && defined(MADV_DONTNEED)
	unsigned char	*ptr=(static_cast<unsigned char *>(pvt->_data))+offset;
	return mAdvise(ptr,len,MADV_DONTNEED);
	#else
	return true;
	#endif
}

bool memorymap::normalAccess(off64_t offset, size_t len) {
	#if defined(RUDIMENTS_HAVE_MADVISE) && defined(MADV_NORMAL)
	unsigned char	*ptr=(static_cast<unsigned char *>(pvt->_data))+offset;
	return mAdvise(ptr,len,MADV_NORMAL);
	#else
	return true;
	#endif
}

bool memorymap::lock() {
	return lock(0,pvt->_length);
}

bool memorymap::lock(off64_t offset, size_t len) {
	#ifdef RUDIMENTS_HAVE_MLOCK
	unsigned char	*ptr=(static_cast<unsigned char *>(pvt->_data))+offset;
	int	result;
	do {
		result=mlock(reinterpret_cast<MLOCK_ADDRCAST>(ptr),len);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
	#else
	return false;
	#endif
}

bool memorymap::unlock() {
	return unlock(0,pvt->_length);
}

bool memorymap::unlock(off64_t offset, size_t len) {
	#ifdef RUDIMENTS_HAVE_MUNLOCK
	unsigned char	*ptr=(static_cast<unsigned char *>(pvt->_data))+offset;
	int	result;
	do {
		result=munlock(reinterpret_cast<MUNLOCK_ADDRCAST>(ptr),len);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
	#else
	return false;
	#endif
}

bool memorymap::inMemory() {
	return inMemory(0,pvt->_length);
}

bool memorymap::inMemory(off64_t offset, size_t len) {

	#ifdef RUDIMENTS_HAVE_MINCORE

	// create an array of char's, 1 for each page
	int		pagesize=getpagesize();
	int		tmplen=(len+pagesize-1)/pagesize;
	#ifdef RUDIMENTS_HAVE_MINCORE_CHAR
	char		*tmp=new char[tmplen];
	#else
	unsigned char	*tmp=new unsigned char[tmplen];
	#endif

	// call mincore to fill the array
	unsigned char	*ptr=(static_cast<unsigned char *>(pvt->_data))+offset;
	int		result;
	do {
		result=mincore(reinterpret_cast<MINCORE_ADDRCAST>(ptr),len,tmp);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	if (result) {
		delete[] tmp;
		return false;
	}

	// look through the array, if any of the
	// pages aren't in memory, return false
	for (int i=0; i<tmplen; i++) {
		if (tmp[i]) {
			delete[] tmp;
			return false;
		}
	}
	delete[] tmp;
	return true;
	#else
	return false;
	#endif
}

bool memorymap::lockAll() {
	#if defined(MCL_CURRENT) && defined(MCL_FUTURE)
	return mLockAll(MCL_CURRENT|MCL_FUTURE);
	#else
	return false;
	#endif
}

bool memorymap::lockAllCurrent() {
	#if defined(MCL_CURRENT)
	return mLockAll(MCL_CURRENT);
	#else
	return false;
	#endif
}

bool memorymap::lockAllFuture() {
	#if defined(MCL_FUTURE)
	return mLockAll(MCL_FUTURE);
	#else
	return false;
	#endif
}

bool memorymap::unlockAll() {
	#ifdef RUDIMENTS_HAVE_MUNLOCKALL
	int	result;
	do {
		result=munlockall();
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
	#else
	return false;
	#endif
}

bool memorymap::mAdvise(unsigned char *start, size_t length, int advice) {
	#if defined(RUDIMENTS_HAVE_MADVISE)
	int	result;
	do {
		result=madvise(reinterpret_cast<MADVISE_ADDRCAST>(start),
								length,advice);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
	#else
	return true;
	#endif
}

bool memorymap::mLockAll(int flags) {
	#ifdef RUDIMENTS_HAVE_MLOCKALL
	int	result;
	do {
		result=mlockall(flags);
	} while (result==-1 && error::getErrorNumber()==EINTR);
	return !result;
	#else
	return false;
	#endif
}

#ifdef RUDIMENTS_NAMESPACE
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1