/*--------------------------------------------------------------------*/
/*--- Callgrind                                                    ---*/
/*---                                                     events.c ---*/
/*--------------------------------------------------------------------*/

/*
   This file is part of Callgrind, a Valgrind tool for call tracing.

   Copyright (C) 2002-2009, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)

   This program 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.

   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 GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA.

   The GNU General Public License is contained in the file COPYING.
*/

#include "global.h"

#define MAX_EVENTTYPE 20

static EventType eventtype[MAX_EVENTTYPE];
static Int eventtype_count = 0;

EventType* CLG_(register_eventtype)(Char* name)
{
  EventType* et;

  if (eventtype_count == MAX_EVENTTYPE) {
    VG_(printf)("\nMore than %d event types used!\n"
		"Increase MAX_EVENTTYPE in ct_events.c and recomile this tool!\n",
		MAX_EVENTTYPE);
    VG_(tool_panic)("Too many event types requested.");
  }

  et = &(eventtype[eventtype_count]);
  et->id = eventtype_count; 
  et->name = (UChar*) VG_(strdup)("cl.events.re.1", name);
  et->description = 0;

  eventtype_count++;

  return et;
}


EventType* CLG_(get_eventtype)(Char* name)
{
  Int i;

  for(i=0;i<eventtype_count;i++)
    if (VG_(strcmp)(eventtype[i].name, name) == 0)
      return eventtype+i;
  return 0;
}

EventType* CLG_(get_eventtype_byindex)(Int id)
{
  if ((id >= 0) && (id < eventtype_count))
    return eventtype+id;
  return 0;
}

/* Allocate space for an event set */
EventSet* CLG_(get_eventset)(Char* n, Int capacity)
{
  EventSet* es;

  es = (EventSet*) CLG_MALLOC("cl.events.geSet.1",
                               sizeof(EventSet) +
			       capacity * sizeof(EventSetEntry));
  es->capacity = capacity;
  es->size = 0;
  es->name = n;

  return es;
}

/* Incorporate a event type into a set, get start offset */
Int CLG_(add_eventtype)(EventSet* es, EventType* t)
{
  Int offset = es->size;
  if (es->capacity - offset < 1) return -1;

  es->size++;
  es->e[offset].type = t;
  es->e[offset].nextTop = es->size;

  return offset;
}

/* Incorporate one event set into another, get start offset */
Int CLG_(add_eventset)(EventSet* dst, EventSet* src)
{
  Int offset = dst->size, i;
  if (!src || (src->size == 0)) return offset;

  if (dst->capacity - offset < src->size) return -1;
  
  for(i=0;i<src->size;i++) {
    dst->e[offset+i].type = src->e[i].type;
    dst->e[offset+i].nextTop = src->e[i].nextTop + offset;
  }
  dst->size += src->size;

  return offset;
}

/* Incorporate two event types into a set, with second < first */
Int CLG_(add_dep_event2)(EventSet* es, EventType* e1, EventType* e2)
{
  Int offset = es->size;

  if (es->capacity - offset < 2) return -1;

  es->size += 2;
  es->e[offset].type = e1;
  es->e[offset].nextTop = es->size;
  es->e[offset+1].type = e2;
  es->e[offset+1].nextTop = es->size;
  
  return offset;
}

/* Incorporate 3 event types into a set, with third < second < first */
Int CLG_(add_dep_event3)(EventSet* es,
			EventType* e1, EventType* e2, EventType* e3)
{
  Int offset = es->size;

  if (es->capacity - offset < 3) return -1;

  es->size += 3;
  es->e[offset].type = e1;
  es->e[offset].nextTop = es->size;
  es->e[offset+1].type = e2;
  es->e[offset+1].nextTop = es->size;
  es->e[offset+2].type = e3;
  es->e[offset+2].nextTop = es->size;
  
  return offset;
}

Int CLG_(add_dep_event4)(EventSet* es,
			EventType* e1, EventType* e2,
			EventType* e3, EventType* e4)
{
  Int offset = es->size;

  if (es->capacity - offset < 4) return -1;

  es->size += 4;
  es->e[offset].type = e1;
  es->e[offset].nextTop = es->size;
  es->e[offset+1].type = e2;
  es->e[offset+1].nextTop = es->size;
  es->e[offset+2].type = e3;
  es->e[offset+2].nextTop = es->size;
  es->e[offset+3].type = e4;
  es->e[offset+3].nextTop = es->size;
  
  return offset;
}

/* Returns number of characters written */
Int CLG_(sprint_eventset)(Char* buf, EventSet* es)
{
  Int i, pos = 0;

  for(i=0; i< es->size; i++) {
    if (pos>0) buf[pos++] = ' ';
    pos += VG_(sprintf)(buf + pos, "%s", es->e[i].type->name);
  }
  buf[pos] = 0;

  return pos;
}

