/*  -*- Mode: C;  -*- */

/******************************************************************************
*                                                                             *
*   Copyright 2005 University of Cambridge Computer Laboratory.               *
*                                                                             *
*   This file is part of Nprobe.                                              *
*                                                                             *
*   Nprobe 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.                                       *
*                                                                             *
*   Nprobe 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 Nprobe; if not, write to the Free Software                     *
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *
*                                                                             *
******************************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>

typedef struct {
    u_int hi, lo, tot;
} fingerprint_t;



/*********** This code is for the Little Endian Intel x86 *************/

/* ACC(x,k)  :   x = x + k and fold in any carry */

#define ACC(x,k) { \
__asm(" \
add %2, %0 ;\
adc $0, %0 ;\
" : "=r" (x) : "0" (x), "r" (k) ); }


inline void fingerprint_init( fingerprint_t *f )
{
  f->hi = f->lo = f->tot = 0;
}

inline void fingerprint_add( fingerprint_t *f, u_char *cp, int len )
{
  u_int y = f->hi;
  u_int x = f->lo;
  u_int *p = (u_int*) cp;    // good job this an x86!
  u_int k;
  int i, start_off, l, ll;

  //printf("Add Segment len %d to position %d\n",len,f->tot);

  start_off = f->tot & 3;

  if( start_off )
    {
      //printf("start off = %d\n",start_off);
      k = *p;

      if( len < 4 )
	k &= ~( (-1) << ( len*8 ));  

      k <<= (start_off *8 );

      ACC(x,k)
	  
      p = (u_int*) (  ((u_char*)p) + 4 - start_off );
      f->tot += 4 - start_off;
      len -= 4 - start_off;

      if (len<0)
	{
	  f->lo = x;
	  f->tot += len;
	  return;
	}
    }

  ll = l = len & ~3;

  // decrementing loop to help out dumb compiler (GCC 2.95)
#if 0
  for( i = l; i>0; i-=4 )  
    {      

      ACC(y,x)
      k = *p++;
      ACC(x,k)
    }

#else

  /*
    if( l<=0 ) goto end
top:
    y = y + x
    y = y + 0 with carry
    x = x + [p]
    x = x + 0 with carry
    p = p + 4
    l = l - 4 
    if( l>0 ) goto top
end:
   */

   __asm("
     cmp $0, %0
     jle  2f
1:
     add %5, %2      
     adc $0, %2
     add (%7), %1
     adc $0, %1
     add $4, %7
     sub $4, %0
     jg  1b
2:
" : "=r" (ll), "=r" (x), "=r" (y), "=r" (p) : 
     "0" (ll),  "1" (x),  "2" (y),  "3" (p) );

#endif
  //printf("end len = %d, i = %d  len &3 = %d\n",len, i, len&3);

//printf("i=%d  l=%d  len=%d  %d\n",i,l,len,len&3);

  if( l<len )
    {
      /* This code makes the assumption that its safe to access up to
	 3 bytes past the end of the array. I expect it to only be
	 used on data stored in skbufs (which have loads of padding),
	 hence the assumption is safe. Beware! */

      ACC(y,x)

      k = *p++;
      k &= ~( (-1) << ( (len&3)*8) );

      ACC(x,k)
    }

  f->hi = y;
  f->lo = x;
  f->tot += len;
}

inline void fingerprint_final( fingerprint_t *f )
{
  ACC(f->hi,f->lo)
}


#ifdef TESTING

/****************************************************************

Routine below this point are used only for test purposes

*****************************************************************/

#define LOOP_KERNEL(x,y,k) { \
__asm(" \
add %4, %0 ;\
adc $0, %0 ;\
add %0, %1 ;\
adc $0, %1 ;\
" : "=r" (x), "=r" (y) : "0" (x), "1" (y), "r" (k) ); }

void finger_gold( u_char *cp, int len, fingerprint_t *f)
{
  u_int y = f->hi;
  u_int x = f->lo;
  u_int *p = (u_int*) cp;    // good job this an x86!
  u_int k;
  int i;

  for( i=0; i< len ; i+=4)
    {      
      k = *p++;

      LOOP_KERNEL(x,y,k)
    }

  f->hi = y;
  f->lo = x;
  f->tot += len;
}


