/*
** Splint - annotation-assisted static program checker
** Copyright (C) 1994-2003 University of Virginia,
**         Massachusetts Institute of Technology
**
** 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.
** 
** The GNU General Public License is available from http://www.gnu.org/ or
** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
** MA 02111-1307, USA.
**
** For information on splint: info@splint.org
** To report a bug: splint-bug@splint.org
** For more information: http://www.splint.org
*/
/*
** sRefSet.c
**
** based on set_template.c
**
** where T has T_equal (or change this) and T_unparse
*/

# include "splintMacros.nf"
# include "basic.h"

/*@constant int sRefSetBASESIZE;@*/
# define sRefSetBASESIZE SMALLBASESIZE

sRefSet
sRefSet_new (void)
{
  return sRefSet_undefined;
}

static /*@notnull@*/ /*@only@*/ sRefSet
sRefSet_newEmpty (void)
{
  sRefSet s = (sRefSet) dmalloc (sizeof (*s));
  
  s->entries = 0;
  s->nspace = sRefSetBASESIZE;
  s->elements = (sRef *) dmalloc (sizeof (*s->elements) * sRefSetBASESIZE);

  return (s);
}

/*@only@*/ sRefSet
sRefSet_single (/*@exposed@*/ sRef sr)
{
  sRefSet s = (sRefSet) dmalloc (sizeof (*s));
  
  s->entries = 1;
  s->nspace = sRefSetBASESIZE - 1;
  s->elements = (sRef *) dmalloc (sizeof (*s->elements) * sRefSetBASESIZE);
  s->elements[0] = sr;

  return (s);
}

static void
sRefSet_grow (/*@notnull@*/ sRefSet s)
{
  int i;
  sRef *newelements; 

  s->nspace = sRefSetBASESIZE;
  newelements = (sRef *) dmalloc (sizeof (*newelements) * (s->entries + s->nspace));

  for (i = 0; i < s->entries; i++)
    {
      newelements[i] = s->elements[i];
    }

  sfree (s->elements);
  s->elements = newelements;
}

sRefSet
sRefSet_insert (sRefSet s, /*@exposed@*/ sRef el)
{
  if (sRefSet_isUndefined (s))
    {
      s = sRefSet_newEmpty ();
    }

  
  if (!sRefSet_isSameMember (s, el))
    {
      
      if (s->nspace <= 0)
	sRefSet_grow (s);

      s->nspace--;

      llassert (s->elements != NULL);
      s->elements[s->entries] = el;
      s->entries++;
    }
  else
    {
      ;
    }
  
  return s;
}

void
sRefSet_clear (sRefSet s)
{
  if (sRefSet_isDefined (s))
    {
      s->nspace += s->entries;
      s->entries = 0;
    }
}

/*
** slow algorithm...but it doesn't matter
*/

void
sRefSet_clearStatics (sRefSet s)
{
  if (sRefSet_isDefined (s))
    {
      int i;
      
      for (i = 0; i < s->entries; i++)
	{
	  sRef current = s->elements[i];
	  
	  if (sRef_isFileStatic (sRef_getRootBase (current)))
	    {
	      int j;
	      
	      for (j = i; j < s->entries - 1; j++)
		{
		  s->elements[j] = s->elements[j+1];
		}
	      
	      s->entries--;
	      s->nspace++;
	      i--;
	    }
	}
    }
}

bool
sRefSet_delete (sRefSet s, sRef el)
{
  int i;

  if (sRefSet_isUndefined (s)) return FALSE;

  if (s->elements != NULL)
    {
      for (i = 0; i < s->entries; i++)
	{
	  sRef current = s->elements[i];
	  
	  if (sRef_realSame (el, current))
	    {
	      int j;

	      for (j = i; j < s->entries - 1; j++)
		{
		  s->elements[j] = s->elements[j+1];
		}
	      
	      s->entries--;
	      s->nspace++;
	      return TRUE;
	    }
	}
    }

  return FALSE;
}

/*@exposed@*/ sRef
sRefSet_choose (sRefSet s)
{
  llassert (sRefSet_isDefined (s));
  llassert (s->entries > 0);
  llassert (s->elements != NULL);

  return (s->elements[0]);
}

/*@exposed@*/ sRef
sRefSet_mergeIntoOne (sRefSet s)
{
  sRef res;
  int i;

  if (sRefSet_isUndefined (s)) return sRef_undefined;
  if (s->entries == 0) return sRef_undefined;

  llassert (s->elements != NULL);

  res = s->elements[0];

  for (i = 1; i < s->entries; i++)
    {
      sRef tmp;

      tmp = sRef_makeConj (res, s->elements[i]);
      res = tmp;
    }
  
  return res;
}

