//============================================================================
// Name        : mmapTest.cpp
// Author      : 
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <limits.h>
#include <errno.h>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <sys/types.h>
#include <fcntl.h>
#include <inttypes.h>
#include <unistd.h>
#include <ctime>

extern int errno;

using namespace std;


//#define MMAP_TOTAL_SZ	(1 * 1024 * 1024 * 1024UL)
const off64_t PAGE_SIZE=1 * 1024 * 1024 * 1024;
char buffer[PAGE_SIZE]={'a'};

int main(int argc, char** argv)
{
	if (argc < 2)
	{
		cout << "usage: filename [size]" << endl;
		return -1;
	}
	off64_t size = 0;
	if (argc == 3)
	{
		if (sscanf(argv[2], "%jd", &size) < 0)
		{
			cout << "argv[2] [" << argv[2] << "] is not valid size:" << strerror(errno) << endl;
			perror("sscanf failed");
			return -1;
		}
	}

	int fd = open(argv[1], O_RDWR);
	if (fd > 0)
	{
		if (size == 0)
		{
			struct stat64 sb;
			if (fstat64(fd, &sb) == 0)
			{
				size = sb.st_size;
				if (size == 0)
				{
					// it is possible it is just a devdax char device, here we need to figure out its entire device size
					//
					char spath[PATH_MAX];
					char npath[PATH_MAX];
					char *rpath, *basename;
					snprintf(spath, PATH_MAX, "/sys/dev/char/%d:%d/subsystem",
							 major(sb.st_rdev), minor(sb.st_rdev));

					rpath = realpath(spath, npath);
					if (!rpath)
					{
						perror("realpath failed");
					}
					else
					{
						/* check if DAX device */
						basename = strrchr(rpath, '/');
						if (!basename || strcmp("dax", basename+1))
						{
							perror("basename failed");
						}
						else
						{
							snprintf(spath, PATH_MAX, "/sys/dev/char/%d:%d/size",
								 major(sb.st_rdev), minor(sb.st_rdev));

							FILE* sfile = fopen(spath, "r");
							if (!sfile)
							{
								perror("fopen failed");
							}
							else
							{

								if (fscanf(sfile, "%jd", &size) < 0)
								{
									perror("fscanf failed");
								}
								else
								{
									cout << "success to get file size of devdax:" << size<< endl;
								}
								fclose(sfile);
							}
						}
					}
				}
			}
		}
		cout << "about to mmap ["<<argv[1] <<"] of size:"<< size << endl;

		char * pAddr = (char*)mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
		if (pAddr != MAP_FAILED)
		{
			time_t start = time(NULL);
			for (off64_t i = 0; i+PAGE_SIZE < size; i+=PAGE_SIZE)
			{
				memcpy(pAddr+i, buffer, PAGE_SIZE);
				//pAddr[i] = 'A';
				msync(pAddr+i,PAGE_SIZE, MS_SYNC);
			}

			time_t end = time(NULL);
			cout << "time elapsed:" << end-start << endl;
			munmap(pAddr, size);
		}
		else
		{
			perror("mmap failed");
		}
		close(fd);
	}
	else
	{
		perror("open failed");
	}

	return 0;
}