/* (PD) 2001 The Bitzi Corporation
 * Please see file COPYING or http://bitzi.com/publicdomain 
 * for more info.
 *
 * tigertree.c - Implementation of the TigerTree algorithm
 *
 * NOTE: The TigerTree hash value cannot be calculated using a
 * constant amount of memory; rather, the memory required grows
 * with the size of input. (Roughly, one more interim value must
 * be remembered for each doubling of the input size.) The
 * default TT_CONTEXT struct size reserves enough memory for
 * input up to 2^64 in length
 *
 * Requires the tiger() function as defined in the reference
 * implementation provided by the creators of the Tiger
 * algorithm. See
 *
 *    http://www.cs.technion.ac.il/~biham/Reports/Tiger/
 *
 * $Id: tigertree.c,v 1.3 2001/04/12 21:01:16 robert Exp $
 *
 */

#include <string.h>
#include "tigertree.h"

/* Initialize the tigertree context */
void tt_init(TT_CONTEXT *ctx)
{
  ctx->count = 0;
  ctx->index = 0;
  ctx->top = ctx->nodes;
}

static void tt_compose(TT_CONTEXT *ctx) {
  byte *node = ctx->top - NODESIZE;
  tiger((word64*)node,(word64)NODESIZE,(word64*)ctx->top); // combine two nodes
  memmove(node,ctx->top,TIGERSIZE);           // move up result
  ctx->top -= TIGERSIZE;                      // update top ptr
}

static void tt_block(TT_CONTEXT *ctx, byte *block)
{
  word64 b;

  tiger((word64*)block,(word64)ctx->index,(word64*)ctx->top);
  ctx->top += TIGERSIZE;
  ++ctx->count;
  b = ctx->count;
  while(b == ((b >> 1)<<1)) { // while evenly divisible by 2...
    tt_compose(ctx);
    b = b >> 1;
  }
}

void tt_update(TT_CONTEXT *ctx, byte *buffer, word32 len)
{
  if (ctx->index)
    { /* Try to fill partial block */
      unsigned left = BLOCKSIZE - ctx->index;
      if (len < left)
	{
	  memmove(ctx->block + ctx->index, buffer, len);
	  ctx->index += len;
	  return; /* Finished */
	}
      else
	{
	  memmove(ctx->block + ctx->index, buffer, left);
	  ctx->index = BLOCKSIZE;
	  tt_block(ctx, ctx->block);
	  buffer += left;
	  len -= left;
	}
    }
  while (len >= BLOCKSIZE)
    {
      ctx->index = BLOCKSIZE;
      tt_block(ctx, buffer);
      buffer += BLOCKSIZE;
      len -= BLOCKSIZE;
    }
  if ((ctx->index = len))     /* This assignment is intended */
    /* Buffer leftovers */
    memmove(ctx->block, buffer, len);
}

// no need to call this directly; tt_digest calls it for you
static void tt_final(TT_CONTEXT *ctx)
{
  // do last partial block, unless index is zero
  // AND we're past the first block
  if((ctx->index>0)||(ctx->top==ctx->nodes))
    tt_block(ctx,ctx->block);
}

void tt_digest(TT_CONTEXT *ctx, byte *s)
{
  tt_final(ctx);
  while( (ctx->top-TIGERSIZE) > ctx->nodes ) {
    tt_compose(ctx);
  }
  memmove(s,ctx->nodes,TIGERSIZE);
}

// this code untested; use at own risk
void tt_copy(TT_CONTEXT *dest, TT_CONTEXT *src)
{
  int i;
  dest->count = src->count;
  for(i=0; i < BLOCKSIZE; i++)
    dest->block[i] = src->block[i];
  dest->index = src->index;
  for(i=0; i < STACKSIZE; i++)
    dest->nodes[i] = src->nodes[i];
  dest->top = src->top;
}
