/*
** 2013-10-01
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
******************************************************************************
**
** Compute hash signatures for every page of a database file.  This utility
** program is useful for analyzing the output logs generated by the
** ext/misc/vfslog.c extension.
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>

/*
** Compute signature for a block of content.
**
** For blocks of 16 or fewer bytes, the signature is just a hex dump of
** the entire block.
**
** For blocks of more than 16 bytes, the signature is a hex dump of the
** first 8 bytes followed by a 64-bit hash of the entire block.
*/
static void vlogSignature(unsigned char *p, int n, char *zCksum){
  unsigned int s0 = 0, s1 = 0;
  unsigned int *pI;
  int i;
  if( n<=16 ){
    for(i=0; i<n; i++) sprintf(zCksum+i*2, "%02x", p[i]);
  }else{ 
    pI = (unsigned int*)p;
    for(i=0; i<n-7; i+=8){
      s0 += pI[0] + s1;
      s1 += pI[1] + s0;
      pI += 2;
    }
    for(i=0; i<8; i++) sprintf(zCksum+i*2, "%02x", p[i]);
    sprintf(zCksum+i*2, "-%08x%08x", s0, s1);
  }
}

/*
** Open a file.  Find its page size.  Read each page, and compute and
** display the page signature.
*/
static void computeSigs(const char *zFilename){
  FILE *in = fopen(zFilename, "rb");
  unsigned pgsz;
  size_t got;
  unsigned n;
  unsigned char aBuf[50];
  unsigned char aPage[65536];

  if( in==0 ){
    fprintf(stderr, "cannot open \"%s\"\n", zFilename);
    return;
  }
  got = fread(aBuf, 1, sizeof(aBuf), in);
  if( got!=sizeof(aBuf) ){
    goto endComputeSigs;
  }
  pgsz = aBuf[16]*256 + aBuf[17];
  if( pgsz==1 ) pgsz = 65536;
  if( (pgsz & (pgsz-1))!=0 ){
    fprintf(stderr, "invalid page size: %02x%02x\n", aBuf[16], aBuf[17]);
    goto endComputeSigs;
  }
  rewind(in);
  for(n=1; (got=fread(aPage, 1, pgsz, in))==pgsz; n++){
    vlogSignature(aPage, pgsz, aBuf);
    printf("%4d: %s\n", n, aBuf);
  }

endComputeSigs:
  fclose(in);
}

/*
** Find page signatures for all named files.
*/
int main(int argc, char **argv){
  int i;
  for(i=1; i<argc; i++) computeSigs(argv[i]);
  return 0;
}