/*
** this is really yucky...but it works...
*/

bool
sRefSet_deleteBase (sRefSet s, sRef base)
{
  int i = 0;
  int offset = 0;

  if (sRefSet_isUndefined (s) || (s->elements == NULL))
    {
      return FALSE;
    } ;

  while (i + offset < s->entries)
    {
      sRef current = s->elements[i + offset];

      while (sRef_includedBy (current, base))
	{
	  	  offset++;
	  if (i + offset >= s->entries) goto doneLoop;
	  current = s->elements [i + offset];
	}
      
      if (offset > 0)
	{
	  	  s->elements [i] = current;
	}
      
      i++;
    }

 doneLoop:
  s->entries -= offset;
  s->nspace += offset;

  return (offset > 0);
}

/*
** modifies *s1
*/

sRefSet
sRefSet_unionFree (/*@returned@*/ sRefSet s1, sRefSet s2)
{
  sRefSet res = sRefSet_union (s1, s2);

  sRefSet_free (s2);
  return res;
}

sRefSet
sRefSet_union (/*@returned@*/ sRefSet s1, sRefSet s2)
{
  if (s1 == s2) 
    {
      return s1;
    }

  if (sRefSet_isEmpty (s1))
    {
      s1 = sRefSet_copyInto (s1, s2);
    }
  else
    {
      sRefSet_allElements (s2, el)
	{
	  s1 = sRefSet_insert (s1, el);
	} end_sRefSet_allElements;
    }

  return s1;
}

/*
** s1 <- s1 U (s2 - ex - params)
*/

sRefSet
sRefSet_unionExcept (/*@returned@*/ sRefSet s1, sRefSet s2, sRef ex)
{
  if (s1 == s2) return s1;

  sRefSet_allElements (s2, el)
    {
      if (sRef_same (el, ex))
	{
	  ;
	}
      else
	{
	  s1 = sRefSet_insert (s1, el);
	}
    } end_sRefSet_allElements;
  
  return s1;
}

/*@only@*/ sRefSet
sRefSet_realNewUnion (sRefSet s1, sRefSet s2)
{
  llassert (NOALIAS (s1, s2));

  if (sRefSet_isUndefined (s1))
    {
      return (sRefSet_newCopy (s2));
    }
  else
    {
      sRefSet ret = sRefSet_newCopy (s1);
      
      sRefSet_allElements (s2, el)
	{
	  ret = sRefSet_insert (ret, el);
	} end_sRefSet_allElements;
      
      return ret;
    }
}

/* slow! */

/*@only@*/ sRefSet
sRefSet_intersect (sRefSet s1, sRefSet s2)
{
  sRefSet s = sRefSet_new ();

  llassert (NOALIAS (s1, s2));

  sRefSet_allElements (s1, el)
    {
      if (sRefSet_member (s2, el))
	{
	  s = sRefSet_insert (s, el);
	}
    } end_sRefSet_allElements;
  
    return s;
}

sRefSet
sRefSet_levelUnion (/*@returned@*/ sRefSet sr, sRefSet s, int lexlevel)
{
  llassert (NOALIAS (sr, s));

  sRefSet_allElements (s, el)
    {
      if (sRef_lexLevel (el) <= lexlevel)
	{
	  sr = sRefSet_insert (sr, el);
	}
    } end_sRefSet_allElements;

  return sr;
}

void
sRefSet_levelPrune (sRefSet s, int lexlevel)
{
  if (sRefSet_isDefined (s))
    {
      int i;
      int backcount = sRefSet_size (s) - 1;
      
      for (i = 0; i <= backcount; i++)
	{
	  sRef el = s->elements[i]; 
	  
	  if (sRef_lexLevel (el) > lexlevel)
	    {
	      int j;
	      
	      
	      for (j = backcount; j > i; j--)
		{
		  backcount--;
		  s->entries--;
		  s->nspace++;
		  
		  if (sRef_lexLevel (s->elements[j]) <= lexlevel)
		    {
		      s->elements[i] = s->elements[j];
		      
		      if (backcount == i) s->entries++;
		      /*@innerbreak@*/ break;
		    }
		}

	      if (backcount == i)
		{
		  s->entries--;
		}
	    }
	}
    }
}

/*
** s1 <- s2
*/