void regression_test()
{
int i,num,k,c;
fingerprint_t f,g;

#define SIZE 16

  while(1)
    {
      u_char data[SIZE];
      u_char msg[SIZE][SIZE];
      u_char xx[SIZE];
      int len[SIZE];

      for(i=0;i<SIZE;i++)
	{
	  data[i] = rand() & 0xff ;
	}

      fingerprint_init( &g );
      fingerprint_add( &g, data, SIZE );
      fingerprint_final( &g );


      for(c=0;c<100;c++)
	{
	  i=num=k=0;
	  do
	    {
	      int l = 0 + rand() % (SIZE);
	      
	      if( i+l > SIZE ) l = SIZE - i;

	      len[num] = l;

	      memset(msg[num], 0x55, SIZE);
	      memcpy(msg[num], &data[i], l);

	      i+=l;
	      num++;
	    }
	  while(i<SIZE);
#if 1
	  fingerprint_init( &f );

	  for(i=0;i<num;i++)
	    fingerprint_add( &f, msg[i], len[i] );

	  fingerprint_final( &f );

	  printf("%d Fingerprint \t %08x:%08x  =%08x:%08x\n",c,g.hi,g.lo,f.hi,f.lo);

	  if( ! (g.hi==f.hi && g.lo==f.lo) )
	  {
	      exit(-1);
	  }
#endif

#if 0
	  for(i=0;i<SIZE;i++)
	    printf("%02x",data[i]);
	  printf("OOOOOO\n");

	  for(i=0;i<num;i++)
            for(k=0;k<len[i];k++)
	      printf("%02x",msg[i][k]);
	  printf("XXXXX\n");

	  for(i=k=0;i<num;k+=len[i],i++)
	    memcpy(&xx[k], msg[i], len[i]);

	  printf("%d memcmp %d\n",c,memcmp(data,xx,SIZE));
#endif

	}
    }

#undef SIZE
}

long tvdiff2us(struct timeval *t0, struct timeval *t1)
{
  struct timeval tdiff;
  long us;

  tdiff.tv_sec = t1->tv_sec - t0->tv_sec;
  tdiff.tv_usec = t1->tv_usec - t0->tv_usec;
  if (tdiff.tv_usec < 0)
    tdiff.tv_sec--, tdiff.tv_usec += 1000000;

  return ((long)tdiff.tv_sec*1000000) + tdiff.tv_usec;
}

int memsys_measure( u_char *cp, int len )
{
  int i,a=0;
  u_int *p = (u_int*) cp;    // good job this an x86!
  
  for(i=0;i<len;i+=4)
    {
      a+=*p++;
    }
  return a;
}

void performance_test()
{
  int i,num,k,c;
  fingerprint_t g;
  struct timeval start, stop;
  long us;
  u_char *data;

#define SIZE 100*1024*1024
#define OFF 0

  data = malloc(SIZE+16);
  if(!data) exit(-1);

#if 1
  for(i=0;i<SIZE;i++)
    {
      //data[i] = rand() & 0xff ;
      data[i] = i & 0xff ;
      //data[i] = 0 ;
    }
#endif

  while(1)
    {
#if 0
      for(i=0;i<SIZE;i++)
	{
	  data[i] = rand() & 0xff ;
	  //data[i] = i & 0xff ;
	  //data[i] = 0 ;
	}
#endif
      gettimeofday(&start, NULL);

      fingerprint_init( &g );
#if 0
      fingerprint_add( &g, data+OFF, SIZE );
#else
      memsys_measure( data+OFF, SIZE );
#endif
      fingerprint_final( &g );

      gettimeofday(&stop, NULL);
      
      us = tvdiff2us( &start, &stop );

      printf("Fingerprint of %d bytes took %dus = %.2fMB/s\n",
	     SIZE, us, ((float)SIZE)/us );
    }


}


main()
{
  fingerprint_t f;
  int i;
  u_char test[8192];
#if 1
  performance_test();
#endif

#if 0
  regression_test();
#endif


#if 0
  for(i=0;i<8192;i++)
    test[i] = i&0xff;

  fingerprint_init( &f );
  finger_gold( test, 8000, &f );

  printf("Fingerprint %08x:%08x\n",f.hi,f.lo);

  fingerprint_init( &f );
  fingerprint_add( &f, test, 8000 );
  fingerprint_final( &f );
  printf("Fingerprint %08x:%08x\n",f.hi,f.lo);

  fingerprint_init( &f );
  fingerprint_add( &f, test, 4000 );
  fingerprint_add( &f, test+4000, 4000 );
  fingerprint_final( &f );
  printf("Fingerprint %08x:%08x\n",f.hi,f.lo);

  fingerprint_init( &f );
  fingerprint_add( &f, test, 1001 );
  fingerprint_add( &f, test+1001, 6999 );
  fingerprint_final( &f );
  printf("Fingerprint %08x:%08x\n",f.hi,f.lo);

  fingerprint_init( &f );
  fingerprint_add( &f, test, 1002 );
  fingerprint_add( &f, test+1002, 6998 );
  fingerprint_final( &f );
  printf("Fingerprint %08x:%08x\n",f.hi,f.lo);

  fingerprint_init( &f );
  fingerprint_add( &f, test, 1002 );
  fingerprint_add( &f, test+1002, 3 );
  fingerprint_add( &f, test+1005, 6995 );
  fingerprint_final( &f );
  printf("Fingerprint %08x:%08x\n",f.hi,f.lo);
#endif

}

/*
len = 8000, i = 8000  len &3 = 0
adc Fingerprint 0bfd687b:b5de064d

add Fingerprint 0bee2020:b5de0260

 */

#endif /* TESTING */

/*
 * end fingerprint.c
 */
