/*
 *   rcksum/lib - library for using the rsync algorithm to determine
 *               which parts of a file you have and which you need.
 *   Copyright (C) 2004,2005 Colin Phipps <cph@moria.org.uk>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the Artistic License v2 (see the accompanying 
 *   file COPYING for the full license terms), or, at your option, any later 
 *   version of the same license.
 *
 *   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
 *   COPYING file for details.
 */

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

#include "config.h"
#include "rcksum.h"
#include "internal.h"

#define calc_min_max(x, min, max) \
  int min = 0, max = zs->numranges-1;         \
  for (min=0,max=zs->numranges-1; min<=max;) { \
    int y = (max+min)/2;                      \
    if (x > zs->ranges[2*y+1]) min = y+1;     \
    else if (x < zs->ranges[2*y]) max = y-1;  \
    else { min = max = y; break; }            \
  }                                           \
  (void)0
  

void add_to_ranges(struct rcksum_state* zs, zs_blockid x)
{
  calc_min_max(x, min, max);

  if (min == max) {
  } else {
    zs->gotblocks++;

    if (zs->numranges && max >=0 && min < zs->numranges && zs->ranges[2*max+1] == x-1 && zs->ranges[2*min] == x+1) {
      // This block fills the gap between two areas that we have got completely. Merge the adjacent ranges
      zs->ranges[2*max+1] = zs->ranges[2*max+3];
      memmove(&zs->ranges[2*min], &zs->ranges[2*min+2], (zs->numranges - min - 1)*sizeof(zs->ranges[0])*2);
      zs->numranges--;
    } else
    if (max >= 0 && zs->numranges && zs->ranges[2*max+1] == x-1) {
      zs->ranges[2*max+1] = x;
    } else 
    if (min < zs->numranges && zs->ranges[2*min] == x+1) {
      zs->ranges[2*min] = x;
    } else {
      // New range
      zs->ranges = realloc(zs->ranges, (zs->numranges+1) * 2 * sizeof(zs->ranges[0]));
      if (min < zs->numranges)
	memmove(&zs->ranges[2*min+2],&zs->ranges[2*min], (zs->numranges - min) * 2 * sizeof(zs->ranges[0]));
      zs->ranges[2*min] = zs->ranges[2*min+1] = x;
      zs->numranges++;
    }
  }
#if 0
  {
    int i;
    for (i=0; i<zs->numranges; i++)
      fprintf(stderr,"%d-%d,",zs->ranges[i*2],zs->ranges[i*2+1]);
    fprintf(stderr," are the current ranges got\n");
  }
#endif
}

int already_got_block(struct rcksum_state* zs, zs_blockid x)
{
  calc_min_max(x, min, max);

  return (min == max);
}

zs_blockid* rcksum_needed_block_ranges(const struct rcksum_state* z, int* num, zs_blockid from, zs_blockid to) {
  int i,n;
  int alloc_n = 100;
  zs_blockid* r = malloc(2 * alloc_n * sizeof(zs_blockid));

  if (!r) return NULL;

  if (to >= z->blocks) to = z->blocks;
  r[0] = from; r[1] = to; n = 1;
  /* Note r[2*n-1] is the last range in our prospective list */

  for (i = 0; i<z->numranges; i++) {
    if (z->ranges[2*i] > r[2*n-1]) continue;
    if (z->ranges[2*i+1] < from) continue;
    
    /* Okay, they intersect */
    if (n == 1 && z->ranges[2*i] <= from) { /* Overlaps the start of our window */
      r[0] = z->ranges[2*i+1]+1;
    } else {
      /* If the last block that we still (which is the last window end -1, due
       * to half-openness) then this range just cuts the end of our window */
      if (z->ranges[2*i+1] >= r[2*n-1]-1) {
	r[2*n-1] = z->ranges[2*i];
      } else {
	/* In the middle of our range, split it */
	r[2*n] = z->ranges[2*i+1]+1;
	r[2*n+1] = r[2*n-1];
	r[2*n-1] = z->ranges[2*i];
	n++;
	if (n == alloc_n) {
	  zs_blockid* r2;
	  alloc_n += 100;
	  r2 = realloc(r, 2 * alloc_n * sizeof *r);
	  if (!r2) { free(r); return NULL; } 
	  r = r2;
	}
      }
    }
  }
  r = realloc(r, 2 * n * sizeof *r);
  if (n == 1 && r[0] >= r[1]) n = 0;

  *num = n;
  return r;
}

int rcksum_blocks_todo(const struct rcksum_state* rs)
{
  int i, n = rs->blocks;
  for (i = 0; i<rs->numranges; i++) {
    n -= 1 + rs->ranges[2*i+1] - rs->ranges[2*i];
  }
  return n;
}



syntax highlighted by Code2HTML, v. 0.9.1