#ifndef REPORT__COMPARE_FILES_C
#define REPORT__COMPARE_FILES_C
/*
indexing
cluster: "demangler"
namespace: "candidate"
description: "Delete a path in a `SYNCHRONISED_DIRECTORY'."
original_author: "Justin Finnerty"
original_date: "July 2005"
current_date: "$Date: 2005/08/26 23:31:39 $"
current_author: "$Author: finnerty $"
revision: "$Revision: 2.0 $"	
$Log: compare_files.c,v $
Revision 2.0  2005/08/26 23:31:39  finnerty
== alpha version ==

Revision 1.4  2005/08/26 22:50:27  finnerty
autoconfiscated

Revision 1.3  2005/08/24 12:08:59  finnerty
Substantial changes to make a much more robust system. The
code now include design by contract assertions.

Revision 1.2  2005/08/21 11:55:13  finnerty
Alpha 1 version

Revision 1.1  2005/08/17 06:46:11  finnerty
C function to compare contents of two files.


*/
#include "file_io.h"
#include "../config.h"

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#else
#error Need <stdlib.h>
#endif

#include <assert.h>
#include <errno.h>

#define ensure( X ) assert((X))
#define require( X ) assert((X))

/* Comparison buffers */
#define BUFFER_SIZE 8192
#define FAKE_BUFFER_SIZE 32768
#define FAKE_STEP_SIZE 1024
#define STEP_SIZE 256 /* vectorable loop  size */
#define MAX_STEP 32 /* == 32768/1024 == BUFFER_SIZE/STEP_SIZE */

#define DEBUGF( A ) printf ("DEBUG: %s\n", (A))

static unsigned int red_buffer[BUFFER_SIZE], black_buffer[BUFFER_SIZE];

static struct file_io*
open_file_private (const char* a_filename, char* a_error_str, int a_err_str_size, 
		   struct file_io* (*open_fn)(const char*,const char*))
{
  struct file_io *red;
  red = open_fn (a_filename, "r");
  if (a_err_str_size > 0)
    {
      if (NULL == red)
	{
	  /* Memory error */ 
	  memset (a_error_str, 0, a_err_str_size);
	  if (0 != errno)
	    {
	      strncpy (a_error_str, strerror (errno), a_err_str_size);
	    }
	  else
	    {
	      strncpy (a_error_str, "Unknown error", a_err_str_size);
	    }
	}
      else if (io_in_error (red))
	{
	  memset (a_error_str, 0, a_err_str_size);
	  strncpy (a_error_str, io_error_message (red), a_err_str_size);
	}
    }
  if (NULL != red && io_in_error (red))
    {
      io_reset (red);
      io_delete (&red, a_error_str, a_err_str_size);
    }
  return red;
}

inline static struct file_io*
open_file (const char* a_filename, char* a_error_str, int a_err_str_size)
{
  return open_file_private(a_filename, a_error_str, a_err_str_size, io_new_file);
}

inline static struct file_io*
open_zfile (const char* a_filename, char* a_error_str, int a_err_str_size)
{
  return open_file_private(a_filename, a_error_str, a_err_str_size, io_new_zfile);
}

inline static struct file_io*
open_bzfile (const char* a_filename, char* a_error_str, int a_err_str_size)
{
  return open_file_private(a_filename, a_error_str, a_err_str_size, io_new_bzfile);
}

static void
close_file (struct file_io **red, char* a_error_str, int a_err_str_size)
{
  require(red);
  require (*red);
  if (io_in_error (*red))
    {
      if (a_err_str_size > 0)
	{
	  memset (a_error_str, 0, a_err_str_size);
	  strncpy (a_error_str, io_error_message (*red), a_err_str_size);
	}
      io_reset (*red);
    }
  io_delete (red, a_error_str, a_err_str_size);
}

int
compare_files_private (const char* filename1,
		       const char* filename2,
		       int size_of_file,
		       char* a_error_str, 
		       int a_err_str_size,
		       struct file_io* (*open_fn)(const char*,char*,int))
{
  int result;
/*   printf ("%s [%s] [%s]\n", "Comparing", filename1, filename2); */
  struct file_io *red = NULL, *black = NULL;
  red = open_fn (filename1, a_error_str, a_err_str_size);
  if (NULL == red || io_in_error(red))
    {
      result = -1;
    }
  else
    {
      black = open_file (filename2, a_error_str, a_err_str_size);
      if (NULL == black || io_in_error(black))
	{
	  result = -1;
	}
      else
	{
	  result = do_compare_files (red, black, size_of_file);
	}
    }
  if (red != NULL) close_file (&red, a_error_str, a_err_str_size);
  if (black != NULL) close_file (&black, a_error_str, a_err_str_size);
  return result;
}

inline int
compare_files (const char* filename1,
	       const char* filename2,
	       int size_of_file,
	       char* a_error_str, 
	       int a_err_str_size)
{
  return compare_files_private(filename1, filename2, size_of_file, a_error_str, a_err_str_size, open_file);
}

