#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define BLOCKSIZE 4096
#define RECSIZE 8
#define COUNT 1000000

static void
do_test(int append_fd, int inplace_fd, int use_fdatasync)
{
  char buf[RECSIZE+1];
  char buf2[BLOCKSIZE];
  int seq;
  int res;

  memset(buf2, 0, sizeof(buf2));
  for (seq = 0; seq < COUNT; ++seq)
  {
    printf("Adding log entry %d...\n", seq);
    snprintf(buf, sizeof(buf), "%7d\n", seq);

    res = write(append_fd, buf, RECSIZE);
    if (res != RECSIZE)
    {
      fprintf(stderr, "Unexpected return %d from write() (errno=%d: %s)\n",
              res, errno, strerror(errno));
      exit(1);
    }
    res = use_fdatasync ? fdatasync(append_fd) : fsync(append_fd);
    if (res)
    {
      fprintf(stderr, "Error in fdatasync() of append file (errno=%d: %s)\n",
              errno, strerror(errno));
      exit(1);
    }

    memcpy(buf2 + (seq * RECSIZE) % BLOCKSIZE, buf, RECSIZE);
    res = pwrite(inplace_fd, buf2, BLOCKSIZE, 0);
    if (res != BLOCKSIZE)
    {
      fprintf(stderr, "Unexpected return %d from pwrite() (errno=%d: %s)\n",
              res, errno, strerror(errno));
      exit(1);
    }
    res = fdatasync(inplace_fd);
    if (res)
    {
      fprintf(stderr, "Error in fdatasync() of inplace file (errno=%d: %s)\n",
              errno, strerror(errno));
      exit(1);
    }
  }
}

int
main(int argc, char *argv[])
{
  int append_fd, inplace_fd;
  int res;
  int use_fdatasync = 1;
  unsigned char buf[BLOCKSIZE];

  if (argc > 1 && !strcmp("--fsync", argv[1]))
    use_fdatasync = 0;

  append_fd = open("append.log", O_WRONLY|O_CREAT|O_TRUNC, 0666);
  if (append_fd < 0)
  {
    fprintf(stderr, "Failed to open append file: %d: %s\n",
            errno, strerror(errno));
    exit(1);
  }
  res = fsync(append_fd);
  if (res)
  {
    fprintf(stderr, "Failed initial fsync() of append file: %d: %s\n",
            errno, strerror(errno));
    exit(1);
  }
  inplace_fd = open("inplace.log", O_WRONLY|O_CREAT, 0666);
  if (inplace_fd < 0)
  {
    fprintf(stderr, "Failed to open inplace file: %d: %s\n",
            errno, strerror(errno));
    exit(1);
  }
  memset(buf, 0, BLOCKSIZE);
  res = write(inplace_fd, buf, BLOCKSIZE);
  if (res != BLOCKSIZE)
  {
    fprintf(stderr, "Failed to initialise inplace file, write() returns %d "
            "errno=%d: %s\n", res, errno, strerror(errno));
    exit(1);
  }
  res = fsync(inplace_fd);
  if (res)
  {
    fprintf(stderr, "Failed initial fsync() of inplace file: %d: %s\n",
            errno, strerror(errno));
    exit(1);
  }

  do_test(append_fd, inplace_fd, use_fdatasync);

  return 0;
}