sRefSet sRefSet_copyInto (/*@returned@*/ sRefSet s1, /*@exposed@*/ sRefSet s2)
{
  llassert (NOALIAS (s1, s2));
  
  if (sRefSet_isUndefined (s1))
    {
      if (sRefSet_isEmpty (s2))
	{
	  return s1;
	}
      else
	{
	  s1 = sRefSet_newEmpty ();
	}
    } 

  s1->nspace = s1->entries + s1->nspace;
  s1->entries = 0;

  sRefSet_allElements (s2, el)
    {
      if (s1->nspace == 0)
	{
	  sRefSet_grow (s1);
	}

      s1->elements[s1->entries] = el; 
      s1->nspace--;
      s1->entries++;
    } end_sRefSet_allElements;

  return s1;
}

/*@only@*/ sRefSet
  sRefSet_newCopy (/*@exposed@*/ sRefSet s)
{
  if (sRefSet_isEmpty (s))
    {
      return sRefSet_undefined;
    }
  else
    {
      sRefSet r = (sRefSet) dmalloc (sizeof (*r));
      int i;
      
      r->entries = s->entries;
      r->nspace = s->nspace;
      r->elements = (sRef *) dmalloc (sizeof (*r->elements) * (s->entries + s->nspace));
      
      for (i = 0; i < s->entries; i++)
	{
	  r->elements[i] = s->elements[i]; 
	}  
      
      return r;
    }
}

/*@only@*/ sRefSet
sRefSet_levelCopy (/*@exposed@*/ sRefSet s, int lexlevel)
{
  if (sRefSet_isEmpty (s))
    {
      return sRefSet_undefined;
    }
  else
    {
      sRefSet r = (sRefSet) dmalloc (sizeof (*r));
      int i;
      
      r->nspace = s->entries;
      r->entries = 0;
      r->elements = (sRef *) dmalloc (sizeof (*r->elements) * (s->entries)); 

      for (i = 0; i < s->entries; i++)
	{
	  if (sRef_lexLevel (s->elements[i]) <= lexlevel)
	    {
	      r->elements[r->entries] = s->elements[i];
	      r->entries++;
	      r->nspace--;
	    }
	}  
      
      return r;
    }
}

/*@only@*/ sRefSet
sRefSet_newDeepCopy (sRefSet s)
{
  if (sRefSet_isUndefined (s))
    {
      return sRefSet_newEmpty ();
    }
  else
    {
      sRefSet r = (sRefSet) dmalloc (sizeof (*r));
      int i;
      
      r->entries = s->entries;
      r->nspace = s->nspace;
      r->elements = (sRef *) dmalloc (sizeof (*r->elements) * (s->entries + s->nspace));
      
      for (i = 0; i < s->entries; i++)
	{
	  r->elements[i] = sRef_copy (s->elements[i]);
	}  
      
      return r;
    }
}

static bool
sRefSet_isElementCompare (bool (*test)(sRef, sRef), sRefSet s, sRef el)
{
  sRefSet_allElements (s, e)
    {
      if ((test)(el, e))
	{
	  return TRUE;
	}
    } end_sRefSet_allElements;

  return FALSE;
}

static bool
sRefSet_isElementTest (bool (*test)(sRef), sRefSet s)
{
  sRefSet_allElements (s, e)
    {
      if ((test)(e))
	{
	  return TRUE;
	}
    } end_sRefSet_allElements;

  return FALSE;
}

bool
sRefSet_hasRealElement (sRefSet s)
{
  sRefSet_allElements (s, e)
    {
      if (sRef_isMeaningful (e) && !sRef_isUnconstrained (e))
	{
	  return TRUE;
	}
    } end_sRefSet_allElements;

  return FALSE;
}

#ifdef DEADCODE
bool
sRefSet_containsSameObject (sRefSet s, sRef el)
{
  return (sRefSet_isElementCompare (sRef_sameObject, s, el));
}
#endif

bool
sRefSet_isSameMember (sRefSet s, sRef el)
{
  return (sRefSet_isElementCompare (sRef_realSame, s, el));
}

bool
sRefSet_isSameNameMember (sRefSet s, sRef el)
{
  return (sRefSet_isElementCompare (sRef_sameName, s, el));
}

bool
sRefSet_member (sRefSet s, sRef el)
{
  return (sRefSet_isElementCompare (sRef_similar, s, el));
}

bool
sRefSet_hasStatic (sRefSet s)
{
  return (sRefSet_isElementTest (sRef_isFileStatic, s));
}

bool
sRefSet_hasUnconstrained (sRefSet s)
{
  return (sRefSet_isElementTest (sRef_isUnconstrained, s));
}

