/*
**	Copyright (C) 2005-2011 Erik de Castro Lopo
**
**	This program is free software; you can redistribute it and/or modify
**	it under the terms of 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.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program; if not, write to the Free Software
**	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
**	A simple checksum for short, int and float data.
*/

#include "sfconfig.h"

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

#include <sndfile.h>

#include "regtest.h"

#define	BIG_PRIME		999983

#define	ARRAY_LEN(x)	((int) (sizeof (x)) / (sizeof ((x) [0])))

static int short_checksum (SNDFILE * file, int start) ;
static int int_checksum (SNDFILE * file, int start) ;
static int float_checksum (SNDFILE * file, int start) ;

int
calc_checksum (SNDFILE * file, const SF_INFO * info)
{	int start ;

	/* Seed the checksum with data from the SF_INFO struct. */
	start = info->samplerate ;
	start = start * BIG_PRIME + info->channels ;
	start = start * BIG_PRIME + info->format ;

	switch (info->format & SF_FORMAT_SUBMASK)
	{	case SF_FORMAT_FLOAT :
		case SF_FORMAT_DOUBLE :
			return float_checksum (file, start) ;

		case SF_FORMAT_PCM_24 :
		case SF_FORMAT_PCM_32 :
			return int_checksum (file, start) ;

		default :
			return short_checksum (file, start) ;
		} ;

	return 0 ;
} /* calc_checksum */

/*------------------------------------------------------------------------------
*/

static union
{	short	s [1 << 16] ;
	int		i [1 << 15] ;
	float	f [1 << 15] ;
} data ;

static int
short_checksum (SNDFILE * file, int start)
{	int k, count ;

	do
	{	count = (int) sf_read_short (file, data.s, ARRAY_LEN (data.s)) ;
		for (k = 0 ; k < count ; k++)
			start = start * BIG_PRIME + data.s [k] ;
		}
	while (count > 0) ;

	return start ;
} /* short_checksum */

static int
int_checksum (SNDFILE * file, int start)
{	int k, count ;

	do
	{	count = (int) sf_read_int (file, data.i, ARRAY_LEN (data.i)) ;
		for (k = 0 ; k < count ; k++)
			start = start * BIG_PRIME + data.i [k] ;
		}
	while (count > 0) ;

	return start ;
} /* int_checksum */

static int
float_checksum (SNDFILE * file, int start)
{	int k, count ;

	do
	{	count = (int) sf_read_float (file, data.f, ARRAY_LEN (data.f)) ;
		for (k = 0 ; k < count ; k++)
			start = start * BIG_PRIME + lrintf (0x7FFFFFFF * data.f [k]) ;
		}
	while (count > 0) ;

	return start ;
} /* float_checksum */