#ifdef HAVE_LIBZ
inline int
compare_zfiles (const char* filename1,
		const char* filename2,
		int size_of_file,
		char* a_error_str, 
		int a_err_str_size)
{
  return compare_files_private(filename1, filename2, size_of_file, a_error_str, a_err_str_size, open_zfile);
}
#else /* !HAVE_LIBZ */
inline int
compare_zfiles (const char* filename1,
		const char* filename2,
		int size_of_file,
		char* a_error_str, 
		int a_err_str_size)
{
  return -1;
}
#endif /* !HAVE_LIBZ */

#ifdef HAVE_LIBBZ2
inline int
compare_bzfiles (const char* filename1,
		 const char* filename2,
		 int size_of_file,
		 char* a_error_str, 
		 int a_err_str_size)
{
  return compare_files_private(filename1, filename2, size_of_file, a_error_str, a_err_str_size, open_bzfile);
}
#else /* !HAVE_LIBBZ2 */
inline int
compare_bzfiles (const char* filename1,
		 const char* filename2,
		 int size_of_file,
		 char* a_error_str, 
		 int a_err_str_size)
{
  return -1;
}
#endif /* !HAVE_LIBBZ2 */

/*
compare_files reads two files of identical length in 32k pieces. It
returns 1 if files are identical, 0 if files are different (and -1 if
files actually have different sizes or files could not be opened).

*/
int 
do_compare_files (struct file_io *red_file, struct file_io *black_file, int size_of_file)
{
  require(red_file);
  require(black_file);
  require(!io_no_stream(red_file));
  require(!io_no_stream(black_file));
  require(!io_in_error(red_file));
  require(!io_in_error(black_file));
  /* Nomenclature: Function reads a file in 32k "bits" and compares
     within a "bit" in 1k "steps". The two files are "red" and
     "black". */

  /* final result */
  int result = 1;
  /* amount of file yet to be read */
  int remaining_file_size = size_of_file;
  /* number of steps in each 32k bit = (MAX_STEP if
     remaining_file_size > 32k otherwise remaining_file_size/1024) */
  unsigned int number_of_steps;
  /* index of current step */
  unsigned int step_index;
  /* offset into buffer of beginning of current step (step_index *
     FAKE_STEP_SIZE) */
  unsigned int step_start_offset;
  /* offset into buffer of end of current step ((step_index + 1) *
     FAKE_STEP_SIZE) */
  unsigned int step_end_offset;
  /* actual offset into buffer (step_start_offset <= index <
     step_end_offset) */
  unsigned int index;
  /* amount to read for current "bit" */
  int to_read;
  
  /* Loop over each 32k piece */
  for (; remaining_file_size > 0 && result == 1; remaining_file_size -= FAKE_BUFFER_SIZE)
    {
      /* Determine how much to read */
      if (remaining_file_size < FAKE_BUFFER_SIZE) 
	{
	  number_of_steps = (remaining_file_size / FAKE_STEP_SIZE)+ 1; /* int math */
	  to_read = remaining_file_size;

	  /* As we compare all elements in a step before checking for
	     a mismatch (to allow vectoring the loop) we set to zero
	     the elements of the last step before reading so the extra
	     comparisons will be comparing zeros. */

	  /* memset treats red_buffer/black_buffer as single-byte arrays */
	  memset (&(red_buffer[(number_of_steps - 1) * STEP_SIZE]), 0, FAKE_STEP_SIZE);
	  memset (&(black_buffer[(number_of_steps - 1) * STEP_SIZE]), 0,  FAKE_STEP_SIZE);
	}
      else
	{
	  number_of_steps = MAX_STEP;
	  to_read = FAKE_BUFFER_SIZE;
	}
      /* Read into buffers. Treat red_buffer/black_buffer as
	 single-byte arrays */
      io_read(red_file, &red_buffer[0], sizeof(unsigned char), to_read);
      io_read(black_file, &black_buffer[0], sizeof(unsigned char), to_read);
      if (io_in_error(red_file) || io_in_error(black_file))
	{
	  /* ERROR! */
	  result = -1;
	  if (io_at_eof (red_file))
	    {
	      io_reset (red_file);
	      result = -2;
	    }
	  if (io_at_eof (black_file))
	    {
	      io_reset (black_file);
	      result = -2;
	    }
	};

      /* Do the steps for this "bit" */
      for (step_index = 0; result == 1 && step_index < number_of_steps; ++step_index)
	{
	  step_start_offset = step_index * STEP_SIZE;
	  step_end_offset = (step_index + 1) * STEP_SIZE;
	  /* Do a fixed length step (to allow vectoring the loop) */
	  for (index = step_start_offset; index < step_end_offset; ++index)
	    {
	      if (red_buffer[index] != black_buffer[index])
		{
		  result = 0;
		}
	    }
	}
    }
  /* Return result */
  return result;
}

#endif /* !REPORT__COMPARE_FILES_C */