cstring
  sRefSet_unparseUnconstrained (sRefSet s)
{
  int num = 0;
  cstring res = cstring_undefined;
  
  sRefSet_allElements (s, el)
    {
      if (sRef_isUnconstrained (el))
	{
	  if (cstring_isUndefined (res))
	    {
	      res = cstring_copy (sRef_unconstrainedName (el));
	    }
	  else
	    {
	      res = message ("%q, %s", res, sRef_unconstrainedName (el));
	    }
	  
	  num++;
	}
    } end_sRefSet_allElements ;
  
  if (num == 0)
    {
      llassert (cstring_isUndefined (res));
      return (cstring_makeLiteral ("<ERROR: no unconstrained calls>"));
    }
  else if (num == 1)
    {
      return (message ("unconstrained function %q", res));
    }
  else
    {
      return (message ("unconstrained functions %q", res));
    }
}

cstring
sRefSet_unparseUnconstrainedPlain (sRefSet s)
{
  cstring res = cstring_undefined;

  sRefSet_allElements (s, el)
    {
      if (sRef_isUnconstrained (el))
	{
	  if (cstring_isUndefined (res))
	    {
	      res = cstring_copy (sRef_unconstrainedName (el));
	    }
	  else
	    {
	      res = message ("%q, %s", res, sRef_unconstrainedName (el));
	    }
	}
    } end_sRefSet_allElements ;

  return res;
}
      
bool
sRefSet_modifyMember (sRefSet s, sRef m)
{
  bool ret = FALSE;

  sRefSet_allElements (s, e)
    {
      if (sRef_similar (m, e))
	{
	  sRef_setModified (e);
	  ret = TRUE;
	}
    } end_sRefSet_allElements;

  
  return ret;
}

/*@exposed@*/ sRef
sRefSet_lookupMember (sRefSet s, sRef el)
{
  sRefSet_allElements (s, e)
    {
      if (sRef_similar (el, e))
	{
	  return e;
	}
    } end_sRefSet_allElements;
  
  return sRef_undefined;
}

int sRefSet_size (sRefSet s)
{
  if (sRefSet_isUndefined (s)) return 0;
  return s->entries;
}

/*@only@*/ cstring
sRefSet_unparse (sRefSet s)
{
  int i;
  cstring st = cstring_makeLiteral ("{");

  if (sRefSet_isDefined (s))
    {
      for (i = 0; i < sRefSet_size (s); i++)
	{
	  if (i == 0)
	    st = message ("%q %q", st, sRef_unparse (s->elements[i]));
	  else
	    st = message ("%q, %q", st, sRef_unparse (s->elements[i]));
	}
    }
  
  st = message ("%q }", st);
  return st;
}

cstring sRefSet_unparsePlain (sRefSet s)
{
  int i;
  cstring st = cstring_undefined;

  if (sRefSet_isDefined (s))
    {
      for (i = 0; i < sRefSet_size (s); i++)
	{
	  if (i == 0)
	    st = sRef_unparse (s->elements[i]);
	  else
	    st = message ("%q, %q", st, sRef_unparse (s->elements[i]));
	}
    }
  
  return st;
}

cstring
sRefSet_unparseDebug (sRefSet s)
{
  int i;
  cstring st = cstring_makeLiteral ("{");

  if (sRefSet_isDefined (s))
    {
      for (i = 0; i < sRefSet_size (s); i++)
	{
	  if (i == 0)
	    {
	      st = message ("%q %q", st, sRef_unparseDebug (s->elements[i]));
	    }
	  else
	    {
	      st = message ("%q, %q", st, sRef_unparseDebug (s->elements[i]));
	    }
	}
    }
  
  st = message ("%q }", st);
  return st;
}

cstring
sRefSet_unparseFull (sRefSet s)
{
  int i;
  cstring st = cstring_makeLiteral ("{");

  if (sRefSet_isDefined (s))
    {
      for (i = 0; i < sRefSet_size (s); i++)
	{
	  if (i == 0)
	    {
	      st = message ("%q %q", st, sRef_unparseFull (s->elements[i]));
	    }
	  else
	    {
	      st = message ("%q, %q", st, sRef_unparseFull (s->elements[i]));
	    }
	}
    }
  
  st = message ("%q }", st);
  return st;
}

void
sRefSet_fixSrefs (sRefSet s)
{
  if (sRefSet_isDefined (s))
    {
      int i;
      
      for (i = 0; i < sRefSet_size (s); i++)
	{
	  sRef current = s->elements[i];

	  if (sRef_isLocalVar (current))
	    {
	      s->elements[i] = uentry_getSref (sRef_getUentry (current));
	    }
	}
    }
}

void
sRefSet_free (/*@only@*/ sRefSet s)
{
  if (!sRefSet_isUndefined (s))
    {
      /* evans 2003-10-20: increase size sanity limit from 1000 */
      llassertprint (s->entries < 99999, ("sRefSet free size: %d", s->entries));

      sfree (s->elements); 
      sfree (s);
    }
}

