/*****************************************************************************

$Id: mapper.cpp 4527 2007-07-04 10:21:34Z francis $

File:     mapper.cpp
Date:     02Jul07

Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.
Gmail: garbagecat10

This program is free software; you can redistribute it and/or modify
it under the terms of either: 1) 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; or 2) Ruby's License.

See the file COPYING for complete licensing information.

*****************************************************************************/


//////////////////////////////////////////////////////////////////////
// UNIX implementation
//////////////////////////////////////////////////////////////////////


#ifdef OS_UNIX

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>

#include <iostream>
#include <string>
#include <cstring>
#include <stdexcept>

#include "mapper.h"

/******************
Mapper_t::Mapper_t
******************/

Mapper_t::Mapper_t (const std::string &filename)
{
	/* We ASSUME we can open the file.
	 * (More precisely, we assume someone else checked before we got here.)
	 */

	Fd = open (filename.c_str(), O_RDONLY);
	if (Fd < 0)
		throw std::runtime_error (strerror (errno));

	struct stat st;
	if (fstat (Fd, &st))
		throw std::runtime_error (strerror (errno));
	FileSize = st.st_size;

	#ifdef OS_WIN32
	MapPoint = (char*) mmap (0, FileSize, PROT_READ, MAP_SHARED, Fd, 0);
	#else
	MapPoint = (const char*) mmap (0, FileSize, PROT_READ, MAP_SHARED, Fd, 0);
	#endif
	if (MapPoint == MAP_FAILED)
		throw std::runtime_error (strerror (errno));
}


/*******************
Mapper_t::~Mapper_t
*******************/

Mapper_t::~Mapper_t()
{
	Close();
}


/***************
Mapper_t::Close
***************/

void Mapper_t::Close()
{
	// Can be called multiple times.
	// Calls to GetChunk are invalid after a call to Close.
	if (MapPoint) {
		#ifdef CC_SUNWspro
		// TODO: The void * cast works fine on Solaris 11, but
		// I don't know at what point that changed from older Solaris.
		munmap ((char*)MapPoint, FileSize);
		#else
		munmap ((void*)MapPoint, FileSize);
		#endif
		MapPoint = NULL;
	}
	if (Fd >= 0) {
		close (Fd);
		Fd = -1;
	}
}

/******************
Mapper_t::GetChunk
******************/

const char *Mapper_t::GetChunk (unsigned start)
{
	return MapPoint + start;
}



#endif // OS_UNIX


//////////////////////////////////////////////////////////////////////
// WINDOWS implementation
//////////////////////////////////////////////////////////////////////

#ifdef OS_WIN32

#include <windows.h>

#include <iostream>
#include <string>
#include <stdexcept>

#include "mapper.h"

/******************
Mapper_t::Mapper_t
******************/

Mapper_t::Mapper_t (const std::string &filename)
{
	/* We ASSUME we can open the file.
	 * (More precisely, we assume someone else checked before we got here.)
	 */

	hFile = INVALID_HANDLE_VALUE;
	hMapping = NULL;
	MapPoint = NULL;
	FileSize = 0;

	hFile = CreateFile (filename.c_str(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	if (hFile == INVALID_HANDLE_VALUE)
		throw std::runtime_error ("File not found");

	BY_HANDLE_FILE_INFORMATION i;
	if (GetFileInformationByHandle (hFile, &i))
		FileSize = i.nFileSizeLow;

	hMapping = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
	if (!hMapping)
		throw std::runtime_error ("File not mapped");

	#ifdef OS_WIN32
	MapPoint = (char*) MapViewOfFile (hMapping, FILE_MAP_WRITE, 0, 0, 0);
	#else
	MapPoint = (const char*) MapViewOfFile (hMapping, FILE_MAP_WRITE, 0, 0, 0);
	#endif
	if (!MapPoint)
		throw std::runtime_error ("Mappoint not read");
}


/*******************
Mapper_t::~Mapper_t
*******************/

Mapper_t::~Mapper_t()
{
	Close();
}

/***************
Mapper_t::Close
***************/

void Mapper_t::Close()
{
	// Can be called multiple times.
	// Calls to GetChunk are invalid after a call to Close.
	if (MapPoint) {
		UnmapViewOfFile (MapPoint);
		MapPoint = NULL;
	}
	if (hMapping != NULL) {
		CloseHandle (hMapping);
		hMapping = NULL;
	}
	if (hFile != INVALID_HANDLE_VALUE) {
		CloseHandle (hFile);
		hFile = INVALID_HANDLE_VALUE;
	}
}


/******************
Mapper_t::GetChunk
******************/

const char *Mapper_t::GetChunk (unsigned start)
{
	return MapPoint + start;
}



#endif // OS_WINDOWS