/* Get cost array for an event set */
ULong* CLG_(get_eventset_cost)(EventSet* es)
{
  return CLG_(get_costarray)(es->capacity);
}

/* Set all costs of an event set to zero */
void CLG_(init_cost)(EventSet* es, ULong* cost)
{
  Int i;

  if (!cost) return;

  for(i=0;i<es->capacity;i++)
    cost[i] = 0;
}

/* Set all costs of an event set to zero */
void CLG_(init_cost_lz)(EventSet* es, ULong** cost)
{
  Int i;

  CLG_ASSERT(cost != 0);
  if (!(*cost))
    *cost = CLG_(get_eventset_cost)(es);

  for(i=0;i<es->capacity;i++)
    (*cost)[i] = 0;
}

void CLG_(zero_cost)(EventSet* es, ULong* cost)
{
  Int i;

  if (!cost) return;

  for(i=0;i<es->size;i++)
    cost[i] = 0;
}
  
Bool CLG_(is_zero_cost)(EventSet* es, ULong* cost)
{
  Int i = 0;

  if (!cost) return True;

  while(i<es->size) {
    if (cost[i] != 0) return False;
    i = es->e[i].nextTop;
  }
  return True;
}

Bool CLG_(is_equal_cost)(EventSet* es, ULong* c1, ULong* c2)
{
  Int i = 0;

  if (!c1) return CLG_(is_zero_cost)(es,c2);
  if (!c2) return CLG_(is_zero_cost)(es,c1);

  while(i<es->size) {
    if (c1[i] != c2[i]) return False;
    if (c1[i] == 0)
      i = es->e[i].nextTop;
    else
      i++;
  }
  return True;
}

void CLG_(copy_cost)(EventSet* es, ULong* dst, ULong* src)
{
  Int i;

  if (!src) {
    CLG_(zero_cost)(es, dst);
    return;
  }
  CLG_ASSERT(dst != 0);
  
  for(i=0;i<es->size;i++)
    dst[i] = src[i];
}

void CLG_(copy_cost_lz)(EventSet* es, ULong** pdst, ULong* src)
{
  Int i;
  ULong* dst;

  CLG_ASSERT(pdst != 0);

  if (!src) {
    CLG_(zero_cost)(es, *pdst);
    return;
  }
  dst = *pdst;
  if (!dst)
    dst = *pdst = CLG_(get_eventset_cost)(es);
  
  for(i=0;i<es->size;i++)
    dst[i] = src[i];
}

void CLG_(add_cost)(EventSet* es, ULong* dst, ULong* src)
{
  Int i = 0;

  if (!src) return;
  CLG_ASSERT(dst != 0);

  while(i<es->size) {
    if (src[i] == 0)
      i = es->e[i].nextTop;
    else {
      dst[i] += src[i];
      i++;
    }
  }
}

void CLG_(add_cost_lz)(EventSet* es, ULong** pdst, ULong* src)
{
  Int i;
  ULong* dst;

  if (!src) return;
  CLG_ASSERT(pdst != 0);

  dst = *pdst;
  if (!dst) {
    dst = *pdst = CLG_(get_eventset_cost)(es);
    CLG_(copy_cost)(es,dst,src);
    return;
  }

  i = 0;
  while(i<es->size) {
    if (src[i] == 0)
      i = es->e[i].nextTop;
    else {
      dst[i] += src[i];
      i++;
    }
  }
}

/* Adds src to dst and zeros src. Returns false if nothing changed */
Bool CLG_(add_and_zero_cost)(EventSet* es, ULong* dst, ULong* src)
{
  Int i = 0, j = 0;

  CLG_DEBUGIF(6) {
    CLG_DEBUG(6, "   add_and_zero_cost(%s, dst %p, src %p)\n", es->name, dst, src);
    CLG_(print_cost)(-5, es, src);
  }

  if (!es || !src) return False;

  while(i<es->size) {
    if (src[i] == 0)
      i = es->e[i].nextTop;
    else {
      dst[i] += src[i];
      src[i] = 0;
      i++;
      j++;
    }
  }

  return (j>0);
}

/* Adds src to dst and zeros src. Returns false if nothing changed */
Bool CLG_(add_and_zero_cost_lz)(EventSet* es, ULong** pdst, ULong* src)
{
  Int i;
  ULong* dst;

  if (!src) return False;

  i = 0;
  while(1) {
    if (i >= es->size) return False;
    if (src[i] != 0) break;
    i = es->e[i].nextTop;
  }

  CLG_ASSERT(pdst != 0);
  dst = *pdst;
  if (!dst) {
    dst = *pdst = CLG_(get_eventset_cost)(es);
    CLG_(copy_cost)(es,dst,src);
    CLG_(zero_cost)(es,src);
    return True;
  }

  dst[i] += src[i];
  src[i] = 0;
  i++;

  while(i<es->size) {
    if (src[i] == 0)
      i = es->e[i].nextTop;
    else {
      dst[i] += src[i];
      src[i] = 0;
    }
  }

  return True;
}

