#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>

#define BLCKSZ		8192
#define FILE_SIZE	(1024 * 1024 * 1024)

unsigned long long
timediff(struct timeval t0, struct timeval t1)
{
	unsigned long long v;

	v = t1.tv_sec - t0.tv_sec;
	v *= 1000000;
	v += t1.tv_usec - t0.tv_usec;

	return v;
}

void
write_test_file(int b, int use_fallocate)
{
	int		fd;
	int		write_size = b * BLCKSZ;
	int		bytes_written = 0;
	struct timeval	t0, t1, t2;
	char   *buffer;
	unsigned long long i1, i2;

	buffer = malloc(write_size);
	if (buffer == NULL)
	{
		perror("malloc");
		exit(1);
	}
	memset(buffer, 0, write_size);

	sync();
	sleep(10);

	fd = open("extend.test", O_WRONLY|O_CREAT|O_EXCL, 0700);
	if (fd < 0)
	{
		perror("open");
		exit(1);
	}
	if (fdatasync(fd) < 0)
	{
		perror("fdatasync");
		exit(1);
	}

	gettimeofday(&t0, NULL);
	while (bytes_written < FILE_SIZE)
	{
		if (use_fallocate)
		{
			if (posix_fallocate(fd, 0, bytes_written + write_size) < 0)
			{
				perror("ftruncate");
				exit(1);
			}
			bytes_written += write_size;
		}
		else
		{
			off_t	pos;
			int		wc;

			pos = lseek(fd, bytes_written, SEEK_SET);
			if (pos != bytes_written)
			{
				perror("lseek");
				exit(1);
			}
			wc = write(fd, buffer, write_size);
			if (wc != write_size)
			{
				perror("write");
				exit(1);
			}
			bytes_written += wc;
		}
	}

	gettimeofday(&t1, NULL);
	if (fdatasync(fd) < 0)
	{
		perror("fdatasync(2)");
		exit(1);
	}
	gettimeofday(&t2, NULL);

	if (close(fd) < 0)
	{
		perror("close");
		exit(1);
	}
	if (unlink("extend.test") < 0)
	{
		perror("unlink");
		exit(1);
	}

	printf("%s %d 8K blocks at a time: write=%.3f fdatasync=%.3f\n",
		   use_fallocate ? "posix_fallocate" : "write",
		   b, timediff(t0, t1) / 1000.0, timediff(t1, t2) / 1000.0);
}

int
main(int argc, char **argv)
{
	int r,
		b;

	for (r = 0; r < 3; ++r)
	{
		for (b = 1; b <= 64; b *= 2)
		{
			write_test_file(b, 1);
			write_test_file(b, 0);
		}
	}
	for (r = 0; r < 3; ++r)
	{
		for (b = 64; b != 0; b /= 2)
		{
			write_test_file(b, 0);
			write_test_file(b, 1);
		}
	}
	exit(0);
}