sRefSet sRefSet_removeIndirection (sRefSet s)
{
  /*
  ** returns a NEW sRefSet containing references to all sRef's in s
  */

  sRefSet t = sRefSet_new ();
  
  
  sRefSet_allElements (s, el)
    {
      if (!sRef_isAddress (el))
	{
	  t = sRefSet_insert (t, sRef_makeAddress (el));
	}
    } end_sRefSet_allElements;

  return t;
}

sRefSet sRefSet_addIndirection (sRefSet s)
{
  /*
  ** returns a NEW sRefSet containing references to all sRef's in s
  */

  sRefSet t = sRefSet_new ();
  
  
  sRefSet_allElements (s, el)
    {
      ctype ct = ctype_realType (sRef_getType (el));
      
      
      if ((ctype_isArrayPtr (ct)))
	{
	  
	  sRef a = sRef_constructPointer (el);
	  t = sRefSet_insert (t, a);   
	}
    } end_sRefSet_allElements;

    return t;
}

sRefSet sRefSet_accessField (sRefSet s, /*@observer@*/ cstring f)
{
  /*
  ** returns a NEW sRefSet containing references to all sRef's in s
  */
  
  sRefSet t = sRefSet_new ();
  
  sRefSet_allElements (s, el)
    {
      ctype ct = ctype_realType (sRef_getType (el));
      
      if ((ctype_isStruct (ct) || ctype_isUnion (ct))
	  && (!uentry_isUndefined (uentryList_lookupField (ctype_getFields (ct), f))))
	{
	  t = sRefSet_insert (t, sRef_makeNCField (el, f));
	}
    } end_sRefSet_allElements;
  
    return t;
}

sRefSet sRefSet_fetchUnknown (sRefSet s)
{
  sRefSet t = sRefSet_new ();
  
  sRefSet_allElements (s, el)
    {
      ctype ct = ctype_realType (sRef_getType (el));
      
      if (ctype_isArrayPtr (ct))
	{
	  t = sRefSet_insert (t, sRef_makeArrayFetch (el));
	}
    } end_sRefSet_allElements;

  return t;
}

sRefSet sRefSet_fetchKnown (sRefSet s, int i)
{
  sRefSet t = sRefSet_new ();
  
  sRefSet_allElements (s, el)
    {
      ctype ct = ctype_realType (sRef_getType (el));
      
      if (ctype_isArrayPtr (ct))
	{
	  t = sRefSet_insert (t, sRef_makeArrayFetchKnown (el, i));
	}
    } end_sRefSet_allElements;

  return t;
}

# ifdef DEADCODE
int sRefSet_compare (sRefSet s1, sRefSet s2)
{
  sRefSet_allElements (s1, el)
    {
      if (!sRefSet_isSameMember (s2, el))
	{
	  return -1;
	}
    } end_sRefSet_allElements;

  sRefSet_allElements (s2, el)
    {
      if (!sRefSet_isSameMember (s1, el))
	{
	  return 1;
	}
    } end_sRefSet_allElements;

  return 0;
}
# endif /* DEADCODE */

bool sRefSet_equal (sRefSet s1, sRefSet s2)
{
  sRefSet_allElements (s1, el)
    {
      if (!sRefSet_isSameMember (s2, el))
	{
	  return FALSE;
	}
    } end_sRefSet_allElements;

  sRefSet_allElements (s2, el)
    {
      if (!sRefSet_isSameMember (s1, el))
	{
	  return FALSE;
	}
    } end_sRefSet_allElements;

  return TRUE;
}

/*@only@*/ sRefSet
sRefSet_undump (char **s)
{
  char c;
  sRefSet sl = sRefSet_new ();

  while ((c = **s) != '#' && c != '@' && c != '$' && c != '&')
    {
      sl = sRefSet_insert (sl, sRef_undump (s));

      
      if (**s == ',')
	{
	  (*s)++;
	}
    }

    return sl;
}

/*@only@*/ cstring
sRefSet_dump (sRefSet sl)
{
  cstring st = cstring_undefined;
  bool first = TRUE;

  
  sRefSet_allElements (sl, el)
    {
      if (!first)
	{
	  st = cstring_appendChar (st, ',');
	}
      else
	{
	  first = FALSE;
	}

      st = cstring_concatFree (st, sRef_dump (el));
    } end_sRefSet_allElements;

  return st;
}

void
sRefSet_markImmutable (sRefSet s)
{
  sRefSet_allElements (s, el)
    {
      sRef_markImmutable (el);
    } end_sRefSet_allElements;
}