/* Adds difference of new and old to dst, and set old to new.
 * Returns false if nothing changed */
Bool CLG_(add_diff_cost)(EventSet* es, ULong* dst, ULong* old, ULong* new_cost)
{
  Int i = 0, j = 0;

  while(i<es->size) {
    if (new_cost[i] == old[i])
      i = es->e[i].nextTop;
    else {
      dst[i] += new_cost[i] - old[i];
      old[i] = new_cost[i];
      i++;
      j++;
    }
  }

  return (j>0);
}

/* Adds difference of new and old to dst, and set old to new.
 * Returns false if nothing changed */
Bool CLG_(add_diff_cost_lz)(EventSet* es, ULong** pdst, 
			    ULong* old, ULong* new_cost)
{
  Int i;
  ULong* dst;

  if (!old && !new_cost) return False;
  CLG_ASSERT(old && new_cost);

  i = 0;
  while(1) {
    if (i >= es->size) return False;
    if (old[i] != new_cost[i]) break;
    i = es->e[i].nextTop;
  }

  CLG_ASSERT(pdst != 0);
  dst = *pdst;
  if (!dst) {
    dst = *pdst = CLG_(get_eventset_cost)(es);
    CLG_(zero_cost)(es,dst);
  }

  dst[i] += new_cost[i] - old[i];
  old[i] = new_cost[i];
  i++;

  while(i<es->size) {
    if (new_cost[i] == old[i])
      i = es->e[i].nextTop;
    else {
      dst[i] += new_cost[i] - old[i];
      old[i] = new_cost[i];
      i++;
    }
  }

  return True;
}

/* Returns number of characters written */
Int CLG_(sprint_cost)(Char* buf, EventSet* es, ULong* c)
{
  Int i, pos, skipped = 0;

  if (!c || es->size==0) return 0;

  /* At least one entry */
  pos = VG_(sprintf)(buf, "%llu", c[0]);
  i = 1;

  while(i<es->size) {
    if (c[i] == 0) {
      skipped  += es->e[i].nextTop - i;
      i = es->e[i].nextTop;
    }
    else {
      while(skipped>0) {
	buf[pos++] = ' ';
	buf[pos++] = '0';
	skipped--;
      }
      buf[pos++] = ' ';
      pos += VG_(sprintf)(buf+pos, "%llu", c[i]);
      i++;
    }
  }

  return pos;
}


/* Allocate space for an event mapping */
EventMapping* CLG_(get_eventmapping)(EventSet* es)
{
  EventMapping* em;

  CLG_ASSERT(es != 0);

  em = (EventMapping*) CLG_MALLOC("cl.events.geMapping.1",
                                   sizeof(EventMapping) +
				   es->capacity * sizeof(Int));
  em->capacity = es->capacity;
  em->size = 0;
  em->set = es;

  return em;
}

void CLG_(append_event)(EventMapping* em, Char* n)
{
  Int i;

  CLG_ASSERT(em != 0);

  for(i=0; i<em->set->size; i++)
    if (VG_(strcmp)(n, em->set->e[i].type->name)==0)
      break;
  
  if (i == em->set->size) return;

  CLG_ASSERT(em->capacity > em->size);

  em->index[em->size] = i;
  em->size++;
}


/* Returns number of characters written */
Int CLG_(sprint_eventmapping)(Char* buf, EventMapping* em)
{
  Int i, pos = 0;

  CLG_ASSERT(em != 0);

  for(i=0; i< em->size; i++) {
    if (pos>0) buf[pos++] = ' ';
    pos += VG_(sprintf)(buf + pos, "%s", em->set->e[em->index[i]].type->name);
  }
  buf[pos] = 0;

  return pos;
}

/* Returns number of characters written */
Int CLG_(sprint_mappingcost)(Char* buf, EventMapping* em, ULong* c)
{
  Int i, pos, skipped = 0;

  if (!c || em->size==0) return 0;

    /* At least one entry */
  pos = VG_(sprintf)(buf, "%llu", c[em->index[0]]);
  i = 1;

  while(i<em->size) {
    if (c[em->index[i]] == 0) {
      skipped++;
      i++;
    }
    else {
      while(skipped>0) {
	buf[pos++] = ' ';
	buf[pos++] = '0';
	skipped--;
      }
      buf[pos++] = ' ';
      pos += VG_(sprintf)(buf+pos, "%llu", c[em->index[i]]);
      i++;
    }
  }

  return pos;
}
