#define _GNU_SOURCE

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

#include "misc.h"

#define NP 50

typedef struct Plist {
  const void *b,*e;
  size_t i;
  const char *s;
} Plist;

static Plist *P1=NULL,*P=NULL,*PE=NULL;
static char *DEBUG_MALLOC;
static size_t nb;

static __inline__ void
pplist(void) {

  Plist *p;

  for (p=P1;p<P;p++)
    fprintf(stderr,"Unfreed memory: %p -- %p, %s\n",p->b,p->e,p->s);

}

static __inline__ int
plistcomp(const void *v1,const void *v2) {

  if (((Plist *)v1)->b<((Plist *)v2)->e &&
      ((Plist *)v1)->e>((Plist *)v2)->b)
    return 0;

  return ((Plist *)v1)->b>((Plist *)v2)->b ? 1 : -1;

}

void * 
pcalloc1(size_t n,size_t m,const char *s) {

  void *v;
  static char N;
  Plist *q;
  
  if (!N) {

    N=1;
    if ((DEBUG_MALLOC=getenv("DEBUG_MALLOC")))
      atexit(pplist);

  }

  if (n*m<1)
    errretv(NULL,"Must allocate at least one byte, n=%d m=%d\n",n,m);

  if ((v=calloc(n,m))==NULL)
    errretv(NULL,"Can't calloc %s\n",s);

  if (DEBUG_MALLOC) {

    if (P==PE) {
      size_t np=P-P1,npe=PE-P1;
      
      if (!(P1=realloc(P1,(npe+NP)*sizeof(Plist))))
	errretv(NULL,"Cannot expand pointer list by %d elements, current %d\n",NP,npe);
      P=P1+np;
      PE=P1+npe+NP;
    }

    P->b=v;
    P->e=(void *)((char *)v + n*m);
    P->i=m;
    P->s=s;
    
    nb+=(char *)P->e-(char *)P->b;

    for (q=P1;q<P;q++)
      if (!plistcomp(q,P))
	errretv(NULL,"Pointer overlap:\n%p -- %p, %s\n%p -- %p, %s\n",
	      P->b,P->e,P->s,q->b,q->e,q->s);

    P++;

    qsort(P1,P-P1,sizeof(Plist),plistcomp);

    err("Calloc %p %s %d %d, tot=%d\n",v,s,n,m,nb);

  }

  return v;


}

void * 
pcalloc2(size_t n,size_t ns,
	  size_t m,size_t ms,const char *s) { 

  void **v; 
  size_t i; 

  if (!(v=pcalloc1(n,ns,s)))
    errretv(NULL,"Can't pcalloc v\n");
  if (!(v[0]=pcalloc1(n*m,ms,"pcalloc2:v[0]")))
    errretv(NULL,"Can't pcalloc v[0]\n");
  for (i=1;i<n;i++) 
    v[i]=(void *)((char *)v[i-1]+m*ms);

  return v; 

} 

int 
pfree1(void *v) {

  Plist *p,tp;
  
  if (DEBUG_MALLOC) {

    tp.b=v;
    tp.e=(void *)((char *)v+1);
    if (!(p=bsearch(&tp,P1,P-P1,sizeof(Plist),plistcomp)))
      errret("Attempt to free pointer that was never pcalloc1ed\n");
    
    nb-=(char *)p->e-(char *)p->b;

    err("Free %p %s, tot=%d\n",v,p->s,nb);
    
    for (p++;p<P;p++) 
      p[-1]=*p;
    
    P--;

  }
  
  free(v);
  
  return 0;

}

int 
pfree2(void **v) {

  if (pfree1(v[0]))
    errret("Can't free v[0]\n");
  if (pfree1(v))
    errret("Can't free v\n");
    
  return 0;

}

static __inline__ void *
prealloc1(void *v,size_t s) {

  Plist *p,p1;
  void *v1;

  if (!(v1=realloc(v,s)))
    errretv(NULL,"Can't reallocate %d bytes to %p\n",s,v);

  if (DEBUG_MALLOC) {

    p1.b=v;
    p1.e=(void *)((char *)v+1);
    if (!(p=bsearch(&p1,P1,P-P1,sizeof(Plist),plistcomp))) {
      
      if (P==PE) {
	size_t np=P-P1,npe=PE-P1;
      
	if (!(P1=realloc(P1,(npe+NP)*sizeof(Plist))))
	  errretv(NULL,"Cannot expand pointer list by %d elements, current %d\n",NP,npe);
	P=P1+np;
	PE=P1+npe+NP;
      }

      P->i=1;
      P->s="none";
      P->e=P->b=NULL;
      p=P++;


    }

    nb-=(char *)p->e-(char *)p->b;

    p->b=v1;
    p->e=(void *)((char *)p->b+s);
    
    nb+=(char *)p->e-(char *)p->b;

    err("Realloc, %p -> %p, %d, %s, tot=%d\n",v,v1,s,p->s,nb);
    
    qsort(P1,P-P1,sizeof(Plist),plistcomp);

  }

  return v1;

}


int 
r_mem1(void **a1,void **a,void **ae,int b,const char *name) {

  int j,k;
  
  if (!a1 || !a || !ae)
    errret("Null pointers %p %p %p\n",a1,a,ae);

  j=*a-*a1;
  k=*ae-*a1;
  if (j<0 || k<0) 
    errret("Bad pointers %s %p %p %p j=%d k1=%d\n",name,*a1,*a,*ae,j,k);
  if (b<0) 
    errret("Resizing mem to b=%d is impossible\n",b);
  if (!(*a1=prealloc1(*a1,b))) 
    errret("Cannot realloc %p to %d\n",*a1,b);
  j=b < j ? b : j;
  *a=*a1+j;
  *ae=*a1+b;
  if (b>k) 
    memset(*a1+k,0,b-k);

  return 0;

}

int
l_str1(char **a1,char **a,char **ae,const char *fmt,...) {

  int j,k,l;
  char *v;

  va_list args;

  va_start(args,fmt);
  if (vasprintf(&v,fmt,args)<0) {
    va_end(args);
    errret("vasprintf fails on format %s\n",fmt);
  } else
    va_end(args);

  j=strlen(v)+1;
  k=*ae-*a;
  l=*a-*a1;

  if (k<j && r_mem(*a,j+l)) 
    errret("r_mem failure\n");

  memcpy(*a,v,j);
  (*a)+=j-1;
  free(v);

  return 0;

}


int
l_mem1(char **a1,char **a,char **ae,const void *i,size_t n) {

  if (*ae-*a<n)
    r_mem(*a,(*a-*a1)+n);

  memcpy(*a,i,n);
  (*a)+=n;

  return 0;

}

